Java中的集合类

Java中的集合类

就如前几天所说的那样,开个新帖子,写一点关于Java中的集合类的内容,但本篇的内容只关注这些集合类提供的各种功能,不会去太过详细的讲解这些集合类的底层实现(太复杂了,在下实在没弄明白)

概述

先来看一看Java中集合类的具体结构

d236588d91e8b569dff99a1f5090a031

怎么样,是不是看着就有些复杂,这么多类想要学完要花多少时间啊,不要慌,其实在实际代码过程中并不会真的用到这么多,我们只需要长其中的最常用的内容即可

从图中可以看到,Java的集合类或者说collection类可以分为四个大的子类,List,Queue,Map,Set。我们索性直接一次介绍

Collection接口

在开始展示各个类之前,我们首先看一看Collection接口中又哪些方法吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

public interface Collection<E> extends Iterable<E> {
//查询与操作部分
int size();//获取当前集合中元素的数量

boolean isEmpty();//判断当前集合是否为空


boolean contains(Object o);//判断当前集合是否含有对应的元素


Iterator<E> iterator();//返回该集合的迭代器(迭代器后面会将)


Object[] toArray();//将对应的集合转换为数组


<T> T[] toArray(T[] a);//和上面的方法作用一样,但考虑了泛型的支持,,如果传入的数组足够大,则直接将元素保存在传入的数组中,如果不够大,则返回新创建的与传入的数组同类型的数组


boolean containsAll(Collection<?> c);//判断当前集合是否含有传入集合中的全部元素


boolean addAll(Collection<? extends E> c);//将传入集合中的元素全部添加至原有集合中

boolean removeAll(Collection<?> c);//和上面的类似


default boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
boolean removed = false;
final Iterator<E> each = iterator();
while (each.hasNext()) {
if (filter.test(each.next())) {
each.remove();
removed = true;
}
}
return removed;
}//在上一篇中提到过,支持函数式接口,根据函数要求判断,满足条件的移除


boolean retainAll(Collection<?> c);//只保留传入的集合中有的元素


void clear();//清楚全部元素


//排序与哈希部分
boolean equals(Object o);//用于判断两个集合是否相等


int hashCode();//返回当前整个对象的哈希值


default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}//将当前集合转换为流(莫慌,后面会将)


default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}//将当前集合转换为并行流(后面会将)
}

上面这些方法是我直接从源码中复制过来然后加了点注释,有条件最好自己可以去看看源码

LIst类

直接上源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
public interface List<E> extends SequencedCollection<E> {
// 查询与数据操作部分,一些前面已经介绍过的方法我就不再重复


int size();


boolean isEmpty();


boolean contains(Object o);


Iterator<E> iterator();


Object[] toArray();



<T> T[] toArray(T[] a);



boolean add(E e);


boolean remove(Object o);



boolean containsAll(Collection<?> c);

boolean addAll(Collection<? extends E> c);


boolean addAll(int index, Collection<? extends E> c);


boolean removeAll(Collection<?> c);


boolean retainAll(Collection<?> c);


default void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
final ListIterator<E> li = this.listIterator();
while (li.hasNext()) {
li.set(operator.apply(li.next()));
}
}//根据条件替换数据


@SuppressWarnings({"unchecked", "rawtypes"})
default void sort(Comparator<? super E> c) {
Object[] a = this.toArray();
Arrays.sort(a, (Comparator) c);
ListIterator<E> i = this.listIterator();
for (Object e : a) {
i.next();
i.set((E) e);
}
}//排序方法,也在上一篇中提到过


void clear();


// Comparison and hashing

boolean equals(Object o);


int hashCode();



E get(int index);//获取下标所对应的元素


E set(int index, E element);//设置下标所指值为element


void add(int index, E element);//在指定位置插入元素,其余元素顺次右移


E remove(int index);//移除下标对应的元素,其余元素顺次左移



int indexOf(Object o);//返回该对象第一次出现时的下标


int lastIndexOf(Object o);//返回该对象最后一次出现时的下标



ListIterator<E> listIterator();//返回对应的迭代器

ListIterator<E> listIterator(int index);//返回从当前下标开始的一个迭代器


List<E> subList(int fromIndex, int toIndex);//返回指定下标范围的元素


@Override
default Spliterator<E> spliterator() {
if (this instanceof RandomAccess) {
return new AbstractList.RandomAccessSpliterator<>(this);
} else {
return Spliterators.spliterator(this, Spliterator.ORDERED);
}
}//同样是返回迭代器,不过是并行的

default void addFirst(E e) {
this.add(0, e);
}//在开头出添加对应的元素


