Day22-集和框架二
集和进阶(二)
Collection的其他相关知识
可变参数
就是一种特殊形参,定义在方法、构造器的形参列表里,格式是:数据类型…参数名称。
特点:可以不传数据给它;可以传一个或者同时传多个数据给它;也可以传一个数组给它。
好处:常常用来灵活的接收数据。
注意事项
- 可变参数在方法内部就是一个 数组
- 一个形参列表中,只能有一个可变参数。
- 可变参数必须放在形参列表的最后面。
Collections类
- 是一个用来操作集和的工具类
常用静态方法

实战:斗地主发牌
- 需求分析:
- 业务:总共有54张牌。点数分别要组合4种花色,大小王各一张。
- 点数:3、4、5、6、7、8、9、10、J、Q、K、A、2
- 花色:♦、♣、♥、♠
- 大小王: 小王👿 大王😈
- 斗地主:发出51张牌,剩下3张作为底牌。
Map集合
概述
- Collection:单列集和,每个元素包含一个值。
- Map:双列集和,每个元素包含一个键值对。
- Map集合称为双列集合,格式:{key1=value1,key2=value2,key3=value3,…},一次需要存一对数据做为一个元素。
- Map集合的每个元素“key=value”称为一个键值对/键值对对象/一个Entry对象,Map集合也被叫做“键值对集合”。
- Map集合的所有键是不允许重复的,但值可以重复,键和值是一一对应的,每一个键只能找到自己对应的值。
- 应用场景:淘宝购物车
- {商品1 = 华为手机, 商品2 = 华为手机, 商品3 = 苹果手机}
- 需要存储一一对应的数据时,就可以考虑使用Map集合来做。
Map集合体系

Map集合特点
- 注意:Map系列集合的特点都是由键决定的,值只是一个附属品,值是不做要求的。
- 细分
- HashMap(由键决定特点):无序、不重复、无索引。(用的最多)
- LinkedHashMap(由键决定特点):有序、不重复、无索引。
- TreeMap(由键决定特点):按照大小默认升序排序、不重复、无索引。
- 创建一个HashMap
1 | Map<String, Integer> map = new HashMap; //一行经典代码 |
- 相同的键,后存入的值会覆盖之前的值。
Map集合常用方法

Map集合遍历方式
键找值
先获取Map集合全部的键,再通过遍历键来找值。

键值对
把“键值对”看成一个整体进行遍历(难度较大)。

1
Set<Map.Entry<KeyType, ValueType>> entries = map.entryset();
该方法会返回一个Set集合,元素类型为将键值对封装成的Entry对象,之后遍历该Set集合即可。
Lambda
JDK1.8开始之后的新技术(非常的简单)。

