在 Java 8 中,新增了 Stream
这个重要的抽象概念,结合同版本出现的 Lambda
表达式,Stream
通过其 API 提供了一系列高效、友好的处理集合数据的操作方式。
从字面上理解,Stream
就是把集合中将要处理的元素集合看作一个流,通过使用其 API 对流中的元素进行各种操作,如过滤、映射、聚合等。它具有以下几个特性:
Stream
的操作方式主要分为两大类,根据其执行特点还能继续向下细分,具体划分如下:
Stream
定义在 java.util.stream
包中,作为一个重要接口概念,在各种集合类以及数组类中都提供了初始化方法。Stream
自身也提供了一系列创建方法。
// 1.通过集合类的stream()或者parallelStream()方法
List<String> list = Arrays.asList("one", "two", "three");
Stream<String> stream = list.stream();
Stream<String> parallelStream = list.parallelStream();
// 2.通过数组的stream()方法
String array = {1,2,3,4,5};
IntStream stream = Arrays.stream(array);
// 3.通过Stream的静态方法
Stream<Character> charStream = Stream.of('a','b','c');
Stream<Integer> intStream = Stream.iterate(0, (x) -> x + 1).limit(5);
Stream<Double> doubleStream = Stream.generate(Math::random).limit(5);
stream是顺序流,而parallelStream是并行流,内部以多线程运行的方式对流进行操作,使用并行流时必须确保数据的处理对顺序是没有要求的。
在其他的场合,还有一些常见的使用 Stream
的方式,比如:
// 通过BufferedReader.lines()方法将每行的内容转换成流
BufferedReader reader = new BufferedReader(new FileReader("C:\\file.txt"));
Stream<String> lineStream = reader.lines();
// 通过Pattern.splitAsStream()方法将字符串转换成流
Pattern pattern = Pattern.compile(",");
Stream<String> stringStream = pattern.splitAsStream("a,b,c,d,e");
我们这里按照 Stream
操作的分类分别提供示例和进行讲解。
Stream<Integer> stream = Stream.of(3,5,132,6,856,9,132,2,53);
Stream<Integer> newStream = stream.filter(s -> s > 10) // 132, 856, 132, 53
.distinct(); // 132, 856, 53
Stream<Integer> stream = Stream.of(3,5,132,6,856,9,132,2,53);
Stream<Integer> newStream = stream.skip(4) // 9, 132, 2, 53
.limit(2); // 9, 132
List<String> list = Stream.of("a,b,c,d", "1,2,3,4");
// 去除每个字符串里面的逗号
Stream<String> s1 = list.stream().map(s -> s.replaceAll(",", ""));
// 将每个字符串按照逗号分割后转换成一个流,并将所有流组合起来
Stream<String> s2 = list.stream().flatMap(s -> {
String[] split = s.split(",");
Stream<String> newStream = Arrays.stream(split);
return newStream;
});
User u1 = new User("John", "[email protected]");
User u2 = new User("Jenny", "[email protected]");
List<User> list = Arrays.asList(u1, u2);
// 修改每个用户的邮箱信息为指定值,内置修改,无返回值
list.stream().peek(u -> u.setEmail("[email protected]"));
User u1 = new User("John", "[email protected]");
User u2 = new User("Jenny", "[email protected]");
User u3 = new User("Zed", "[email protected]");
// 自然排序,按照名字字母升序排列
list.stream().sorted();
// 自定义排序,按照名字字母倒序排列,名字相同时按照邮箱长度升序排序
list.stream().sorted((e1, e2) -> {
if (e1.getName().equals(e2.getName())) {
return e1.getEmail.length() - e2.getEmail.length();
} else {
return e2.getName.compareTo(e1.getName());
}
})
List<Integer> list = Arrays.asList(1,2,3,4,5,6);
boolean allMatch = list.stream().allMatch(e -> e > 2); // false
boolean noneMatch = list.stream().noneMatch(e -> e > 10); // true
boolean anyMatch = list.stream().anyMatch(e -> e > 5); // true
Integer findFirst = list.stream().findFirst().get(); // 1
Integer findAny = list.stream().findAny().get(); // 4
在这里的 findFirst
和 findAny
方法返回的结果都是 Optional
对象
具体说明可以去看看这篇文章:Java 8 Optional使用介绍
List<Integer> list = Arrays.asList(1,2,3,4,5,6);
long count = list.stream().count(); // 6
Integer max = list.stream().max(Integer::compareTo).get(); //6
在收集中常常使用到 Collectors 工具库,通过其静态方法返回一个 Collector 实例。具体应用方式如下:
// 构造对象列表,构建函数入参分别为name,gender, email,age
User u1 = new User("John", "m", "[email protected]", 14);
User u2 = new User("Jenny", "f", "[email protected]", 15);
User u3 = new User("Zed", "m", "[email protected]", 27);
List<User> users = Arrays.asList(u1, u2, u3);
// 提取元素转换成list
List<Integer> ageList = users.stream().map(User::getAge).collect(Collectors.toList());
// 提取元素转换成set
Set<Integer> ageSet = users.stream().map(User::getAge).collect(Collectors.toSet());
// 提取元素转换成map(这里key不能重复)
Map<String, Integer> userMap = users.stream().collect(Collectors.toMap(User::getName, User::getAge));
// 提取元素转换成map(这里相同的key允许value新值覆盖旧值;若是(m, m0) -> m则为保留原来的值)
Map<String, Integer> userMap = users.stream().collect(Collectors.toMap(User::getName, User::getAge, (m, m0) -> m0));
// 对象去重
List<User> distinctUserList = users.stream().collect(Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(User::getName))),
ArrayList::new));
// 字符串分隔符连接
String joinName = list.stream().map(Student::getName).collect(Collectors.joining(",", "(", ")"));
除了 stream( ) 自身的聚合方法,使用 Collector 实例同样可以进行聚合运算。
// 1.学生总数
Long count = list.stream().collect(Collectors.counting());
// 2.最大年龄 (最小的minBy同理)
Integer maxAge = list.stream().map(Student::getAge).collect(Collectors.maxBy(Integer::compare)).get();
// 3.所有人的年龄
Integer sumAge = list.stream().collect(Collectors.summingInt(Student::getAge));
// 4.平均年龄
Double averageAge = list.stream().collect(Collectors.averagingDouble(Student::getAge));
//分组
Map<Integer, List<Student>> ageMap = list.stream().collect(Collectors.groupingBy(Student::getAge));
//多重分组,先根据性别分再根据年龄分
Map<Integer, Map<Integer, List<Student>>> typeAgeMap = list.stream().collect(Collectors.groupingBy(Student::getGender, Collectors.groupingBy(Student::getAge)));
//分区
//分成两部分,一部分大于10岁,一部分小于等于10岁
Map<Boolean, List<Student>> partMap = list.stream().collect(Collectors.partitioningBy(v -> v.getAge() > 10));
List<Integer> list = Arrays.asList(1, 3, 2, 8, 11, 4);
Optional<Integer> sum = list.stream().reduce((x, y) -> x + y);
Optional<Integer> sum2 = list.stream().reduce(Integer::sum);
Integer sum3 = list.stream().reduce(0, Integer::sum);
Optional<Integer> product = list.stream().reduce((x, y) -> x * y);
Optional<Integer> max = list.stream().reduce((x, y) -> x > y ? x : y);
Integer max2 = list.stream().reduce(1, Integer::max);
Integer allAge = list.stream().map(Student::getAge).collect(Collectors.reducing(Integer::sum)).get();
通过上面的例子可以看出,Collectors 工具类提供的 reducing
方法和 Stream 本身的 reduce
方法都实现了归约的操作,两者的主要差别在于,前者增加了对自定义归约方法的支持。