default void addLast(E e) {
this.add(e);
}//在结尾出添加


default E getFirst() {
if (this.isEmpty()) {
throw new NoSuchElementException();
} else {
return this.get(0);
}
}//获取第一个元素


default E getLast() {
if (this.isEmpty()) {
throw new NoSuchElementException();
} else {
return this.get(this.size() - 1);
}
}//获取最后一个


default E removeFirst() {
if (this.isEmpty()) {
throw new NoSuchElementException();
} else {
return this.remove(0);
}
}//移除第一个


default E removeLast() {
if (this.isEmpty()) {
throw new NoSuchElementException();
} else {
return this.remove(this.size() - 1);
}
}//移除最后一个


default List<E> reversed() {
return ReverseOrderListView.of(this, true); // we must assume it's modifiable
}//将列表反转


@SuppressWarnings("unchecked")//下面这一长串of的功能都是返回一个不可变的列表,只是其中的元素数量不同罢了
static <E> List<E> of() {
return (List<E>) ImmutableCollections.EMPTY_LIST;
}


static <E> List<E> of(E e1) {
return new ImmutableCollections.List12<>(e1);
}


static <E> List<E> of(E e1, E e2) {
return new ImmutableCollections.List12<>(e1, e2);
}


static <E> List<E> of(E e1, E e2, E e3) {
return ImmutableCollections.listFromTrustedArray(e1, e2, e3);
}


static <E> List<E> of(E e1, E e2, E e3, E e4) {
return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4);
}


static <E> List<E> of(E e1, E e2, E e3, E e4, E e5) {
return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5);
}


static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6) {
return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5,
e6);
}


static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7) {
return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5,
e6, e7);
}


static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8) {
return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5,
e6, e7, e8);
}


static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) {
return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5,
e6, e7, e8, e9);
}

/
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) {
return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5,
e6, e7, e8, e9, e10);
}


@SafeVarargs
@SuppressWarnings("varargs")
static <E> List<E> of(E... elements) {
switch (elements.length) { // implicit null check of elements
case 0:
@SuppressWarnings("unchecked")
var list = (List<E>) ImmutableCollections.EMPTY_LIST;
return list;
case 1:
return new ImmutableCollections.List12<>(elements[0]);
case 2:
return new ImmutableCollections.List12<>(elements[0], elements[1]);
default:
return ImmutableCollections.listFromArray(elements);
}
}//特别解释一下,这里的形参是一个可变长参数,也就是说支持输入任意个参数,然后JVM会将这些参数打包成一个数组


static <E> List<E> copyOf(Collection<? extends E> coll) {
return ImmutableCollections.listCopy(coll);
}//复制列表,不用多说
}

或许你会返现,LIst与数组有很多相似之处,没错,LIst就是基于数组写的,只不过添加了一大堆扩展方法方便使用

当然,更下一级的ArrayLIst与linkedLIst类也是非常有用的,我们干脆再看看这部分的源码

ArrayLIst

所谓的ArrayLIst也就是顺序表,这部分的功能实现本身不是很难,我们自己写一个出来的问题也不是很大

这一部分的源码相当长,所以我就不展示全部代码了,只写一部分比较常用的,如果有兴趣可以自行阅读

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{


private static final int DEFAULT_CAPACITY = 10;//不要忘了,这部分的底层是数组,这部分就是默认的数组容量


transient Object[] elementData;


private int size;//当前元素数量


public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}//构造函数,这只是其中的一个,还支持填入一个LIst代表要创建的集合中包含的元素,或者什么也不填代表创建一个默认大小的空数组

...

public void ensureCapacity(int minCapacity) {
if (minCapacity > elementData.length
&& !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
&& minCapacity <= DEFAULT_CAPACITY)) {
modCount++;//一个用来记录当前类的结构被修改的次数
grow(minCapacity);//用来对数组扩容
}//用来判断数组的容量是否充足,如果不足则进行扩容
}
public boolean add(E e) {
ensureCapacityInternal(size + 1); //先确保数组容量充足
elementData[size++] = e;
return true;
}


private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); //这里是指新的容量为原来容量的1.5倍
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}//刚才发现在jdk21中,这部分源码的实现有了一定的变动,但无需在意,名称和用法是没有变的
}

值得注意的是下面的区别

1
2
3
4
5
6
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(10); //添加Integer的值10
list.remove((Integer) 10); //注意,不能直接用10,默认情况下会认为传入的是int类型值,删除的是下标为10的元素,我们这里要删除的是刚刚传入的值为10的Integer对象
System.out.println(list); //可以看到,此时元素成功被移除
}

