前言

  • Java进阶课程的第二篇,集合进阶与Stream流相关内容。

  • 学习完基础之后就是进阶的内容了。


包含的知识

  1. Collection接口及其子类:
  • List:有序、可重复元素的集合,如ArrayList。
  • Set:无序、不重复元素的集合,如HashSet、LinkedHashSet(保持插入顺序)、TreeSet(按自然排序或自定义比较器排序)。
  1. 遍历集合的方法:
  • 迭代器遍历:通过调用集合的iterator()方法获取迭代器进行遍历。
  • 增强for循环:直接使用foreach语法结构遍历集合中的每个元素。
  • Lambda表达式:利用forEach方法结合Lambda表达式实现简洁的遍历操作。
  1. LinkedList的使用:
  • 作为队列使用时,通过addLast和removeFirst方法模拟入队和出队操作。
  • 作为栈使用时,通过push和pop方法实现压栈和出栈操作。
  1. Set家族集合的特点:
  • HashSet:基于哈希表实现,不保证元素的顺序。
  • LinkedHashSet:基于哈希表和链表实现,保持了元素的插入顺序。
  • TreeSet:基于红黑树实现,元素按照自然排序或自定义比较器排序。
  1. Map接口及其实现类:
  • HashMap:基于哈希表实现,不保证映射的顺序。
  • LinkedHashMap:基于哈希表和双向链表实现,保持了键值对的插入顺序。
  • TreeMap:基于红黑树实现,根据键进行排序。
  • Map的常用操作:添加、删除、查询、检查键或值是否存在、清空集合等。
  1. 自定义对象在集合中的排序:
  • 实现Comparable接口并重写compareTo方法来指定排序规则。
  • 使用Comparator接口创建比较器对象,并传递给集合构造器或相关方法以实现定制化排序。
  1. Map集合的遍历:
  • 通过keySet()方法获取所有键后逐一访问对应的值。
  • 利用entrySet()方法将Map转换为键值对的集合,从而可以同时访问键和值。
  • 采用forEach方法配合Lambda表达式提供一种更现代且简洁的遍历方式。

