Java Stream流
认识Stream流
- 是Jdk8开始新增的一套API(java.util.stream.*),可以用于操作集合或者数组的数据
- 优势:Stream流大量的结合了Lambda的语法风格来编程,功能强大、性能高效、代码简洁、可读性好
代码体验
package com.itheima.stream;import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;public class StreamDemo1 {public static void main(String[] args) {// 认识Stream流List<String> list = new ArrayList<>();list.add("张无忌");list.add("周芷若");list.add("赵敏");list.add("张强");list.add("张三丰");list.add("张三");list.add("张翠兰");System.out.println(list);List<String> newList = new ArrayList<>();for (String name : list) {if (name.startsWith("张") && name.length() == 2) {newList.add(name);}}System.out.println(newList);// 使用Stream流解决List<String> newList1 = list.stream().filter(name -> name.startsWith("张")).filter(name -> name.length() == 3).collect(Collectors.toList()); // filter 过滤System.out.println(newList1);}
}
Stream流的使用步骤
准备数据源(集合、数组、…)→ 过滤 → 排序 → 去重→ … →获取结果
- 1.获取Stream流,Stream流代表着一条流水线,并能与数据源建立连接;
- 2.调用流水线各种方法,对数据进行处理;
- 3.获取处理的数据,遍历、统计、收集到一个新的集合中返回结果。
获取Stream流
- 获取 **集合 ** 的Stream流
Collection 提供的如下方法 | 说明 |
---|---|
default Stream stream() | 获取当前集合对象的 Stream 流 |
- 获取 **数组 ** 的Stream流
Arrays 类提供的如下方法 | 说明 |
---|---|
public static Stream stream(T[] array) | 获取当前数组的 Stream 流 |
Stream 类提供的如下方法 | 说明 |
---|---|
public static Stream of(T… values) | 获取当前接收数据的 Stream 流 |
代码实例
package com.itheima.stream;import java.util.*;
import java.util.stream.Stream;public class StreamDemo2 {public static void main(String[] args) {// 1.获取集合的Stream流Collection<String> list = new ArrayList<>();Stream<String> s = list.stream();// 2.获取Map集合的Stream流Map<String, Integer> map = new HashMap<>();// map.stream(); // 不能直接调用stream// 获取键流Stream<String> keyStream = map.keySet().stream();// 获取值流Stream<Integer> valueStream = map.values().stream();// 获取键值对流Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();// 获取数组的Stream流String[] name = {"张无忌", "赵敏", "张强", "张三丰", "张翠山", "张小强"};Stream<String> stream = Arrays.stream(name); // 方式1Stream<String> stream1 = Stream.of("张无忌", "赵敏"); // 方式2 }
}
Stream流提供的常用方法
- 中间方法指的是调用完成后会返回新的Stream流,可以继续使用(支持链式编程)。
Stream 提供的常用中间方法 | 说明 |
---|---|
Stream filter(Predicate<? super T> predicate) | 用于对流中的数据进行过滤 |
Stream sorted() | 对元素进行升序排序 |
Stream sorted(Comparator<? super I> comparator) | 按照指定规则排序 |
Stream limit(long maxSize) | 获取前几个元素 |
Stream skip(long n) | 跳过前几个元素 |
Stream distinct() | 去除流中重复的元素 |
Stream map(Function<? super I,? extends R> mapper) | 对元素进行加工,并返回对应的新流 |
static Stream concat(Stream a, Stream b) | 合并 a 和 b 两个流为一个流 |
代码实例
package com.itheima.stream;import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;public class StreamDemo3 {public static void main(String[] args) {// 掌握Stream流常用的中间方法// 1.对流中的数据进行过滤List<String> list = new ArrayList<>();list.add("张三丰");list.add("张无忌");list.add("周芷若");list.add("赵敏");list.add("张强");list.add("张三");list.add("张翠兰");list.add("张晓强");System.out.println(list);System.out.println("--------------------------对流中的数据进行过滤--------------------------");list.stream().filter(name -> name.startsWith("张") && name.length() == 3).forEach(System.out::println);// 2.对元素进行升序排序System.out.println("--------------------------对元素进行升序排序--------------------------");List<Double> list1 = new ArrayList<>();// 无规律添加成绩,有小数点list1.add(99.5);list1.add(88.9);list1.add(88.9);list1.add(76.9);list1.add(98.7);list1.add(65.4);list1.add(87.2);list1.stream().sorted((o1, o2) -> Double.compare(o1, o2)).forEach(System.out::println); // 升序排序// 3.对元素进行降序排序System.out.println("--------------------------对元素进行降序排序--------------------------");list1.stream().sorted((o1, o2) -> Double.compare(o2, o1)).forEach(System.out::println); // 降序排序// 4.获取前几个元素System.out.println("--------------------------获取前几个元素--------------------------");list.stream().limit(3).forEach(System.out::println);// 5.跳过前几个元素System.out.println("--------------------------跳过前几个元素--------------------------");list1.stream().skip(2).forEach(s -> System.out.println(s));// 6.去重System.out.println("--------------------------去重--------------------------");list1.stream().distinct().forEach(System.out::println);// 7.映射/加工方法,返回对应的新Stream流System.out.println("--------------------------映射/加工方法,返回对应的新Stream流--------------------------");// 每个学生加5分,加分超过100分的,则变为100分list1.stream().map(s -> (s + 5) > 100 ? 100 : s + 5).forEach(System.out::println);// 8.合并流System.out.println("--------------------------合并流--------------------------");Stream<Object> concat = Stream.concat(list.stream(), list1.stream());concat.forEach(System.out::println);}
}
终结方法、收集Stream流
Stream流的终结方法
- 终结方法指的是调用完成后,不会返回新Stream了,没法继续使用流了。
Stream 提供的常用终结方法 | 说明 |
---|---|
void forEach(Consumer action) | 对此流运算后的元素执行遍历 |
long count() | 统计此流运算后的元素个数 |
Optional max(Comparator<? super I> comparator) | 获取此流运算后的最大值元素 |
Optional min(Comparator<? super I> comparator) | 获取此流运算后的最小值元素 |
收集Stream流
- 收集Stream流:就是把Stream流操作后的结果转回到集合或者数组中去返回
- 集合/数组:才是开发中的目的
- Stream流:方便操作集合/数组的手段
Stream 提供的常用终结方法 | 说明 |
---|---|
R collect(Collector collector) | 把流处理后的结果收集到一个指定的集合中去 |
Object[] toArray() | 把流处理后的结果收集到一个数组中去 |
Collectors 工具类提供的具体收集方式 | 说明 |
---|---|
public static Collector toList() | 把元素收集到 List 集合中 |
public static Collector toSet() | 把元素收集到 Set 集合中 |
public static Collector toMap(Function keyMapper , Function valueMapper) | 把元素收集到 Map 集合中 |
代码实例
package com.itheima.stream;import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;import static java.util.Comparator.comparingDouble;public class StreamDemo4 {public static void main(String[] args) {// Stream流的终结方法List<Teacher> teacher = new ArrayList<>();teacher.add(new Teacher("张三丰", 50, 51000));teacher.add(new Teacher("张无忌", 20, 20000));teacher.add(new Teacher("周芷若", 18, 18000));teacher.add(new Teacher("赵敏", 16, 16000));teacher.add(new Teacher("金毛狮王", 60, 33000));System.out.println(teacher);// Stream的终结方法// 1.forEachSystem.out.println("------------------------------forEach终结方法------------------------------");teacher.stream().filter(t -> t.getAge() > 30 && t.getSalary() > 30000).forEach(System.out::println);// 2.countSystem.out.println("------------------------------count终结方法------------------------------");System.out.println(teacher.stream().filter(t -> t.getAge() > 30 && t.getSalary() > 30000).count());// 3.maxSystem.out.println("------------------------------max终结方法------------------------------");Optional<Teacher> max = teacher.stream().max((t1, t2) -> Double.compare(t1.getSalary(), t2.getSalary()));// 简化代码Optional<Teacher> max1 = teacher.stream().max(comparingDouble(Teacher::getSalary));Teacher maxTeacher = max.get();System.out.println(maxTeacher);// 4.minSystem.out.println("------------------------------min终结方法------------------------------");Optional<Teacher> min = teacher.stream().min((t1, t2) -> Double.compare(t1.getSalary(), t2.getSalary()));Teacher minTeacher = min.get();System.out.println(minTeacher);// Stream的收集方法,Collectors 工具类提供的具体收集方式System.out.println("------------------------------collectors收集方法------------------------------");List<String> name = new ArrayList<>();name.add("张三丰");name.add("张无忌");name.add("张无忌");name.add("周芷若");name.add("赵敏");name.add("张强");name.add("张三");name.add("张翠兰");name.add("张晓强");// 收集到List集合System.out.println("--------------------------收集到List集合--------------------------");Stream<String> s1 = name.stream().filter(n -> n.startsWith("张") && n.length() == 3);List<String> s1List = s1.collect(Collectors.toList());System.out.println(s1List);// 收集到Set集合System.out.println("--------------------------收集到Set集合--------------------------");HashSet<Object> s1Set = new HashSet<>();s1Set.addAll(s1List);System.out.println(s1Set);// 收集到Map集合System.out.println("--------------------------收集到Map集合--------------------------");Stream<Teacher> s2 = teacher.stream().filter(t -> t.getAge() > 15);Map<String, Double> map = s2.collect(Collectors.toMap(Teacher::getName, Teacher::getSalary));System.out.println(map);// 收集到数组System.out.println("--------------------------收集到数组--------------------------");Stream<String> s3 = name.stream().filter(n -> n.startsWith("张") && n.length() == 3);Object[] array = s3.toArray();System.out.println(Arrays.toString(array));}
}
方法中的可变参数(拓展)
- 就是一种特殊形参,定义在方法、构造器的形参列表里,格式是:数据类型…参数名称;
- 可变参数的特点和好处
- 特点:可以不传数据给它;可以传一个或者同时传多个数据给它;也可以传一个数组给它。
- 好处:常常用来灵活的接收数据。
- 可变参数的注意事项:
- 可变参数在方法内部就是一个数组
- 一个形参列表中可变参数只能有一个
- 可变参数必须放在形参列表的最后面
Collections工具类
- Collections ——一个用来操作集合的工具类
- Collections提供的常用静态方法
方法名称 | 说明 |
---|---|
public static boolean addAll(Collection<? super T> c, T… elements) | 给集合批量添加元素 |
public static void shuffle(List<?> list) | 打乱 List 集合中的元素顺序 |
public static void sort(List list) | 对 List 集合中的元素进行升序排序 |
public static void sort(List list, Comparator<? super T> c) | 对 List 集合中元素,按照比较器对象指定的规则进行排序 |
案例——斗地主游戏设计
- 业务需求:
- 总共有54张牌
- 点数:“3” “4” “5” “6” “7” “8” “9” “10” “J” “Q” “K” “A” “2”
- 花色:4种花色
- 大小王
- 斗地主:发出51张牌,剩下3张做为底牌
- 分析实现
- 在启动游戏房间的时候,应该提前准备好54张牌接着,需要完成洗牌、发牌、对牌排序、看牌
代码实例
启动游戏
package com.itheima.stream;public class Game {public static void main(String[] args) {// 开发斗地主游戏// 每张牌都以一个对象,定义Card类// 每个游戏房间都是一个对象,定义一个GameRoom类new GameRoom().start();}
}
创建一个Card类,用于封装Card对象的特征
package com.itheima.stream;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@AllArgsConstructor
@NoArgsConstructorpublic class Card {// 定义牌的号码size和牌的花色private String size;private String color;private int num;@Overridepublic String toString() {return size + color;}
}
创建一个游戏Room
- 完成牌的初始化,洗牌,发牌,留出底牌,看牌,给任意玩家底牌,给牌排序等功能
package com.itheima.stream;import java.util.*;public class GameRoom {// 1.准备牌,定义一个集合,存储54张牌private List<Card> allCards = new ArrayList<>();//2.初始化54张牌,用实例代码块(示例代码块和对象一起初始化){String[] sizes = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};String[] colors = {"♥", "♦", "♣", "♠"};// 两个循环进行牌的匹配int num = 0;for (String size : sizes) {num++;for (String color : colors) {// 创建牌对象,并添加到集合中Card card = new Card(size, color, num);// 添加到allCards集合中allCards.add(card);}}// 创建大小王并添加到集合中去Collections.addAll(allCards, new Card("", "🃏", ++num), new Card("", "👲", ++num));// 查看新牌System.out.println("原始牌:" + allCards);}public void start() {// 3.洗牌,洗牌就是打乱集合中的顺序Collections.shuffle(allCards);System.out.println("洗牌后:" + allCards);// 4.发牌,定义三个玩家player:老王、老李、老朱,三个玩家对应着三个集合,因此使用Map集合,玩家是键,牌的集合是值Map<String, List<Card>> players = new HashMap<>();List<Card> lw = new ArrayList<>();players.put("老王", lw);List<Card> ll = new ArrayList<>();players.put("老李", ll);List<Card> lz = new ArrayList<>();players.put("老朱", lz);for (int i = 0; i < allCards.size() - 3; i++) {if (i % 3 == 0) {lw.add(allCards.get(i));}else if(i % 3 == 1) {ll.add(allCards.get(i));}else {lz.add(allCards.get(i));}}// 5.看牌,遍历Map集合for (Map.Entry<String, List<Card>> entry : players.entrySet()){// 获取玩家姓名String key = entry.getKey();// 获取牌的集合List<Card> value = entry.getValue();System.out.println(key + "的牌为:" + value);}// 底牌// subList(int fromIndex, int toIndex) 是 List 接口提供的一个方法,用于截取列表中的一部分元素,返回一个新的子列表List<Card> lastCard = allCards.subList(allCards.size() - 3, allCards.size());System.out.println("底牌:" + lastCard);// 6.随机选一个玩家将底牌交给玩家String key = players.keySet().toArray()[new Random().nextInt(players.size())].toString();players.get(key).addAll(lastCard);// 7.给牌排序(增加一个排序属性)sortCard(lw);System.out.println("老王的牌:" + lw);sortCard(ll);System.out.println("老李的牌:" + ll);sortCard(lz);System.out.println("老朱的牌:" + lz);}private void sortCard(List<Card> cards) {// 牌排序Collections.sort(cards, new Comparator<Card>() {@Overridepublic int compare(Card o1, Card o2) {// 牌大小排序return o2.getNum() - o1.getNum();}});}}