如果你输入的是基本的数据类型,则判断为要删除对应下标的元素,如果输入对应的包装后的对象,则是删除和这个对象相同的元素(只会删除第一次出现的那个)

类似这样的小问题还有很多,我的建议是有时间自己看看源码,注释写的都很详细

LinkedLIst

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
transient int size = 0;

//引用首结点
transient Node<E> first;

//引用尾结点
transient Node<E> last;

//构造方法,很简单,直接创建就行了
public LinkedList() {
}

...

private static class Node<E> { //内部使用的结点类
E item;
Node<E> next; //不仅保存指向下一个结点的引用,还保存指向上一个结点的引用
Node<E> prev;

Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}

...
}

这就是一个提前创建好的链表类,其实功能很简单,如果你愿意的话用ArrayLIst直接模拟这里面的全部功能也不是不行.所以这部分我就说的简单一点

剩下的两个子类分别是矢量类和队列类,矢量其实不怎么用,队列的话下面再讲

迭代器

现在先让我们来解释解释那个看不懂的迭代器是什么东西吧

首先,看一个例子

1
2
3
4
5
6
public static void main(String[] args) {
List<String> list = Arrays.asList("A", "B", "C");
for (String s : list) {
System.out.println(s);
}
}

不知道你是否见过这样的例子,用加强的for循环来遍历集合的每一个元素,我们知道如果是一个数组,那么编译过后会将这个循环干脆变成普通的for循环,但是如果是LIst呢

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
List<String> list = Arrays.asList("A", "B", "C");
Iterator var2 = list.iterator();

while(var2.hasNext()) {
String s = (String)var2.next();
System.out.println(s);
}

}

会创造出一个迭代器用来遍历,这就引出了这部分的话题,迭代器到底是怎样构造的呢

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public interface Iterator<E> {

boolean hasNext();//判断迭代器中是否还有元素


E next();//返回下一个元素

//将删除上一个被遍历的元素(最新的被next返回的值)
default void remove() {
throw new UnsupportedOperationException("remove");
}


default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}//自定义的修改行为
}

如果你学过C语言,那么一定会感觉这不就是指针操作吗,对,这就是指针操作(不过事实上Java中没有指针,这只是在模拟)

还有一件事值得一提,迭代器是一次性的,不会倒着往回走,所以用过一次后就没用了,当然,我还是比较推荐直接上for循环的,这两者本质上没有什么区别

接下来再看一看LIstiterator,这是为LIst强化过的迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public interface ListIterator<E> extends Iterator<E> {

boolean hasNext();


E next();


boolean hasPrevious();//是否存在前一个元素


E previous();//获取前一个元素


int nextIndex();//获取下一个元素的下标


int previousIndex();获取前一个元素的下标





void remove();


void set(E e);//修改当前的元素


void add(E e);//在当前元素后插入一个元素
}

Queue与Deque

说人话就是队列与双向队列,队列是什么,队列是一种经典的数据结构,要求先进先出(就像排队一样,先排队的人先离开),先看看源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public interface Queue<E> extends Collection<E> {

boolean add(E e);//入队操作,在队尾添加一个元素


boolean offer(E e);//入队操作,在队尾添加一个元素,但add无法添加抛出异常,offer则只返回false


E remove();//移除队首元素,并将队首元素返回,队首为空则抛出异常


E poll();//移除并返回队首元素,如果队伍为空则返回NULL


E element();//返回但不移除队首元素,不存在抛出异常


E peek();//返回但不移除队首元素,不存在返回NULL
//注意:虽然不会删除元素,但指针还是会向后移
}

可以直接将linkedlist作为队列操作

1
Queue<String> q = new LinkedList<String>();

再来看一看双向队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
public interface Deque<E> extends Queue<E>, SequencedCollection<E> {

void addFirst(E e);//在队首添加


void addLast(E e);//在队尾添加
//下面的一些简单方法不再过多赘述

boolean offerFirst(E e);


boolean offerLast(E e);


E removeFirst();


E removeLast();


E pollFirst();


E pollLast();

//返回队首/队尾元素,失败抛出异常
E getFirst();


E getLast();

//返回队首/队尾元素,不存在返回null
E peekFirst();


E peekLast();


boolean removeFirstOccurrence(Object o);//删除指定元素的第一次出现


boolean removeLastOccurrence(Object o);//删除该元素的最后一次出现

//下面的这些方法是将这个双向队列直接视作一个普通队列所进行的操作,出现的意义就是减少记忆量
boolean add(E e);


boolean offer(E e);


E remove();


E poll();


E element();

E peek();


boolean addAll(Collection<? extends E> c);//不用多说

//下面的这些方法是将队列视作一个栈进行的操作
void push(E e);


E pop();



boolean remove(Object o);//移除指定元素的第一次出现


boolean contains(Object o);//判断是否存在


int size();//获取元素数量


Iterator<E> iterator();//获得一个迭代器


Iterator<E> descendingIterator();//生成一个反向迭代器,会从队尾开始遍历


default Deque<E> reversed() {
return ReverseOrderDequeView.of(this);
}//将队列反转
}

