一、Stream流的前置知识:
- Stream流的基本介绍:Stream流是JDK8开始新增的一套API,在java.util包下,可用于操作集合或数组的数据。
- Stream流的优势:大量结合函数式编程风格,大部分用Lambda实现,具有功能强大、性能高效、代码简洁、可读性好的特点。
- 体验Stream流的使用:通过案例对比传统方式和Stream流方式实现从集合中筛选姓张且名字为三个字的元素存到新集合的需求,体现Stream流的简洁性。
- Stream流的使用步骤:第一步准备数据源(集合或数组),第二步获取数据源的Stream流,第三步用流的功能处理数据,第四步获取处理结果。
- 获取集合的Stream流:单列集合调用stream()方法获取流;Map集合可通过获取键流、值流或键值对流来操作,键流通过keySet().stream()获取,值流通过values().stream()获取,键值对流通过entrySet().stream()获取。
- 获取数组的Stream流:可以使用Arrays.stream(数组)或Stream.of(数组元素)来获取数组的Stream流。
二、Stream流的常用中间方法:
- 中间方法的概念及特点:中间方法是对流中的数据进行中间处理的方法,如过滤、排序、去重等。调完中间方法后会返回新的流,支持链式编程。
- 过滤方法filter:用于筛选符合条件的数据。例如筛选姓张且名字为三个字的人,可通过
filter(s -> s.startsWith("张") && s.length() == 3)
实现,代码简洁,一行即可完成之前需要多行代码的操作。 - 排序方法sorted:可对数据进行排序。对于实现了
Comparable
接口的类型(如Double
),使用sorted()
可默认升序排序;若要降序或自定义排序规则,可使用sorted((s1, s2) -> s2.compareTo(s1))
等方式。 - 限制和跳过方法limit/skip:
limit(n)
用于获取前n个数据,skip(n)
用于跳过前n个数据。例如降序排序后用limit(2)
获取前两名,用skip(2)
跳过前两名获取后面的数据。 - 去重方法distinct:用于去除流中的重复数据。对于基本类型或重写了
hashCode
和equals
方法的对象,可直接使用distinct()
去重。若为自定义对象,需重写这两个方法才能正确去重。 - 映射方法map:用于将流中的数据加工成新数据。例如将所有分数加10分,可通过
map(s -> "加分后" + (s + 10))
实现,加工后的新数据会放入新流中。 - 合并流方法concat:用于合并两个流为一个新流。合并时需注意泛型约束,合并后的流类型为两个流类型的共同父类(如合并字符串流和整数流,合并后的流为
Object
类型)。每次只能合并两个流。
三、Stream流的常用终结方法:
- 终结方法的概念及特点:是在流处理数据完毕后获取处理结果的方法,如遍历、统计或将数据收集到新集合中。调用后流无法再使用,而中间方法调用后会返回新流,可链式编程。
- 简单终结方法:
- forEach:用于遍历流中的元素,遍历后流不能再用,因为底层索引走到头。
- count:用于统计流中元素个数,返回的是个数,不是流,是终结方法。
- max/min:用于获取流中的最大值和最小值,会把结果放到Optional容器里,避免返回null导致空指针异常,使用时需给比较规则,通过get方法获取容器中的元素。
- 收集操作:流处理完数据后需恢复到集合或数组中,因为开发中一般用集合或数组接数据,流只是处理数据的手段。
- 收集到List:通过stream.collect(Collectors.toList())实现,返回ArrayList对象,可收集重复数据。
- 收集到Set:通过stream.collect(Collectors.toSet())实现,不能收集重复数据,流只能收集一次,否则会报错,若要收集到Set,可先收集到List再倒入Set。
- 收集到数组:通过stream.toArray()实现,收集到Object数组,数组可重复。
- 收集到Map:通过stream.collect(Collectors.toMap(键的提取函数, 值的提取函数))实现,需指定键和值的提取方式,Map集合无序,收集时会打乱顺序。
四、可变参数和Collections工具类:
- 可变参数的定义及作用:定义方法或构造器时,参数可用“数据类型+…”格式,好处是可灵活接收0个、1个、多个数据或数组,能替代数组传参,避免创建数组的麻烦。
- 可变参数的使用演示:以求和方法为例,演示了不传参、传1个参数、传多个参数和传数组的情况,说明其使用灵活。
- 可变参数的内部实现:可变参数本质是数组,可通过访问长度、索引遍历等数组操作来获取数据。
- 可变参数的注意事项:形参列表中只能有一个可变参数,且必须放在最后面,否则会报错。
- Collections工具类介绍:是操作集合的工具类,提供很多静态方法,可直接用类名调用。
- Collections工具类的常用方法:addAll方法可批量向单列集合添加数据;shuffle方法可打乱List集合顺序,因Set集合无序故不支持;sort方法可对List集合排序,可声明比较器自定义规则,与Stream流排序功能有重叠,体现Java API的灵活性。
五、综合实战-做牌-对牌排序:
- 游戏需求与分析:斗地主游戏需准备54张牌(点数3~2,四种花色,大小王各一张),发出51张,剩下3张作为底牌,需实现洗牌、发牌、对牌排序和看牌操作。
- 类的设计:需要定义牌类(包含点数、花色,并重写toString方法)和房间类(用于准备54张牌、启动房间等)。
- 牌的初始化:在房间类的实例代码块中初始化54张牌,先准备点数和花色数组,通过双重循环组合成52张普通牌,再单独添加大小王,将所有牌存入ArrayList集合。
- 洗牌:使用Collections工具类的shuffle方法对牌集合进行随机打乱。
- 发牌:定义三个玩家,用Map集合存储玩家及其牌集合,通过轮询(索引对3求余)的方式将51张牌发给三个玩家,剩余3张作为底牌。
- 底牌处理:通过sublist方法从牌集合中截取最后3张作为底牌,抢地主后可将底牌倒给地主玩家。
- 牌的排序:在牌类中添加大小属性,创建牌时为每张牌设置大小值(三为1,四为2,依此类推,小王13,大王14),使用Collections工具类的sort方法并传入比较器(根据牌的大小值降序排列)对玩家的牌进行排序。
- 看牌:遍历Map集合,展示每个玩家的牌。