学习总纲:
Java 集合框架提供了一组接口和类,以实现各种数据结构和算法。
Java集合框架提供了可以直接使用的各种数据结构和算法。这有两个主要优点:
-
我们不需要手动编写代码来实现这些数据结构和算法。
-
随着集合框架的高度优化,我们的代码将更加高效。
此外,集合框架允许我们对特定类型的数据使用特定的数据结构。例如,
-
如果我们希望我们的数据是唯一的,那么我们可以使用集合框架提供的Set接口。
-
要以键/值对的形式存储数据,可以使用Map接口。
-
ArrayList类提供可调整大小的数组的功能
〇-1、大树根之一:Collection
Collection 接口包括各种可用于对对象执行不同操作的方法。这些方法在其所有子接口中均可用。
- add() - 将指定的元素插入到集合中
- addAll() - 将指定集合的所有元素添加到集合中
- size() - 返回集合的大小
- remove() - 从集合中删除指定的元素
- removeAll() - 从集合中删除指定集合的所有元素
- iterator() - 返回一个迭代器以访问集合的元素
- clear() - 删除集合中的所有元素
〇-2、大树根之二:Map
〇-3、大树根之三:Iterator
一、List路线
List接口是一个有序的集合,它允许我们像数组一样添加和删除元素
由于List是接口,因此无法从中创建对象。
为了使用List接口的功能,我们可以使用以下类:
- 数组列表(ArrayList类)
- 链表(LinkedList类)
- 向量(vector类)
- 堆栈(Stack类)
首先,使用这些集合框架的声明一般为:
//List 的ArrayList 实现
List<String> list1 = new ArrayList<>();
// List 的LinkedList 实现
List<String> list2 = new LinkedList<>();
此时它是一个List对象了,有些ArrayList有但是List没有的属性和方法,它就不能再用了。而ArrayList list=new ArrayList();创建一对象则保留了ArrayList的所有属性
为什么一般都使用 List list = new ArrayList() ,而不用 ArrayList alist = new ArrayList()呢?
问题就在于List有多个实现类,如 LinkedList或者Vector等等,现在你用的是ArrayList,也许哪一天你需要换成其它的实现类呢?,这时你只要改变这一行就行了:List list = new LinkedList();
其它使用了list地方的代码根本不需要改动。假设你开始用 ArrayList alist = new ArrayList(), 这下你有的改了,特别是如果你使用了 ArrayList特有的方法和属性。
如果没有特别需求的话,最好使用List list = new LinkedList(); ,便于程序代码的重构. 这就是面向接口编程的好处
Java ArrayList
ArrayList类是List接口的实现,允许我们创建可调整大小的数组
它的常用方法:
-
将元素添加到ArrayList
study.list.add(28);
study.list.add(3,21);//这种方法add,它的下标不能跳跃。animals.addAll(mammals);
-
初始化ArrayList:与数组不同,我们不能直接初始化数组列表
import java.util.Arrays;//但是我们可以使用atList()来达到相同的目的
ArrayList<String> animals = new ArrayList<>(Arrays.asList("Cat", "Cow", "Dog"));
-
访问ArrayList的元素
- 使用get()方法
//从数组列表中获取元素 String str = animals.get(0);
- 使用 iterator() 方法
import java.util.Iterator;
//创建一个Iterator对象 Iterator<String> iterate = animals.iterator(); //使用Iterator的方法访问元素 while(iterate.hasNext()){ System.out.print(iterate.next()); System.out.print(", "); }
- 使用get()方法
-
更改ArrayList元素
//更改数组列表的元素 animals.set(2, "Zebra");
-
删除ArrayList元素
//从索引2中删除元素 String str = animals.remove(2);
// 删除所有元素 animals.removeAll(animals);
//删除所有元素 animals.clear();
-
遍历 ArrayList
- 使用 for 循环
for(int i = 0; i < animals.size(); i++) { System.out.print(animals.get(i)); System.out.print(", "); }
- 使用forEach循环
//使用forEach循环 System.out.println("访问所有元素: "); for(String animal : animals) { System.out.print(animal); System.out.print(", "); }
- 使用迭代器
- 使用 for 循环
-
获取ArrayList的长度
//获取arrayList的大小 System.out.println("arrayList的大小: " + animals.size());
-
对ArrayList的元素进行排序
import java.util.Collections;
//对数组列表进行排序 Collections.sort(animals);
-
转换为 数组
//将ArrayList转换成数组 animals.toArray(arr);//arr为应该转化成的数组
-
Array 转换为 ArrayList
import java.util.Arrays;
String[] arr = {"Dog", "Cat", "Horse"};
ArrayList<String> animals = new ArrayList<>(Arrays.asList(arr));
-
ArrayList 转换为 字符串
String str = animals.toString();
LinkedList:见Queue
Stack:不推荐使用
Java堆栈Stack
类已经过时,Java官方推荐使用Deque
替代Stack
使用
Vector(向量)
Vector类是List接口的一个实现,它允许我们创建类似于ArrayList类的可调整大小的数组
由于向量(vector)不是线程安全的并且效率较低,因此建议使用ArrayList代替Vector
二、Queue路线
当我们要以先进先出(有输入输出次序)的方式存储和访问元素时,可以使用Queue接口
不知道为啥,队列这种集合好像很少被用到,可能是这种输入输出端受限的数据结构也可以用List实现。
1、PriorityQueue实现类
使用方法和队列一样,但是它对入队的元素有排序(大小根堆),是用数组存放的,但是逻辑上是有顺序的。
2、Deque接口
在常规队列中,元素是从后面添加的,而从前面删除的。但是,在双端队列中,我们可以从前后插入和删除元素
双端队列作为堆栈数据结构
-
push() - 在双端队列的开头添加元素
-
pop() - 从双端队列的开头删除元素
- peek() - 从双端队列的开头返回一个元素
1、LinkedList(链表)
LinkedList的API boolean add(E object) void add(int location, E object) boolean addAll(Collection<? extends E> collection) boolean addAll(int location, Collection<? extends E> collection) void addFirst(E object) void addLast(E object) void clear() Object clone() boolean contains(Object object) Iterator<E> descendingIterator() E element() E get(int location) E getFirst() E getLast() int indexOf(Object object) int lastIndexOf(Object object) ListIterator<E> listIterator(int location) boolean offer(E o) boolean offerFirst(E e) boolean offerLast(E e) E peek() E peekFirst() E peekLast() E poll() E pollFirst() E pollLast() E pop() void push(E e) E remove() E remove(int location) boolean remove(Object object) E removeFirst() boolean removeFirstOccurrence(Object o) E removeLast() boolean removeLastOccurrence(Object o) E set(int location, E object) int size() <T> T[] toArray(T[] contents) Object[] toArray()
2、ArrayDeque
在Java中,我们可以使用ArrayDeque该类使用数组来实现队列和双端队列数据结构
三、Set路线
Set接口允许我们将元素存储在不同的集合中,类似于数学中的集合。它不能有重复的元素
Set集合运算
Java Set接口允许我们执行基本的数学集合运算,例如并集,交集和子集。
-
Union - 为了得到两个集合x和y的并集,我们可以使用x.addAll(y)
-
Intersection - 要获得两个集合x和y的交集,我们可以使用x.retainAll(y)
-
Subset - 要检查x是否是y的子集,我们可以使用y.containsAll(x)
HashSet 类
Java Collections框架的HashSet类提供了哈希表数据结构的功能。
//具有8个容量和0.75负载因子的HashSet HashSet<Integer> numbers = new HashSet<>(8, 0.75);
也可以不传递参数初始化hashset,默认的容量为16,负载因子为0.75
访问HashSet元素
要访问哈希集合的元素,我们可以使用iterator()方法。为了使用此方法,我们必须导入java.util.Iterator包
// 调用iterator()方法 Iterator<Integer> iterate = numbers.iterator(); System.out.print("使用Iterator的HashSet: "); //访问元素 while(iterate.hasNext()) { System.out.print(iterate.next()); System.out.print(", "); }
集合的并交差
执行两个集合之间的并集,我们可以使用addAll()方法
numbers.addAll(evenNumbers); //就是在一个集合中加入另一个集合,由于集合元素的唯一性,实现了并的功能
要执行两个集合之间的交集,我们可以使用retainAll()方法
//集合的交集,即保存与新set共有的元素 evenNumbers.retainAll(primeNumbers);
要计算两组之间的差集,我们可以使用removeAll()方法
//HashSet1和HashSet2之间的差集 primeNumbers.removeAll(oddNumbers);
Set集合的子集
要检查一个集合是否是另一个集合的子集,我们可以使用containsAll()方法
//检查primeNumbers是否是numbers的子集 boolean result = numbers.containsAll(primeNumbers);
为什么选择HashSet?
在Java中,如果我们必须随机访问元素,则通常使用HashSet。 这是因为哈希表中的元素是使用哈希码访问的。
元素的hashcode是唯一标识,它有助于标识散列表中的元素。
HashSet不能包含重复的元素。因此,每个散列集元素都有一个惟一的hashcode。
四、Map路线
Map接口允许元素以键/值对的形式存储。键是唯一的名称,可用于访问map中的特定元素。而且,每个键都有一个与之关联的值
Map接口维护3个不同的集合:
-
键集
-
值集
-
键/值关联(Map集合)的集合。
因此,我们可以分别访问键,值和关联。
//使用HashMap类创建Map Map<Key, Value> numbers = new HashMap<>();
map方法
Map接口包括Collection接口的所有方法。这是因为Collection是Map的超级接口。
除了Collection接口中可用的方法之外,Map接口还包括以下方法:
-
put(K,V) - 将键K和值V的关联插入到map中。如果键已经存在,则新值将替换旧值。
-
putAll() - 将指定Map集合中的所有条目插入此Map集合中。
-
putIfAbsent(K,V) - 如果键K尚未与value关联,则插入关联V。
-
get(K) - 返回与指定键K关联的值。如果找不到该键,则返回null。
-
getOrDefault(K,defaultValue) - 返回与指定键K关联的值。如果找不到键,则返回defaultValue。
-
containsKey(K) - 检查指定的键K是否在map中。
-
containsValue(V) - 检查指定的值V是否存在于map中。
-
replace(K,V) - 将键K的值替换为新的指定值V。
-
replace(K,oldValue,newValue) - 仅当键K与值oldValue相关联时,才用新值newValue替换键K的值。
-
remove(K) - 从键K表示的Map中删除条目。
-
remove(K,V) - 从Map集合中删除键K与值V相关联的条目。。
-
keySet() -返回Map集合中存在的所有键的集合。
-
values() -返回一组包含在Map集合中的所有值。
-
entrySet() -返回map中存在的所有键/值映射的集合。
1、HashMap
//具有默认容量和负载因子的HashMap HashMap<Key, Value> numbers1 = new HashMap<>();
访问HashMap元素
1.使用entrySet(),keySet()和values()
-
-
entrySet() -返回一组所有键/值映射的map
-
keySet() -返回map所有键的集合
-
values() -返回map所有值的集合
-
2.使用get()和getOrDefault()
-
-
get() - 返回与指定键关联的值。如果找不到键,则返回null。
-
getOrDefault() - 返回与指定键关联的值。如果找不到键,则返回指定的默认值。
-
替换元素
-
-
replace(key, value) - 将key的值替换为value
-
replace(key, old, new) - 仅当old值已与指定键key关联时,才用new值替换old值
-
replaceAll(function) - 用指定函数的结果替换映射的每个值
-
重新计算值
1.使用compute()方法
-
-
compute() - 使用指定的函数计算新值。然后将计算值与指定的键相关联。
-
computeIfAbsent() - 如果指定的键没有映射到任何值,该方法将使用指定的函数计算一个新值。然后将新值与键关联。
-
computeIfPresent() -如果指定的键已经映射到任何值,此方法将使用指定的函数计算一个新值。然后将新值与键关联。
-
2.使用merge()方法
如果指定的键尚未关联,则merge()方法将指定的值与指定的键关联。
但是,如果指定的键已经与一个值关联,它将把新的指定值与现有的旧值合并
2、LinkedHashMap
LinkedHashMap类提供了Map接口的哈希表和链表实现。
LinkedHashMap继承了HashMap类,以将其条目存储在哈希表中。它在内部在所有条目之间维护一个双链列表,以对条目进行排序。
3、ConcurrentHashMap
ConcurrentHashMap类提供了线程安全的映射。 也就是说,多个线程可以一次访问该映射,而不会影响映射中条目的一致性。
ConcurrentHashMap和HashMap 的区别
以下是ConcurrentHashMap和HashMap之间的一些区别,
-
ConcurrentHashMap是线程安全的集合。也就是说,多个线程可以同时访问和修改它。
-
ConcurrentHashMap提供用于批量操作的方法,例如forEach(),search()和reduce()。
为什么选择ConcurrentHashMap?
-
ConcurrentHashMap类允许多个线程修改操作并发进行。
-
默认情况下,并发哈希映射分为16段。这就是为什么允许16个线程同时修改映射的原因。但是,一次可以访问任意数量的线程。
-
如果指定的键已经存在,则putIfAbsent()方法将不会覆盖映射中的条目。
五、Iterator路线
Iterator即迭代器,实现透明的遍历访问,即不论集合的内部组织形式是何种样子的,只要实现了Iterator接口就能方便的实现遍历。
迭代器的方法
Iterator接口提供了4种方法,可用于对集合元素执行各种操作。常用的就是next和hasNext。
-
hasNext() - 如果集合中存在元素,则返回true
-
next() - 返回集合的下一个元素
-
remove() -删除next()返回的最后一个元素
-
forEachRemaining() - 对集合的每个剩余元素执行指定的操作
//创建Iterator的实例 Iterator<Integer> iterate = numbers.iterator();
ListIterator 接口
ListIterator提供了访问列表元素的功能。
它是双向的。 这意味着它允许我们在两个方向上迭代列表的元素。
ListIterator的方法
ListIterator接口提供了可用于对列表元素执行各种操作的方法。
-
hasNext() - 如果列表中存在元素,则返回true
-
next() - 返回列表的下一个元素
-
nextIndex() - 返回next()方法将返回的元素的索引
-
previous() - 返回列表的前一个元素
-
previousIndex()- 返回previous()方法将返回的元素的索引
-
remove()- 删除由next()或previous()返回的元素
-
set() - 将next()或previous()返回的元素替换为指定的元素
六、java集合框架提供的算法
1.使用sort()排序
// 使用sort()方法 Collections.sort(numbers);
2.使用shuffle进行洗牌
//使用shuffle()方法 Collections.shuffle(numbers);
3.常规数据处理
在Java中,集合框架提供了可用于处理数据的不同方法。
-
-
reverse() - 反转元素的顺序
-
fill() - 用指定的值替换集合中的每个元素
-
copy() - 创建从指定源到目标的元素副本
-
swap() - 交换集合中两个元素的位置
-
addAll() - 将集合的所有元素添加到其他集合
-
4.使用binarySearch()搜索
// 使用 binarySearch(),返回在集合中的位置,在执行binarySearch()方法之前,应对集合进行排序。 int pos = Collections.binarySearch(numbers, 3);
5.组合
-
-
frequency() - 返回元素在集合中存在的次数计数
-
disjoint() - 检查两个集合是否包含一些公共元素
-
6.寻找最大和最小元素
Java集合框架的min()和max()方法分别用于查找最小和最大元素。