这里再提一嘴,其实还有一种队列叫做优先级队列,可以给每个对象加一个优先级,出队顺序将依照优先级进行,但队列内部顺序依照入队顺序排列

1
2
3
4
5
6
7
8
9
public static void main(String[] args) {
Queue<Integer> queue = new PriorityQueue<>((a, b) -> b - a); //按照从大到小顺序出队
queue.offer(10);
queue.offer(4);
queue.offer(5);
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());
}

Set类

直接上源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
public interface Set<E> extends Collection<E> {
//过于简单的直接跳过
int size();


boolean isEmpty();


boolean contains(Object o);


Iterator<E> iterator();


Object[] toArray();


<T> T[] toArray(T[] a);



boolean add(E e);//在尾部添加



boolean remove(Object o);



boolean containsAll(Collection<?> c);


boolean addAll(Collection<? extends E> c);


boolean retainAll(Collection<?> c);


boolean removeAll(Collection<?> c);


void clear();



boolean equals(Object o);


int hashCode();


@Override
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, Spliterator.DISTINCT);
}

@SuppressWarnings("unchecked")
static <E> Set<E> of() {
return (Set<E>) ImmutableCollections.EMPTY_SET;
}


static <E> Set<E> of(E e1) {
return new ImmutableCollections.Set12<>(e1);
}


static <E> Set<E> of(E e1, E e2) {
return new ImmutableCollections.Set12<>(e1, e2);
}


static <E> Set<E> of(E e1, E e2, E e3) {
return new ImmutableCollections.SetN<>(e1, e2, e3);
}


static <E> Set<E> of(E e1, E e2, E e3, E e4) {
return new ImmutableCollections.SetN<>(e1, e2, e3, e4);
}


static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5) {
return new ImmutableCollections.SetN<>(e1, e2, e3, e4, e5);
}


static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5, E e6) {
return new ImmutableCollections.SetN<>(e1, e2, e3, e4, e5,
e6);
}


static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7) {
return new ImmutableCollections.SetN<>(e1, e2, e3, e4, e5,
e6, e7);
}


static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8) {
return new ImmutableCollections.SetN<>(e1, e2, e3, e4, e5,
e6, e7, e8);
}


static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) {
return new ImmutableCollections.SetN<>(e1, e2, e3, e4, e5,
e6, e7, e8, e9);
}


static <E> Set<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) {
return new ImmutableCollections.SetN<>(e1, e2, e3, e4, e5,
e6, e7, e8, e9, e10);
}


@SafeVarargs
@SuppressWarnings("varargs")
static <E> Set<E> of(E... elements) {
switch (elements.length) { // implicit null check of elements
case 0:
@SuppressWarnings("unchecked")
var set = (Set<E>) ImmutableCollections.EMPTY_SET;
return set;
case 1:
return new ImmutableCollections.Set12<>(elements[0]);
case 2:
return new ImmutableCollections.Set12<>(elements[0], elements[1]);
default:
return new ImmutableCollections.SetN<>(elements);
}
}


@SuppressWarnings("unchecked")
static <E> Set<E> copyOf(Collection<? extends E> coll) {
if (coll instanceof ImmutableCollections.AbstractImmutableSet) {
return (Set<E>)coll;
} else if (coll.isEmpty()) { // Implicit nullcheck of coll
return Set.of();
} else {
return (Set<E>)Set.of(new HashSet<>(coll).toArray());
}
}//返回一个与传入集合元素相同的不可变集合
}

可以看到,大部分的方法都与collection类中的一致,但Set与LIst有两点不同

  • 不允许出现重复元素
  • 不支持通过下标访问

为什么呢,其实Set类是基于哈希表实现的,所以本身就无法保存插入的顺序,也就是说如果你用迭代器遍历所有元素,那么遍历的顺序是无法预知的

如果需要保证一定的顺序,可以使用LInkedHashSet,这个类会基于链表维护插入的顺序

当然,还有一个类交TreeSet,这个类在插入时本身会进行排序,当然,也可以自定义排序规则

1
2
3
4
5
6
7
public static void main(String[] args) {
TreeSet<Integer> set = new TreeSet<>((a, b) -> b - a); //同样是一个Comparator
set.add(1);
set.add(3);
set.add(2);
System.out.println(set);
}