1
2
3
4
5
6
7
8
9
10
11
12//Lambda表达式
map forEach(k, v) -> {
System.out.println(k + "-->" + v)
}
//完整表达式:BiConsumer接口匿名内部类 重写其中的accept方法
map forEach(new BiConsumer<KeyType, ValueType>){
public void accept(KeyType k, ValueType v){
System.out.println(k + "-->" + v)
} //accept方法实际上也是通过第二种遍历方法 即把键值对封装成Entry对象来实现遍历的
}
HashMap
特点:无序、不重复、无索引。(用的最多)
底层原理:
HashMap跟HashSet的底层原理是一模一样的,都是基于哈希表实现的。
实际上原来学的Set系列集合的底层就是基于Map实现的,只是Set集合中的元素只要键数据,不要值数据。
1
2
3public HashSet(){
map = new HashMap<>();
}
HashMap是基于哈希表实现的(以键为基准):
- HashMap集合是一种增删改查数据,性能都较好的集合。
- 但是它是无序,不能重复,没有索引支持的(由键决定特点)。
- HashMap的键依赖hashCode方法和equals方法保证键的唯一。
- 如果键存储的是自定义类型的对象,可以通过重写hashCode和equals方法,这样可以保证多个对象内容一样时,HashMap集合就能认为是重复的。
LinkedHashMap
- 特点:有序、不重复、无索引。
- 实际上原来学习的LinkedHashset集合的底层原理就是LinkedHashMap,只是Set集合中的元素只要键数据,不要值数据。
- LinkedHashMap是基于哈希表实现的(以键为基准),只是每个键值对元素又额外的多了一个双链表的机制记录元素顺序(保证有序)。
TreeMap
- 特点:不重复、无索引、可排序。(按照键的大小默认升序排序,只能对键排序)
- TreeMap跟TreeSet集合的底层原理是一样的,都是基于红黑树实现的排序。
- TreeMap集合同样也支持两种方式来指定排序规则:
- 让类实现Comparable接口,重写比较规则。
- TreeMap集合有一个有参数构造器,支持创建Comparator比较器对象,以便用来指定比较规则。(匿名内部类->Lambda)
补充知识:集合的嵌套
Map集合案例-省和市
需求:要求在程序中记住如下省份和其对应的城市信息,记录成功后,要求可以查询出湖北省的城市信息。
江苏省 = 南京市,扬州市,苏州市,无锡市,常州市
湖北省 = 武汉市,孝感市,十堰市,宜昌市,鄂州市
河北省 = 石家庄市,唐山市,邢台市,保定市,张家口市
分析:定义一个Map集合,键用表示省份名称,值表示城市名称,注意:城市会有多个。
根据“湖北省”这个键获取对应的值展示即可。
1
2
3
4
5
6
7
8
9
10
11
12//创建一个map集合 用于存放各省信息 键-省(String) ---> 值-市的集合(List<String>)
Map<String, List<String>> map = new HashMap<>();
//创建一个ArrayList 用于表示各省 存放市的信息
List<String> cityName1 = new ArrayList<>();
Collentions.addAll(cityName1, "xx市", "xx市", ……, "xx市");
map.put("xx省", cityName1);
//……以此类推
//按照省名读取信息
List<String> cities = map.get("xx省");
//……其他操作
Stream流
Stream概述
- 也叫Stream流,是Jdk8开始新增的一套API(java.util.stream.*),可以用于操作集合或者数组的数据
- 优势:Stream流大量的结合了Lambda的语法风格来编程,提供了一种更加强大,更加简单的方式操
作集合或者数组中的数据,代码更简洁,可读性更好。

案例
需求:把集合中所有以“张”开头,且是3个字的元素存储到一个新的集合。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15List<String> names = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张强");
list.add("张三丰");
//Stream流
List<String> newList = names.stream()
.filters(s -> s.startsWith("张") && s.length()==3)
.collect(Collectors.toList());
//或者把条件拆开
List<String> newList = names.stream()
.filters(s -> s.startsWith("张")).(a -> a.length()==3)
.collect(Collectors.toList());
Stream流的使用步骤

Stream代表一条流水线,对数据源进行加工处理后获取结果。
- 获取Stream流;
- 调用Stream流各种方法对数据进行处理、运算;
- 获取处理结果,遍历、统计、收集到一个新集合中返回。
Stream的常用方法
获取Stream流
- 获取集合的Stream流