具体代码

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
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
package ADV_0;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class ADVZc1 {
public static void main(String[] args) {
// Collection集合的整体特点
System.out.println("======Collection集合的整体特点======");
// 1、List家族的集合:有序、可重复、有索引。
List<String> list = new ArrayList<>();
list.add("Java8");
list.add("Java21");
list.add("C");
list.add("C++");
System.out.println(list); // [Java, Java, C, C++] 顺序和添加顺序一致
String rs = list.get(0); //java8
System.out.println(rs);

// 2、Set家族的集合:无序、不可重复、无索引。
Set<String> set0 = new HashSet<>();
set0.add("鸿蒙");
set0.add("Java");
set0.add("Java17");
set0.add("C");
set0.add("C++");
System.out.println(set0);//无序


System.out.println("======Collection的遍历方式======");
// Collection的遍历方式一:迭代器遍历
// 得到这个集合的迭代器对象
Iterator<String> it = list.iterator();
System.out.println(it.next());
System.out.println(it.next());
System.out.println(it.next());
System.out.println(it.next());
// System.out.println(it.next()); // NoSuchElementException
// 使用一个while循环来遍历
while (it.hasNext()) {
String name = it.next();
System.out.println(name);// Java8 Java21 C C++
}

//Collection的遍历方式二:增强for
for (String name : list) {
System.out.println(name);
}
//例子1
String[] users = {"刘备", "关羽", "张飞", "诸葛亮"};
for (String user : users) {
System.out.println(user);
}

//Collection的遍历方式三:lambda
// 1. list.forEach(new Consumer<String>() {
// @Override
// public void accept(String s) {
// System.out.println(s);
// }
// });
// 2. list.forEach(s -> System.out.println(s));
list.forEach(System.out::println);

System.out.println("-------------------------------------------------");
System.out.println("======LinkedList======");
// LinkedList队列
LinkedList<String> queue = new LinkedList<>();
// 入队
queue.addLast("1");
queue.addLast("2");
queue.addLast("3");
queue.addLast("4");
System.out.println(queue); // [1, 2, 3, 4]

// 出队
System.out.println(queue.removeFirst());
System.out.println(queue.removeFirst());
System.out.println(queue);

// LinkedList栈
LinkedList<String> stack = new LinkedList<>();
// 压栈
stack.push("第1颗子弹");
stack.push("第2颗子弹");
stack.push("第3颗子弹");
stack.push("第4颗子弹");
System.out.println(stack); // [第4颗子弹, 第3颗子弹, 第2颗子弹, 第1颗子弹]
// 出栈
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack);

System.out.println("----------------------------------------------");
System.out.println("======Set======");
// Set家族集合的特点
// Set集合,特点:无序,不重复,无索引。
// Set<String> set = new HashSet<>(); // 一行经典代码 HashSet 无序,不重复,无索引。
Set<String> set = new LinkedHashSet<>(); // LinkedHashSet 有序,不重复,无索引。
set.add("鸿蒙0");
set.add("鸿蒙1");
set.add("java8");
set.add("java8");
set.add("电商");
set.add("电商");
set.add("新媒体");
set.add("大数据");
System.out.println(set);

//TreeSet集合:排序(默认一定要大小升序排序),不重复,无索引
//排序
Set<Double> set1 = new TreeSet<>();
set1.add(3.14);
set1.add(5.6);
set1.add(1.0);
set1.add(1.0);
set1.add(2.0);
System.out.println(set1);
System.out.println("==========================");
//打印哈希码值
//不同对象,内容一样返回的哈希值是一样的
String acd = "acd";
String abc = "abc";
System.out.println(acd.hashCode());
System.out.println(acd.hashCode());
System.out.println(abc.hashCode());
System.out.println(abc.hashCode());


//TreeSet集合对于自定义对象的排序
//TreeSet集合默认不能给自定义对象排序,不知道大小规则。
// 解决方案
// 1、对象类实现一个Comparable比较接口,重写compareTo方法,指定大小比较规则
// 2、public TreeSet(Comparator c)集合自带比较器Comparator对象,指定比较规则
Set<Teacher> teachers = new TreeSet<>(new Comparator<Teacher>() {
@Override
public int compare(Teacher o1, Teacher o2) {
// return o2.getAge() - o1.getAge(); //降序
// if(o1.getSalary() > o2.getSalary()){
// return 1;
// }else if(o1.getSalary() < o2.getSalary()){
// return -1;
// }
// return 0;
// return Double.compare(o1.getSalary(), o2.getSalary()); // 薪水升序
return Double.compare(o2.getSalary(), o1.getSalary()); // 薪水升序
}
}); // 排序,不重复,无索引

// 简化形式
// Set<Teacher> teachers = new TreeSet<>((o1, o2) -> Double.compare(o1.getSalary(), o2.getSalary())); // 排序,不重复,无索引
teachers.add(new Teacher("老陈", 20, 2222.2));
teachers.add(new Teacher("张三", 18, 3333.3));
teachers.add(new Teacher("隔壁老王", 22, 9999.9));
teachers.add(new Teacher("李云龙", 20, 6999.9));
System.out.println(teachers);

System.out.println("-------------------------------------------------");
System.out.println("======Map======");
// Map集合的体系特点(键值对)
// Map特点/HashMap特点:无序,不重复,无索引,键值对都可以是null, 值不做要求(可以重复)
// LinkedMap特点:有序,不重复,无索引,键值对都可以是null, 值不做要求(可以重复)
// TreeMap: 按照键可排序,不重复,无索引
Map<String,Integer> map = new HashMap<>(); // 一行经典代码
// Map<String,Integer> map = new LinkedHashMap<>();
map.put("孙悟空", 600);
map.put("女儿国王", 31);
map.put("嫦娥", 28) ;
map.put("铁扇公主", 38);
map.put("紫霞", 31);
map.put(null, null);
System.out.println(map); // {null=null, 嫦娥=28, 铁扇公主=38, 紫霞=31, 女儿国王=31}
// 常用方法
System.out.println(map.get("嫦娥")); // 根据键取值 28
System.out.println(map.get("嫦娥2")); // null
System.out.println(map.containsKey("嫦娥")); // 判断是否包含某个键 true
System.out.println(map.containsKey("嫦娥2")); // false
System.out.println(map.containsValue(28)); // 判断是否包含某个值 true
System.out.println(map.containsValue(28.0)); // false
System.out.println(map.remove("嫦娥")); // 根据键删除键值对,返回值
System.out.println(map);
// map.clear(); // 清空map
// System.out.println(map);
System.out.println(map.isEmpty()); // 判断是否为空
System.out.println(map.size()); // 获取键值对的个数 4
// 获取所有的键放到一个Set集合返回给我们
Set<String> keys = map.keySet();
for (String key : keys) {
System.out.println(key);
}
// 获取所有的值放到一个Collection集合返回给我们
Collection<Integer> values = map.values();
for (Integer value : values) {
System.out.println(value);
}

// 目标:TreeMap集合(原理和用法于TreeSet一样)
Map<Teacher, String> map0 = new TreeMap<>((o1, o2) -> Double.compare(o2.getSalary(), o1.getSalary())); // 按照键排序:升序
map0.put(new Teacher("老陈", 20, 2222.2), "java8");
map0.put(new Teacher("张三", 18, 3333.3),"如何犯法");
map0.put(new Teacher("隔壁老王", 22, 9999.9),"如何做邻居");
map0.put(new Teacher("李云龙", 20, 6999.9),"打鬼子百法");
System.out.println(map0);

System.out.println("======Map集合的遍历方式一:键找值======");
// 1、提起Map集合的全部键到一个Set集合中去
Set<String> keyss = map.keySet();
// 2、遍历Set集合,得到每一个键
for (String key : keyss) {
// 3、根据键去找值
Integer value = map.get(key);
System.out.println(key + "=" + value);
}

System.out.println("======Map集合的遍历方式二:键值对======");
// 1、把Map集合转换成Set集合,里面的元素类型都是键值对类型(Map.Entry<String, Integer>)
/**
* map = {嫦娥=28, 铁扇公主=38, 紫霞=31, 女儿国王=31}
* ↓
* map.entrySet()
* ↓
* Set<Map.Entry<String, Integer>> entries = [(嫦娥=28), (铁扇公主=38), (紫霞=31), (女儿国王=31)]
* entry
*/
Set<Map.Entry<String, Integer>> entries = map.entrySet();
// 2、遍历Set集合,得到每一个键值对类型元素
for (Map.Entry<String, Integer> entry : entries) {
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println(key + "=" + value);
}
System.out.println("======Map集合的遍历方式三:Lambda======");
// 1、直接调用Map集合的forEach方法完成遍历
// map.forEach(new BiConsumer<String, Integer>() {
// @Override
// public void accept(String key, Integer value) {
// System.out.println(key + "=" + value);
// }
// });
map.forEach((k,v) -> System.out.println(k + "=" + v));

System.out.println("---------------------------------------------");
System.out.println("======Stream流======");
List<String> list1 = new ArrayList<>();
list1.add("张大爷");
list1.add("周日哥");
list1.add("赵四");
list1.add("张叁");
list1.add("张三");
list1.add("张大山");
System.out.println("======传统方案======");
// 1、传统方案:找出姓张的人,名字为3个字的,存入到一个新集合中去
List<String> newList = new ArrayList<>();
for (String name : list1) {
if(name.startsWith("张") && name.length() == 3){
newList.add(name);
}
}
System.out.println(newList);
System.out.println("======Stream方案======");
// 2、使用Stream流解决
List<String> newList2 = list1.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).collect(Collectors.toList());
System.out.println(newList2);