Set的底层其实是Map,所以我们接下来先看一看Map再继续讲

Map类

Map类,或者说映射类所存储的是一个个键值对,即同时保存一条数据的主键与具体数据,其中主键可以作为索引,让我们方便的调用各个值.如果具象化一点讲的话就是对于一个学生,我们将他的学号作为一个主键,将他的其他信息作为值进行保留,这样只需要使用学号就能方便的查询所有数据

可以先看一看源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370

public interface Map<K, V> {

int size();//这里的size所描述的是有多少键值对

boolean isEmpty();


boolean containsKey(Object key);//是否存在对应的键


boolean containsValue(Object value);//是否存在与该键对应的值


V get(Object key);//通过键获得值


V put(K key, V value);//放入键值对,如果当前键已经有对应的值,返回对应的值,如果没有返回null

V remove(Object key);//根据键移除键值对



void putAll(Map<? extends K, ? extends V> m);//将传入的Map中的全部键值对复制到当前Map


void clear();//清空当前Map


Set<K> keySet();//返回一个Set,内容为该Map的所有键


Collection<V> values();//返回一个集合,内容为该Map的全部值


Set<Map.Entry<K, V>> entrySet();//返回一个Set,内容为所有的键值对


interface Entry<K, V> {//键值对的存储形式

K getKey();

V getValue();

/
V setValue(V value);

boolean equals(Object o);


int hashCode();//返回该键值对的哈希值


public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K, V>> comparingByKey() {
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> c1.getKey().compareTo(c2.getKey());
}//这个方法返回的是一个比较器,或者说就是对一个函数接口的具体实现,通过键比较


public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K, V>> comparingByValue() {
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> c1.getValue().compareTo(c2.getValue());
}//返回通过值比较的比价器
//其实我个人页不怎么建议用,这种东西最好是在用到的时候自己写


public static <K, V> Comparator<Map.Entry<K, V>> comparingByKey(Comparator<? super K> cmp) {
Objects.requireNonNull(cmp);
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> cmp.compare(c1.getKey(), c2.getKey());
}//返回对键值对进行比较的比较器

//下面的比较器不做介绍了
public static <K, V> Comparator<Map.Entry<K, V>> comparingByValue(Comparator<? super V> cmp) {
Objects.requireNonNull(cmp);
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> cmp.compare(c1.getValue(), c2.getValue());
}


@SuppressWarnings("unchecked")
public static <K, V> Map.Entry<K, V> copyOf(Map.Entry<? extends K, ? extends V> e) {
Objects.requireNonNull(e);
if (e instanceof KeyValueHolder) {
return (Map.Entry<K, V>) e;
} else {
return Map.entry(e.getKey(), e.getValue());
}
}
}


boolean equals(Object o);


int hashCode();//获得Map的哈希值


default V getOrDefault(Object key, V defaultValue) {
V v;
return (((v = get(key)) != null) || containsKey(key))
? v
: defaultValue;
}//如果传入的键存在则返回对应值,如不存在则返回传入的默认值

default void forEach(BiConsumer<? super K, ? super V> action) {
Objects.requireNonNull(action);
for (Map.Entry<K, V> entry : entrySet()) {
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch (IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
action.accept(k, v);
}
}//之前介绍过

default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
Objects.requireNonNull(function);
for (Map.Entry<K, V> entry : entrySet()) {
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch (IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}

// ise thrown from function is not a cme.
v = function.apply(k, v);

try {
entry.setValue(v);
} catch (IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
}
}//也介绍过,传入一个函数式接口的实现


default V putIfAbsent(K key, V value) {
V v = get(key);
if (v == null) {
v = put(key, value);
}

return v;
}//如果对应的键不存在,在Map总添加该键值对


default boolean remove(Object key, Object value) {
Object curValue = get(key);
if (!Objects.equals(curValue, value) ||
(curValue == null && !containsKey(key))) {
return false;
}
remove(key);
return true;
}如果传入的键值对存在则删除,返回true,如果不存在返回false


default boolean replace(K key, V oldValue, V newValue) {
Object curValue = get(key);
if (!Objects.equals(curValue, oldValue) ||
(curValue == null && !containsKey(key))) {
return false;
}
put(key, newValue);
return true;
}//将键对应的值替换为新的,替换成功返回true


default V replace(K key, V value) {
V curValue;
if (((curValue = get(key)) != null) || containsKey(key)) {
curValue = put(key, value);
}
return curValue;
}//返回同put


default V computeIfAbsent(K key,
Function<? super K, ? extends V> mappingFunction) {
Objects.requireNonNull(mappingFunction);
V v;
if ((v = get(key)) == null) {
V newValue;
if ((newValue = mappingFunction.apply(key)) != null) {
put(key, newValue);
return newValue;
}
}

return v;
}//传入一个键,然后若键所对应的值不存在,通过键按照接口函数的计算出一个值,并一起放入Map中


default V computeIfPresent(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
V oldValue;
if ((oldValue = get(key)) != null) {
V newValue = remappingFunction.apply(key, oldValue);
if (newValue != null) {
put(key, newValue);
return newValue;
} else {
remove(key);
return null;
}
} else {
return null;
}
}//如果传入的键对应的值存在,则通过传入的方法进行修改

default V compute(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
V oldValue = get(key);

V newValue = remappingFunction.apply(key, oldValue);
if (newValue == null) {
// delete mapping
if (oldValue != null || containsKey(key)) {
// something to remove
remove(key);
return null;
} else {
// nothing to do. Leave things as they were.
return null;
}
} else {
// add or replace old mapping
put(key, newValue);
return newValue;
}
}//无论如何通过传入的key计算一个值然后进程替换


default V merge(K key, V value,
BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
Objects.requireNonNull(value);
V oldValue = get(key);
V newValue = (oldValue == null) ? value :
remappingFunction.apply(oldValue, value);
if (newValue == null) {
remove(key);
} else {
put(key, newValue);
}
return newValue;
}//通过某种方式将一个键对应的值与传入的值结合

//接下来的一堆of,不多说
@SuppressWarnings("unchecked")
static <K, V> Map<K, V> of() {
return (Map<K,V>) ImmutableCollections.EMPTY_MAP;
}


static <K, V> Map<K, V> of(K k1, V v1) {
return new ImmutableCollections.Map1<>(k1, v1);
}


static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2) {
return new ImmutableCollections.MapN<>(k1, v1, k2, v2);
}


static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3) {
return new ImmutableCollections.MapN<>(k1, v1, k2, v2, k3, v3);
}


