Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API对集合进行操作,就类似与使用SQL执行的数据库操作。也可以使用Stream API来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。 是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列 集合讲的是数据,流讲的是计算 PS: Stream自己不会存储元素 Stream不会改变源对象,相反,他们会返回一个持有结果的新Stream Stream操作是延迟执行的,这意味着他们会等到需要结果的时候才执行 下面的图可以比较直观的反映这一过程: 创建Stream 一个数据源(数组、集合等),获取一个流 中间操作 一个中间操作链,对数据源的数据进行处理 终止操作(终端操作) 一个终止操作,执行中间操作链,并产生结果 通过Collection系列集合提供的stream()或parallelStream() Java8中的Collection接口被扩展,提供了两个获取流的方法: default Stream stream():返回一个顺序流 default Stream parallelStream():返回一个并行流 示例代码: List list = new ArrayList<>(); Stream stream1 = list.stream(); Stream stream2 = list.parallelStream(); 由数组创建流 Java8的Arrays的静态方法stream()可以获取数据流 static Stream stream(T[] arrays):返回一个流 示例代码: Integer[] integers = new Integer[10]; Stream stream = Arrays.stream(integers); 由值创建流 通过Stream类中的静态方法of(),通过显示值创建一个流,可以接收任意数量的参数 public static Stream of(T ... values):返回一个流 示例代码: Stream stream = Stream.of(1, 2, 3); 创建无限流 使用静态方法Stream.iterate()和Stream.generate(),创建无限流 迭代:public static Stream iterate(final T seed, final UnaryOperator f) 生成:public static Stream generate(Supplier s) 示例代码: // 迭代 Stream stream1 = Stream.iterate(0, (x) -> x + 2); // 生成 Stream stream2 = Stream.generate(() -> Math.random()); 多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称之为“惰性求值”。 筛选与切片 filter:结合搜lambda,从流中排除元素 limit:截断流,使其元素不超过给定数量 skip(n):跳过元素,返回一个删除了前n个元素的流;若流中元素不足n个,则返回一个空流;与limit(n)互补 distinct:筛选,通过流所生成的元素的hashCode()和equals()去除重复元素 示例代码: public class TestStreamApi { private static List demoList = Arrays.asList( new Demo(1, "哈哈哈"), new Demo(2, "嘿嘿嘿嘿"), new Demo(3, "呵呵呵"), new Demo(4, "恩恩恩恩"), new Demo(5, "哼哼哼"), new Demo(6, "啧啧啧"), new Demo(5, "哼哼哼"), new Demo(8, "哼") ); public static void main(String[] args) { // 中间操作不会执行任何操作 Stream demoStream = demoList.stream() .filter((x) -> x.getRemark().length() == 3) .limit(4) .skip(1) .distinct(); // 终止操作一次性执行全部内容 // 内部迭代:迭代操作由Stream API完成 demoStream.forEach(System.out::println); } } 运行结果: 3-呵呵呵 5-哼哼哼 6-啧啧啧 注意:distinct筛选通过流所生成的元素的hashCode()和equals()去除重复元素,所以需要重写Demo的hashCode()和equals()方法。 映射 map:接收Lambda,将元素转换成其它形式或提取信息;接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素 flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有的流连接成一个流 示例代码: public class TestStreamApi { private static List demoList = Arrays.asList( new Demo(1, "哈哈哈"), new Demo(2, "嘿嘿嘿嘿") ); public static void main(String[] args) { demoList.stream() .map(Demo::getRemark) .flatMap(TestStreamApi :: filterCharacter) .forEach(System.out::println); } public static Stream filterCharacter(String str) { List list = new ArrayList<>(); for (Character c : str.toCharArray()) { list.add(c); } return list.stream(); } } 运行结果: 哈 哈 哈 嘿 嘿 嘿 嘿 排序 sorted():自然排序 sorted(Comparator c):定制排序 示例代码: public class TestStreamApi { private static List demoList = Arrays.asList( new Demo(5, "哈哈哈"), new Demo(2, "嘿嘿嘿嘿"), new Demo(3, "呵呵呵"), new Demo(2, "哼哼哼"), new Demo(5, "啧啧啧") ); public static void main(String[] args) { List list = Arrays.asList("aaa", "bbb", "ccc"); list.stream() .sorted() .forEach(System.out::println); System.out.println("----------"); demoList.stream() .sorted((x, y) -> { if (x.getNum().equals(y.getNum())) { return x.getRemark().compareTo(y.getRemark()); } else { return x.getNum().compareTo(y.getNum()); } }) .forEach(System.out::println); } } 运行结果: aaa bbb ccc ---------- 2-哼哼哼 2-嘿嘿嘿嘿 3-呵呵呵 5-哈哈哈 5-啧啧啧 查找与匹配 allMatch:检查是否匹配所有元素 anyMatch:检查是否匹配所有元素 noneMatch:检查是否没有匹配所有元素 findFirst:返回第一个元素 findAny:返回当前流中的任意元素 count:返回流中元素的总个数 max:返回流中的最大值 min:返回流中的最小值 示例代码: public class TestStreamApi2 { private static List demoList = Arrays.asList( new Demo("张三", 18, 6666.66, Demo.Status.BUSY), new Demo("李四", 38, 3333.33, Demo.Status.FREE), new Demo("王五", 28, 5555.55, Demo.Status.FREE), new Demo("赵六", 48, 7777.77, Demo.Status.BUSY), new Demo("王二麻子", 58, 8888.88, Demo.Status.VOCATION) ); public static void main(String[] args) { // 是不是所有的对象都处于BUSY状态 System.out.println(demoList.stream() .allMatch((d) -> d.getStatus().equals(Demo.Status.BUSY))); // 是否有对象处于BUSY状态 System.out.println(demoList.stream() .anyMatch((d) -> d.getStatus().equals(Demo.Status.BUSY))); // 是否没有对象处于BUSY状态 System.out.println(demoList.stream() .noneMatch((d) -> d.getStatus().equals(Demo.Status.BUSY))); // 获取工资最高的 Optional optionalDemo1 = demoList.stream() .sorted((x, y) -> -Double.compare(x.getSalary(), y.getSalary())) .findFirst(); System.out.println(optionalDemo1.get()); // 获取随机一个空闲的 Optional optionalDemo2 = demoList.stream() .filter((e) -> e.getStatus().equals(Demo.Status.FREE)) .findAny(); System.out.println(optionalDemo2.get()); // 总数 System.out.println(demoList.stream().count()); // 工资最高的 Optional optionalDemo3 = demoList.stream() .max((x, y) -> Double.compare(x.getSalary(), y.getSalary())); System.out.println(optionalDemo3.get()); // 最小的工资 Optional optionalDemo4 = demoList.stream() .map(Demo::getSalary) .max(Double::compare); System.out.println(optionalDemo4.get()); } } class Demo{ // 姓名 String name; // 年龄 Integer age; // 工资 Double salary; // 状态 Status status; public Demo() {} public Demo(String name, Integer age, Double salary, Status status) { this.name = name; this.age = age; this.salary = salary; this.status = status; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Double getSalary() { return salary; } public void setSalary(Double salary) { this.salary = salary; } public Status getStatus() { return status; } public void setStatus(Status status) { this.status = status; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Demo demo = (Demo) o; return name.equals(demo.name) && age.equals(demo.age) && salary.equals(demo.salary) && status == demo.status; } @Override public int hashCode() { return Objects.hash(name, age, salary, status); } @Override public String toString() { return "Demo{" + "name='" + name + '\'' + ", age=" + age + ", salary=" + salary + ", status=" + status + '}'; } public enum Status{ FREE, BUSY, VOCATION } } 运行结果: false true false Demo{name='王二麻子', age=58, salary=8888.88, status=VOCATION} Demo{name='李四', age=38, salary=3333.33, status=FREE} 5 Demo{name='王二麻子', age=58, salary=8888.88, status=VOCATION} 8888.88 归约 reduce(T identify, BinaryOperator) / reduce(BinaryOperator):可以将流中元素反复结合起来,得到一个值 示例代码: public class TestStreamApi3 { private static List demoList = Arrays.asList( new Demo("张三", 18, 6666.66, Demo.Status.BUSY), new Demo("李四", 38, 3333.33, Demo.Status.FREE), new Demo("王五", 28, 5555.55, Demo.Status.FREE), new Demo("赵六", 48, 7777.77, Demo.Status.BUSY), new Demo("王二麻子", 58, 8888.88, Demo.Status.VOCATION) ); public static void main(String[] args) { Optional optional = demoList.stream() .map(Demo::getSalary) .reduce(Double::sum); System.out.println(optional.get()); } } 运行结果: 32222.190000000002 收集 collect:将流转换为其他形式。接收一个Collection接口的实现,用于给Stream中元素做汇总的方法 Collectors接口中方法的实现决定了如何对流执行收集操作(如搜集到List、Set、Map)。 toList:把流中元素收集到List toSet:把流中元素收集到Set toCollection:把流中元素收集到创建的集合 counting:计算流中元素的个数 summingInt:对流中元素的整数属性求和 averagingInt:计算流中元素Integer属性的平均值 summarizingInt:收集流中Integer属性的统计值 jioning:连接流中的每个字符串 maxBy:根据比较器选择最大值 minBy:根据比较器选择最小值 reducing:从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而规约成单个值 collectingAndThen:包裹另一个收集器,对其结果转换函数 groupingBy:根据某个属性值对流分组,属性为K,结果为V partitioningBy:根据true、false进行分区 给定一个数组,方便测试: private static List demoList = Arrays.asList( new Demo("张三", 18, 6666.66, Demo.Status.BUSY), new Demo("李四", 38, 3333.33, Demo.Status.FREE), new Demo("王五", 28, 5555.55, Demo.Status.FREE), new Demo("赵六", 48, 7777.77, Demo.Status.BUSY), new Demo("王二麻子", 58, 8888.88, Demo.Status.VOCATION) ); toList 示例代码: // 收集 - toList System.out.println("---------------->toList"); List list = demoList.stream() .map(Demo::getName) .collect(Collectors.toList()); list.forEach(System.out::println); 运行结果: 张三 李四 王五 赵六 王二麻子 toSet 示例代码: // 收集 - toSet System.out.println("---------------->toSet"); Set set = demoList.stream() .map(Demo::getName) .collect(Collectors.toSet()); set.forEach(System.out::println); 运行结果: 李四 张三 王二麻子 王五 赵六 toCollection 示例代码: // 收集 - toCollection System.out.println("---------------->toCollection"); HashSet hashSet = demoList.stream() .map(Demo::getName) .collect(Collectors.toCollection(HashSet::new)); hashSet.forEach(System.out::println); 运行结果: 李四 张三 王二麻子 王五 赵六 counting 示例代码: // 收集 - counting 计算总数 System.out.println("---------------->counting"); System.out.println(demoList.stream() .collect(Collectors.counting())); 运行结果: 5 summingInt 示例代码: // 收集 - summingInt 计算年龄总和 System.out.println("---------------->summingInt"); System.out.println(demoList.stream() .collect(Collectors.summingInt(Demo::getAge))); 运行结果: 190 averagingInt 示例代码: // 收集 - averagingInt 平均年龄 System.out.println("---------------->averagingInt"); System.out.println(demoList.stream() .collect(Collectors.averagingInt(Demo::getAge))); 运行结果: 38.0 summarizingInt 示例代码: // 收集 - summarizingInt System.out.println("---------------->summarizingInt"); IntSummaryStatistics summaryStatistics = demoList.stream() .collect(Collectors.summarizingInt(Demo::getAge)); // 最大值 System.out.println(summaryStatistics.getMax()); // 平均值 System.out.println(summaryStatistics.getAverage()); // 总和 System.out.println(summaryStatistics.getSum()); 运行结果: 58 38.0 190 joining 示例代码: // 收集 - joining 连接姓名 System.out.println("---------------->joining"); String s = demoList.stream() .map(Demo::getName) .collect(Collectors.joining(",", "开始->", "<-结束")); System.out.println(s); 运行结果: 开始->张三,李四,王五,赵六,王二麻子<-结束 maxBy 示例代码: // 收集 - maxBy 获取工资最高的人 System.out.println("---------------->maxBy"); Optional max = demoList.stream() .collect(Collectors.maxBy((x, y) -> Double.compare(x.getSalary(), y.getSalary()))); System.out.println(max.get()); 运行结果: Demo{name='王二麻子', age=58, salary=8888.88, status=VOCATION} minBy 示例代码: // 收集 - minBy 获取最低的 System.out.println("---------------->minBy"); Optional min = demoList.stream() .map(Demo::getSalary) .collect(Collectors.minBy(Double::compare)); System.out.println(min.get()); 运行结果: 3333.33 groupingBy 示例代码: // 收集 - groupingBy 根据状态分组 System.out.println("---------------->groupingBy"); Map> group = demoList.stream() .collect(Collectors.groupingBy(Demo::getStatus)); System.out.println(group); // 多级分组 先按状态分组,在按年龄分组 Map>> group2 = demoList.stream() .collect(Collectors.groupingBy(Demo::getStatus, Collectors.groupingBy((x) -> { if (x.getAge() <= 30) { return "青年"; } else if (x.getAge() <= 45) { return "中年"; } else { return "老年"; } }))); System.out.println(group2); 运行结果: {VOCATION=[Demo{name='王二麻子', age=58, salary=8888.88, status=VOCATION}], FREE=[Demo{name='李四', age=38, salary=3333.33, status=FREE}, Demo{name='王五', age=28, salary=5555.55, status=FREE}], BUSY=[Demo{name='张三', age=18, salary=6666.66, status=BUSY}, Demo{name='赵六', age=48, salary=7777.77, status=BUSY}]} {VOCATION={老年=[Demo{name='王二麻子', age=58, salary=8888.88, status=VOCATION}]}, FREE={青年=[Demo{name='王五', age=28, salary=5555.55, status=FREE}], 中年=[Demo{name='李四', age=38, salary=3333.33, status=FREE}]}, BUSY={青年=[Demo{name='张