Stream流常见的中间方法
- 中间方法指的是调用完成后会返回新的Stream流,可以继续使用(支持链式编程)。

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
49List<Double> scores = new ArrayList<>();
Collections.addAll(scores, 88.5, 100.0, 60.0, 99.0, 9.5, 99.6, 25.0);
// 需求1:找出成绩大于等于60分的数据,并升序后,再输出。
scores.stream().filter(s -> s >= 60).sorted().forEach(s -> System.out.println(s)):
/*-------------------------------------------------------------------------*/
List<Student> students =new ArrayList<>();
Student s1 =new Student(name:"蜘蛛精", age: 26, height:172.5);
Student s2 =new Student(name:"蜘蛛精", age:26, height:172.5);
Student s3 = new Student(name:"紫霞", age:23, height:167.6);
Student s4 = new Student(name:"白晶晶", age:25, height:169.0);
Student s5 = new Student(name:"牛魔王", age: 35, height:183.3);
Student s6 = new Student(name:"牛夫人", age:34, height:168.5);
Collections.addAll(students, s1, s2, s3, s4, s5, s6);
// 需求2:找出年龄大于等于23,且年龄小于等于30岁的学生,并按照年龄降序输出.
students.stream().filter(s -> s.getAge() >= 23 && s.getAge() <= 30)
.sorted((o1, o2) -> o2.getAge() - o1.getAge()) //Lambda
.forEach(System.out::println); //静态方法引用 遍历后打印
// 需求3:取出身高最高的前3名学生,并输出。
students.stream().sorted((o1,o2)-> Double.compare(o2.getHeight(), o1.getHeight()))
.limit(3) //取前三个
.forEach(System.out::println);
// 需求4:取出身高倒数的2名学生,并输出。
students.stream().sorted((o1,02)-> Double.compare(o2.getHeight(),o1.getHeight()))
.skip(students.size()-2) //只取最后两个(跳过前边四个)
.forEach(System.out::println);
// 需求5a:找出身高超过168的学生叫什么名字,要求去除重复的名字,再输出。
students.stream().filter(s ->s.getHeight() > 168)
.map(s -> s.getName()) //映射(把对象加工成名字).map(Student::getName())使用特殊类型方法引用
.distinct() //去除重复项
.forEach(System.out::println);
// 需求5b:找出身高超过168的学生,要求去除重复的对象,再输出。
students.stream().filter(s ->s.getHeight() > 168).distinct() //去除重复项
.forEach(System.out::println);
//若直接输出 发现distinct无法去除内容相同的重复对象
//自定义类型的对象 如果希望内容一样就被认定为重复 需要重写hashCode和equals方法
//需求6:合并Stream流
Stream<String> st1 = Stream.of("A", "B");
Stream<String> st2 = Stream.of("C", "D", "E");
Stream<String> allSt = Stream.concat(st1, st2);
allSt.forEach(System.out::println); //输出 A B C D EStream流常见的终结方法
- 终结方法指的是调用完成后,不会返回新Stream了,没法继续使用流了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18List<Student> students =new ArrayList<>();
Student s1 =new Student(name:"蜘蛛精", age: 26, height:172.5);
Student s2 =new Student(name:"蜘蛛精", age:26, height:172.5);
Student s3 = new Student(name:"紫霞", age:23, height:167.6);
Student s4 = new Student(name:"白晶晶", age:25, height:169.0);
Student s5 = new Student(name:"牛魔王", age: 35, height:183.3);
Student s6 = new Student(name:"牛夫人", age:34, height:168.5);
Collections.addAll(students, s1, s2, s3, s4, s5, s6);
//需求1:计算出身高超过168的人数。
long size = students.stream().filter(s -> s.getHeight() > 168).count(); //返回一个long
//需求2:找出身高最高的学生对象。
Student talleat = students.stream().max((o1, o2) -> Double.compare(o1.getHeight(), o2.getHeight())).get;
//比较Double类型的数字不能直接用减法 要用Double.compare 最后要用.get获取
//需求3:找出身高最矮的学生对象。
Student shortest = students.stream().min((o1, o2) -> Double.compare(o1.getHeight(), o2.getHeight())).get;- 收集Stream流:就是把Stream流操作后的结果转回到集合或者数组中去返回。
- Stream流:方便操作集合/数组的手段,集合/数组 才是开发中的目的。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16//!!!流只能收集一次!!!收集后这次流就关闭了!!!
//需求4:请找出身高超过170的学生对象,并放到一个新集合中去返回。
List<Student> studentsList = students.stream().filter(a -> a.hetHeight() > 170).collect(Collectors.toList());
Set<Student> studentsSet = students.stream().filter(a -> a.hetHeight() > 170).collect(Collectors.toSet()); //去除重复对象
//需求5:请找出身高超过170的学生对象,并把学生对象的名字和身高,存放到一个Map集合中去返回。
Map<String, Double> studentsMap = students.stream().filter(a -> a.hetHeight() > 170)
.distinct() //去除重复 否则有重复会报错
.collect(Collectors.toMap(a -> a.getName(), a -> a.getHeight()));
//需求6a:请找出身高超过170的学生对象,并把学生对象的身高,存放到一个数组中去返回。
Object[] arr = student.stream().filter(a -> a.hetHeight() > 170).toArray();
//需求6b:请找出身高超过170的学生对象,并把学生对象,存放到一个数组中去返回。
Student[] arr = student.stream().filter(a -> a.hetHeight() > 170).toArray(stu -> new Student[stu]);
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 宫本贩剑的博客!