static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) {
return new ImmutableCollections.MapN<>(k1, v1, k2, v2, k3, v3, k4, v4);
}


static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) {
return new ImmutableCollections.MapN<>(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5);
}


static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5,
K k6, V v6) {
return new ImmutableCollections.MapN<>(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5,
k6, v6);
}


static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5,
K k6, V v6, K k7, V v7) {
return new ImmutableCollections.MapN<>(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5,
k6, v6, k7, v7);
}


static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5,
K k6, V v6, K k7, V v7, K k8, V v8) {
return new ImmutableCollections.MapN<>(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5,
k6, v6, k7, v7, k8, v8);
}


static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5,
K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9) {
return new ImmutableCollections.MapN<>(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5,
k6, v6, k7, v7, k8, v8, k9, v9);
}


static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5,
K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9, K k10, V v10) {
return new ImmutableCollections.MapN<>(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5,
k6, v6, k7, v7, k8, v8, k9, v9, k10, v10);
}


@SafeVarargs
@SuppressWarnings("varargs")
static <K, V> Map<K, V> ofEntries(Entry<? extends K, ? extends V>... entries) {
if (entries.length == 0) { // implicit null check of entries array
@SuppressWarnings("unchecked")
var map = (Map<K,V>) ImmutableCollections.EMPTY_MAP;
return map;
} else if (entries.length == 1) {
// implicit null check of the array slot
return new ImmutableCollections.Map1<>(entries[0].getKey(),
entries[0].getValue());
} else {
Object[] kva = new Object[entries.length << 1];
int a = 0;
for (Entry<? extends K, ? extends V> entry : entries) {
// implicit null checks of each array slot
kva[a++] = entry.getKey();
kva[a++] = entry.getValue();
}
return new ImmutableCollections.MapN<>(kva);
}
}

//返回一个新的键值对对象
static <K, V> Entry<K, V> entry(K k, V v) {
// KeyValueHolder checks for nulls
return new KeyValueHolder<>(k, v);
}


@SuppressWarnings({"rawtypes","unchecked"})
static <K, V> Map<K, V> copyOf(Map<? extends K, ? extends V> map) {
if (map instanceof ImmutableCollections.AbstractImmutableMap) {
return (Map<K,V>)map;
} else if (map.isEmpty()) { // Implicit nullcheck of map
return Map.of();
} else {
return (Map<K,V>)Map.ofEntries(map.entrySet().toArray(new Entry[0]));
}
}//返回一个与传入Map相同的不可变Map
}

还是那句话,如果感觉看不懂自己可以去读源码

Map实质上是基于哈希表实现的,具体的实现要涉及到不少复杂的内容,这里暂时不讲,过段时间再细说