System.out.println("======获取Stream流的方式======");
// 1、获取集合的Stream流:调用集合提供的stream()方法
Collection<String> list11 = new ArrayList<>();
Stream<String> s11 = list11.stream();

// 2、Map集合,怎么拿Stream流
Map<String, Integer> map1 = new HashMap<>();
// 获取键流
Stream<String> sk = map.keySet().stream();
// 获取值流
Stream<Integer> sv = map.values().stream();
// 获取键值对流
Stream<Map.Entry<String, Integer>> skv = map.entrySet().stream();

// 3、获取数组流
String[] names = {"张三", "张无忌", "张山", "张良", "张大山"};
Stream<String> sa = Arrays.stream(names);
System.out.println(sa.count()); // 5
Stream<String> saa = Stream.of(names);
Stream<String> saaa = Stream.of("张三", "张无忌", "张山", "张良", "张大山");

System.out.println("======Stream流常用方法======");
// 1、过滤方法
list1.stream().filter(s -> s.startsWith("张") && s.length() == 3).forEach(System.out::println);

// 2、排序方法
List<Double> scores = new ArrayList<>();
scores.add(88.6);
scores.add(66.6);
scores.add(66.6);
scores.add(77.6);
scores.add(77.6);
scores.add(99.6);
scores.stream().sorted().forEach(System.out::println); // 默认是升序。
System.out.println("===降序===");
scores.stream().sorted((s1, s2) -> Double.compare(s2, s1)).forEach(System.out::println); // 降序
System.out.println("===只要前2名===");
scores.stream().sorted((s1, s2) -> Double.compare(s2, s1)).limit(2).forEach(System.out::println); // 只要前2名
System.out.println("===跳过前2名===");
scores.stream().sorted((s1, s2) -> Double.compare(s2, s1)).skip(2).forEach(System.out::println); // 跳过前2名
System.out.println("===去重复===");
// 如果希望自定义对象能够去重复,重写对象的hashCode和equals方法,才可以去重复!
scores.stream().sorted((s1, s2) -> Double.compare(s2, s1)).distinct().forEach(System.out::println); // 去重复
System.out.println("===映射/加工方法===");
// 把流上原来的数据拿出来变成新数据又放到流上去。
scores.stream().map(s -> "加10分后:" + (s + 10)).forEach(System.out::println);