之前提到了hashset,这玩意的实质就是一个Map的子类hashmap上加了一点结构,存数据完全是用Map存的,然后treeset实际上就是一个treemap

最常用的应该就是hashmap和treemap了,二者的功能就是上面提到的两种set的功能,不过没有不能重复的限制

1
2
3
4
5
public static void main(String[] args) {
Map<String , String> map = new HashMap<>();
System.out.println(map.put("0", "十七张"));
System.out.println(map.put("0", "慈善家"));
}

Stream

接下来介绍的是Stream也就是流的相关功能,所谓的流,可以理解为工厂的流水线,将里面的每个元素都按照预定的流程处理之后再输出,上面提到的所有包含collection接口的类都支持转变为流进行数据处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
public interface Stream<T> extends BaseStream<T, Stream<T>> {


Stream<T> filter(Predicate<? super T> predicate);//过滤,根据传入的函数过滤


<R> Stream<R> map(Function<? super T, ? extends R> mapper);//对每个元素按照传入的函数进行处理


IntStream mapToInt(ToIntFunction<? super T> mapper);//用传入的函数将元素转为int

//下面几个类似
LongStream mapToLong(ToLongFunction<? super T> mapper);


DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);

//下面的flat系列的作用是将每一个元素再转换为一个流,然后对这些流用流的方法处理
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);


IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper);


LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper);


DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper);

//接下来是多值系列,将一个值处理为0到多个值放入流中
default <R> Stream<R> mapMulti(BiConsumer<? super T, ? super Consumer<R>> mapper) {
Objects.requireNonNull(mapper);
return flatMap(e -> {
SpinedBuffer<R> buffer = new SpinedBuffer<>();
mapper.accept(e, buffer);
return StreamSupport.stream(buffer.spliterator(), false);
});
}


default IntStream mapMultiToInt(BiConsumer<? super T, ? super IntConsumer> mapper) {
Objects.requireNonNull(mapper);
return flatMapToInt(e -> {
SpinedBuffer.OfInt buffer = new SpinedBuffer.OfInt();
mapper.accept(e, buffer);
return StreamSupport.intStream(buffer.spliterator(), false);
});
}


default LongStream mapMultiToLong(BiConsumer<? super T, ? super LongConsumer> mapper) {
Objects.requireNonNull(mapper);
return flatMapToLong(e -> {
SpinedBuffer.OfLong buffer = new SpinedBuffer.OfLong();
mapper.accept(e, buffer);
return StreamSupport.longStream(buffer.spliterator(), false);
});
}


default DoubleStream mapMultiToDouble(BiConsumer<? super T, ? super DoubleConsumer> mapper) {
Objects.requireNonNull(mapper);
return flatMapToDouble(e -> {
SpinedBuffer.OfDouble buffer = new SpinedBuffer.OfDouble();
mapper.accept(e, buffer);
return StreamSupport.doubleStream(buffer.spliterator(), false);
});
}

//去除重复元素
Stream<T> distinct();

//将元素排序
Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);

//将每个元素填入消费接口运行,但不改变元素
Stream<T> peek(Consumer<? super T> action);

//限制流的最大长度
Stream<T> limit(long maxSize);

//从流中去除前n个元素
Stream<T> skip(long n);

//下面的两个Default不用管,外部无法访问
default Stream<T> takeWhile(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
// Reuses the unordered spliterator, which, when encounter is present,
// is safe to use as long as it configured not to split
return StreamSupport.stream(
new WhileOps.UnorderedWhileSpliterator.OfRef.Taking<>(spliterator(), true, predicate),
isParallel()).onClose(this::close);
}


default Stream<T> dropWhile(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
// Reuses the unordered spliterator, which, when encounter is present,
// is safe to use as long as it configured not to split
return StreamSupport.stream(
new WhileOps.UnorderedWhileSpliterator.OfRef.Dropping<>(spliterator(), true, predicate),
isParallel()).onClose(this::close);
}

//注意,这个方法只能放在最后用,因为他不再返回流
void forEach(Consumer<? super T> action);

//强调保证原来元素的顺序
void forEachOrdered(Consumer<? super T> action);

//返回为数组
Object[] toArray();


<A> A[] toArray(IntFunction<A[]> generator);

//这是一个累加器,将每个元素按照传入的方式累加,最后只返回一个值,第一个形参为初始值
T reduce(T identity, BinaryOperator<T> accumulator);

//将流的第一个元素作为初始值
Optional<T> reduce(BinaryOperator<T> accumulator);


<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner);

//collect会通过供给函数再创造一个值,然后用消费函数处理
<R> R collect(Supplier<R> supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner);


<R, A> R collect(Collector<? super T, A, R> collector);

//转化为LIst
@SuppressWarnings("unchecked")
default List<T> toList() {
return (List<T>) Collections.unmodifiableList(new ArrayList<>(Arrays.asList(this.toArray())));
}

//取最小
Optional<T> min(Comparator<? super T> comparator);

//取最大
Optional<T> max(Comparator<? super T> comparator);

//计数
long count();

//判断是否有满足的
boolean anyMatch(Predicate<? super T> predicate);

//判断是否全部满足
boolean allMatch(Predicate<? super T> predicate);

//判断是否全不满足
boolean noneMatch(Predicate<? super T> predicate);

//返回第一个元素
Optional<T> findFirst();

//随机的返回流中的一个元素
Optional<T> findAny();

//返回一个builder对象
public static<T> Builder<T> builder() {
return new Streams.StreamBuilderImpl<>();
}

//返回一个空的流
public static<T> Stream<T> empty() {
return StreamSupport.stream(Spliterators.<T>emptySpliterator(), false);
}

//of系列
public static<T> Stream<T> of(T t) {
return StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);
}

//根据输入返回流
public static<T> Stream<T> ofNullable(T t) {
return t == null ? Stream.empty()
: StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);
}


@SafeVarargs
@SuppressWarnings("varargs") // Creating a stream from an array is safe
public static<T> Stream<T> of(T... values) {
return Arrays.stream(values);
}

//生成一个无限流,从seed开始,不断将seed投入UnaryOperator<T>中获得新值
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {
Objects.requireNonNull(f);
Spliterator<T> spliterator = new Spliterators.AbstractSpliterator<>(Long.MAX_VALUE,
Spliterator.ORDERED | Spliterator.IMMUTABLE) {
T prev;
boolean started;

@Override
public boolean tryAdvance(Consumer<? super T> action) {
Objects.requireNonNull(action);
T t;
if (started)
t = f.apply(prev);
else {
t = seed;
started = true;
}
action.accept(prev = t);
return true;
}
};
return StreamSupport.stream(spliterator, false);
}

//加入判断,在不符合hasNext时流终止
public static<T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next) {
Objects.requireNonNull(next);
Objects.requireNonNull(hasNext);
Spliterator<T> spliterator = new Spliterators.AbstractSpliterator<>(Long.MAX_VALUE,
Spliterator.ORDERED | Spliterator.IMMUTABLE) {
T prev;
boolean started, finished;

@Override
public boolean tryAdvance(Consumer<? super T> action) {
Objects.requireNonNull(action);
if (finished)
return false;
T t;
if (started)
t = next.apply(prev);
else {
t = seed;
started = true;
}
if (!hasNext.test(t)) {
prev = null;
finished = true;
return false;
}
action.accept(prev = t);
return true;
}

@Override
public void forEachRemaining(Consumer<? super T> action) {
Objects.requireNonNull(action);
if (finished)
return;
finished = true;
T t = started ? next.apply(prev) : seed;
prev = null;
while (hasNext.test(t)) {
action.accept(t);
t = next.apply(t);
}
}
};
return StreamSupport.stream(spliterator, false);
}

//用供给函数生成一个流,最大元素量为long的最大值
public static<T> Stream<T> generate(Supplier<? extends T> s) {
Objects.requireNonNull(s);
return StreamSupport.stream(
new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false);
}

//将两个流合并为一个新流
public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) {
Objects.requireNonNull(a);
Objects.requireNonNull(b);

@SuppressWarnings("unchecked")
Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>(
(Spliterator<T>) a.spliterator(), (Spliterator<T>) b.spliterator());
Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel());
return stream.onClose(Streams.composedClose(a, b));
}

//用来直接创建一个builder
public interface Builder<T> extends Consumer<T> {


@Override
void accept(T t);


default Builder<T> add(T t) {
accept(t);
return this;
}


Stream<T> build();

}
}

感觉怎么样,是不是记不住,记不住很正常,到时候直接看IDE的提示就行了,但你至少要知道存在这样的功能

结语

其实介绍了这么多,只是集合类的冰山一角,但话又说回来其实里面大多数方法的实现我们自己都能写出来,如果实在想不起来的话也可以直接自己写一个(当然,拼性能肯定是比不过库中的方法,底层实际上有很多复杂的数据处理方法),不用太着急,接下来多用用就都记住了,如果有空也可以自己到源码里面翻以翻,包有好处的

上图

3113e63a 5311 4401 9d51 32e74a22adc6


Java中的集合类
http://soulmate.org.cn/2024/12/24/Java中的集合类/
作者
Soul
发布于
2024年12月24日
许可协议