System.out.println("======合并流======");
Stream<String> s1 = Stream.of("张三", "张无忌", "张山", "张良", "张大山");
Stream<Integer> s2 = Stream.of(111, 22, 33, 44);
Stream<Object> s3 = Stream.concat(s1, s2);
System.out.println(s3.count());

System.out.println("-------流只能收集一次--------");
// 流只能收集一次
// 收集到集合或者数组中去
Stream<String> ss1 = list1.stream().filter(s -> s.startsWith("张"));

System.out.println("------收集到List集合------");
List<String> l1 = ss1.collect(Collectors.toList());
System.out.println(l1);
// Set<String> set2 = new HashSet<>();
// set2.addAll(l1);

System.out.println("------收集到Set集合------");
Stream<String> ss2 = list1.stream().filter(s -> s.startsWith("张"));
Set<String> se = ss2.collect(Collectors.toSet());
System.out.println(se);

System.out.println("----------收集到数组中----------");
Stream<String> ss3 = list1.stream().filter(s -> s.startsWith("张"));
Object[] array = ss3.toArray();
System.out.println("数组:" + Arrays.toString(array));

System.out.println("------------------收集到Map集合-------------------------");
// 收集到Map集合:键是老师名称,值是老师薪水
Map<String, Double> mapt = teachers.stream().collect(Collectors.toMap(Teacher::getName, Teacher::getSalary));
System.out.println(mapt);
}
}


//TreeSet集合对于自定义对象的排序
// teacher类
@Data
@NoArgsConstructor
@AllArgsConstructor
class Teacher implements Comparable<Teacher> {
private String name;
private int age;
private double salary;

@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", age=" + age +
", salary=" + salary +
'}' + "\n";
}
// t2.compareTo(t1)
// t2 == this 比较者
// t1 == o 被比较者
// 规定1:如果你认为左边大于右边 请返回正整数
// 规定2:如果你认为左边小于右边 请返回负整数
// 规定3:如果你认为左边等于右边 请返回0
// 默认升序
@Override
public int compareTo(Teacher o) {
// 按照年龄升序
// if(this.getAge() > o.getAge()) return 1;
// if(this.getAge() < o.getAge()) return -1;
// return 0;
return this.getAge() - o.getAge(); // 升序
// return o.getAge() - this.getAge(); // 降序
}
}