「JDK8」stream api流式编程

自带常用的函数式接口

  • Predicate boolean test(T t) 传入一个参数返回boolean值
  • Consumer void accept(T t) 传入一个参数,无返回值
  • Function<T,R> R apply(T t) 传入一个参数,返回另一个类型

准备数据:

//计算机俱乐部
private static List<Student> computerClub = Arrays.asList(
		new Student("2015134001", "小明", 15, "1501"),
		new Student("2015134003", "小王", 14, "1503"),
		new Student("2015134006", "小张", 15, "1501"),
		new Student("2015134008", "小梁", 17, "1505")
);
//篮球俱乐部
private static List<Student> basketballClub = Arrays.asList(
		new Student("2015134012", "小c", 13, "1503"),
		new Student("2015134013", "小s", 14, "1503"),
		new Student("2015134015", "小d", 15, "1504"),
		new Student("2015134018", "小y", 16, "1505")
);
//乒乓球俱乐部
private static List<Student> pingpongClub = Arrays.asList(
		new Student("2015134022", "小u", 16, "1502"),
		new Student("2015134021", "小i", 14, "1502"),
		new Student("2015134026", "小m", 17, "1504"),
		new Student("2015134027", "小n", 16, "1504")
);

private static List<List<Student>> allClubStu = new ArrayList<>();
allClubStu.add(computerClub);
allClubStu.add(basketballClub);
allClubStu.add(pingpongClub);

常用的stream三种创建方式

  • 集合 Collection.stream()
  • 静态方法 Stream.of
  • 数组 Arrays.stream
//1.集合
Stream<Student> stream = basketballClub.stream();
//2.静态方法
Stream<String> stream2 = Stream.of("a", "b", "c");
//3.数组
String[] arr = {"a","b","c"};
Stream<String> stream3 = Arrays.stream(arr);

Stream的终止操作

  • foreach(Consumer c) 遍历操作
  • collect(Collector) 将流转化为其他形式
  • max(Comparator) 返回流中最大值
  • min(Comparator) 返回流中最小值
  • count 返回流中元素数量

Collectors 具体方法

  • toList List 把流中元素收集到List
  • toSet Set 把流中元素收集到Set
  • toCollection Coolection 把流中元素收集到Collection中
  • groupingBy Map<K,List> 根据K属性对流进行分组
  • partitioningBy Map<boolean, List> 根据boolean值进行分组
//此处只是演示 此类需求直接用List构造器即可
List<Student> collect = computerClub.stream().collect(Collectors.toList());
Set<Student> collect1 = pingpongClub.stream().collect(Collectors.toSet());
//注意key必须是唯一的 如果不是唯一的会报错而不是像普通map那样覆盖
Map<String, String> collect2 = pingpongClub.stream()
		.collect(Collectors.toMap(Student::getIdNum, Student::getName));
//分组 类似于数据库中的group by
Map<String, List<Student>> collect3 = pingpongClub.stream()
		.collect(Collectors.groupingBy(Student::getClassNum));
//字符串拼接 第一个参数是分隔符 第二个参数是前缀 第三个参数是后缀
String collect4 = pingpongClub.stream().map(Student::getName).collect(Collectors.joining(",", "【", "】"));
		//【小u,小i,小m,小n】
//三个俱乐部符合年龄要求的按照班级分组
Map<String, List<Student>> collect5 = Stream.of(basketballClub, pingpongClub, computerClub)
		.flatMap(e -> e.stream().filter(s -> s.getAge() < 17))
		.collect(Collectors.groupingBy(Student::getClassNum));
//按照是否年龄>16进行分组 key为true和false
ConcurrentMap<Boolean, List<Student>> collect6 = Stream.of(basketballClub, pingpongClub, computerClub)
		.flatMap(Collection::stream)
		.collect(Collectors.groupingByConcurrent(s -> s.getAge() > 16));

Stream的中间操作

1、filter(Predicate) 筛选流中某些元素

//筛选1501班的学生
computerClub.stream().filter(e -> e.getClassNum().equals("1501")).forEach(System.out::println);
//筛选年龄大于15的学生
List<Student> collect = computerClub.stream().filter(e -> e.getAge() > 15).collect(Collectors.toList());

2、map(Function f) 接收流中元素,并且将其映射成为新元素,例如从student对象中取name属性

//篮球俱乐部所有成员名 + 暂时住上商标^_^,并且获取所有队员名
List<String> collect1 = basketballClub.stream()
		.map(e -> e.getName() + "^_^")
		.collect(Collectors.toList());
collect1.forEach(System.out::println);
//小c^_^^_^
//小s^_^^_^
//小d^_^^_^
//小y^_^^_^

3、flatMap(Function f) 将所有流中的元素并到一起连接成一个流

//获取年龄大于15的所有俱乐部成员
List<Student> collect2 = Stream.of(basketballClub, computerClub, pingpongClub)
		.flatMap(e -> e.stream().filter(s -> s.getAge() > 15))
		.collect(Collectors.toList());
collect2.forEach(System.out::println);

//用双层list获取所有年龄大于15的俱乐部成员
List<Student> collect3 = allClubStu.stream()
		.flatMap(e -> e.stream().filter(s -> s.getAge() > 15))
		.collect(Collectors.toList());
collect3.forEach(System.out::println);

4、peek(Consumer c) 获取流中元素,操作流中元素,与foreach不同的是不会截断流,可继续操作流

//篮球俱乐部所有成员名 + 赞助商商标^_^,并且获取所有队员详细内容
List<Student> collect = basketballClub.stream()
		.peek(e -> e.setName(e.getName() + "^_^"))
		.collect(Collectors.toList());
collect.forEach(System.out::println);
//Student{idNum='2015134012', name='小c^_^', age=13, classNum='1503'}
//Student{idNum='2015134013', name='小s^_^', age=14, classNum='1503'}
//Student{idNum='2015134015', name='小d^_^', age=15, classNum='1504'}
//Student{idNum='2015134018', name='小y^_^', age=16, classNum='1505'}

5、distinct() 通过流所生成元素的equals和hashCode去重

6、limit(long val) 截断流,取流中前val个元素

7、sorted(Comparator) 产生一个新流,按照比较器规则排序

8、sorted() 产生一个新流,按照自然顺序排序

List<String> list = Arrays.asList("b","b","c","a");
list.forEach(System.out::print); //bbca
List<String> collect = list.stream().distinct().sorted().collect(Collectors.toList());
collect.forEach(System.out::print);//abc
//获取list中排序后的top2 即截断取前两个
List<String> collect1 = list.stream().distinct().sorted().limit(2).collect(Collectors.toList());
collect1.forEach(System.out::print);//ab

匹配

  1. booelan allMatch(Predicate) 都符合
  2. .boolean anyMatch(Predicate) 任一元素符合
  3. boolean noneMatch(Predicate) 都不符合
boolean b = basketballClub.stream().allMatch(e -> e.getAge() < 20);
boolean b1 = basketballClub.stream().anyMatch(e -> e.getAge() < 20);
boolean b2 = basketballClub.stream().noneMatch(e -> e.getAge() < 20);

寻找元素

  1. findFirst——返回第一个元素
  2. findAny——返回当前流中的任意元素
Optional<Student> first = basketballClub.stream().findFirst();
if (first.isPresent()) {
	Student student = first.get();
	System.out.println(student);
}

Optional<Student> any = basketballClub.stream().findAny();
if (any.isPresent()) {
	Student student2 = any.get();
	System.out.println(student2);
}
Optional<Student> any1 = basketballClub.stream().parallel().findAny();
System.out.println(any1);

计数和极值

  1. count——返回流中元素的总个数
  2. max——返回流中最大值
  3. min——返回流中最小值
long count = basketballClub.stream().count();
Optional<Student> max = basketballClub.stream().max(Comparator.comparing(Student::getAge));
if (max.isPresent()) {
	Student student = max.get();
}
Optional<Student> min = basketballClub.stream().min(Comparator.comparingInt(Student::getAge));
if (min.isPresent()) {
	Student student = min.get();
}

总结

Java8主要是在原来面向对象的基础上增加了函数式编程的能力。这样就出现了在Java中使用Lambda表达式,将一个函数作为方法的参数来进行传递。Java8的Stream就是典型的例子,Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。

这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。

创建Stream

Arrays.stream()

当在日常编程中面对的是一个数组,也可以使用Arrays.stream()方法来使用Stream。

Integer[] array = new Integer[]{3,4,8,16,19,27,23,99,76,232,33,96};
long count = Arrays.stream(array).filter(i->i>20).count();

Stream.of()

当面对数组时除了可以使用Arrays.stream()方法外,还可以使用Stream将需要的数组转成Stream。这个方法不但支持传入数组,将数组转成Stream,也支持传入多个参数,将参数最终转成Stream。

Integer[] array = new Integer[]{3,4,8,16,19,27,23,99,76,232,33,96};
long count = Stream.of(array).filter(i->i>20).count();
long sum = Stream.of(12,77,59,3,654).filter(i->i>20).mapToInt(Integer::intValue).sum();
System.out.println("count:"+count+",sum:"+sum);

Stream.generate()

Stream接口有两个用来创建无限Stream的静态方法。generate()方法接受一个参数函数,可以使用类似如下代码来创建一个你需要的Stream。

Stream<String> stream = Stream.generate(() -> "test").limit(10);
String[] strArr = stream.toArray(String[]::new);
System.out.println(Arrays.toString(strArr));

运行结果:[test, test, test, test, test, test, test, test, test, test]

Stream.iterate()

Stream接口的另一用来创建无限Stream的静态方法就是iterate()方法。iterate()方法也是接受一个参数函数,可以用类似如下代码来创建一个你需要的Stream。

Stream<BigInteger> bigIntStream = Stream.iterate(BigInteger.ZERO, n -> n.add(BigInteger.TEN)).limit(10);
BigInteger[] bigIntArr = bigIntStream.toArray(BigInteger[]::new);
System.out.println(Arrays.toString(bigIntArr));

运行结果:[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]

Collection.stream()

这个就是最常见的Stream了。因为Collection是Java中集合接口的父接口,Java中的集合都继承或实现了此接口。所以Java中的集合都可以使用此方法来创建一个Stream。

List<Integer> numbers = new ArrayList<>();
numbers.add(3);
numbers.add(4);
numbers.add(8);
numbers.add(16);   
numbers.stream().forEach(number->{
    System.out.println(number);
});

StreamSupport.stream()

通过查看Collection.stream()的方法,我们可以看出来,Colleciton.stream()其实是调用了StreamSupport.stream()来实现的。所以我们也可以使用StreamSupport.stream()来创建一个Stream。当我们面对的是一个迭代器的时候,使用StreamSupport.stream()就可以创建一个Stream。第一个参数是传入一个迭代器,第二个参数是true代表使用并行来进行处理。false代表串行来处理Stream。

List<Integer> numbers = new ArrayList<>();
numbers.add(3);
numbers.add(4);
numbers.add(8);
numbers.add(16);
numbers.add(19);
numbers.add(27);
numbers.add(23);
Spliterator<Integer> integers = numbers.spliterator();
StreamSupport.stream(integers,false).forEach(number->{
  System.out.println(number);
});

流的转换

filter方法

从名字上就能看出来,这是一个Stream的过滤转换,此方法会生成一个新的流,其中包含符合某个特定条件的所有元素。

List<Integer> integerList = Lists.newArrayList();
integerList.add(15);
integerList.add(32);
integerList.add(5);
integerList.add(232);
integerList.add(56);
List<Integer> after = integerList.stream()
                    .filter(i->i>50)
                    .collect(Collectors.toList());
System.out.println(after);

运行结果:[232, 56]

map方法

map方法指对一个流中的值进行某种形式的转换。需要传递给它一个转换的函数作为参数。

List<Integer> integerList = Lists.newArrayList();
integerList.add(15);
integerList.add(32);
integerList.add(5);
integerList.add(232);
integerList.add(56);
//将Integer类型转换成String类型
List<String> afterString = integerList.stream()
                .map(i->String.valueOf(i)).collect(Collectors.toList());
System.out.println(afterString);

提取对象的某一列

//从对象列表中提取一列(以name为例)
List<String> nameList = studentList.stream().map(StudentInfo::getName).collect(Collectors.toList());

提取age列并排重(使用distinct()函数)

//从对象列表中提取age并排重
List<Integer> ageList = studentList.stream().map(StudentInfo::getAge).distinct().collect(Collectors.toList());

flatMap方法

上面用map方法进行流转换的时候,是对每个元素应用一个函数,并将返回的值收集到一个新的流中。但是如果有一个函数,它返回的不是一个值,而是一个包含多个值的流。但是你需要的是一个包含多个流中的元素的集合。

例如:

List<Integer> oneList = Lists.newArrayList(),
twoList = Lists.newArrayList();
oneList.add(34);
oneList.add(23);
oneList.add(87);

twoList.add(29);
twoList.add(48);
twoList.add(92);
Map<String,List<Integer>> testMap = Maps.newHashMap();
testMap.put("1",oneList);
testMap.put("2",twoList);
//返回的是一个流的集合,但是我需要的是List<Integer>这样一个集合
List<Stream<Integer>> testList = testMap.values().stream()
                    .map(number->number.stream()).collect(Collectors.toList());

这个时候就应该使用flatMap将多个流进行合并,然后再收集到一个集合中。

List<Integer> testList = testMap.values().stream()
                .flatMap(number->number.stream()).collect(Collectors.toList());

limit方法和skip方法

limit(n)方法会返回一个包含n个元素的新的流(若总长小于n则返回原始流)。

List<Integer> myList = Lists.newArrayList();
myList.add(1);
myList.add(2);
myList.add(3);
myList.add(4);
myList.add(5);
myList.add(6);
List<Integer> afterLimit = myList.stream().limit(4).collect(Collectors.toList());
System.out.println("afterLimit:"+afterLimit);

skip(n)方法正好相反,它会丢弃掉前面的n个元素。

List<Integer> afterSkip = myList.stream().skip(4).collect(Collectors.toList());
System.out.println("afterSkip:"+afterSkip);

运行结果:

afterLimit:[1, 2, 3, 4]
afterSkip:[5, 6]

用limit和skip方法一起使用就可以实现日常的分页功能:

List<Integer> pageList = myList.stream()
                  .skip(pageNumber*pageSize)
                  .limit(pageSize).collect(Collectors.toList());

distinct方法和sorted方法

上面介绍的流的转换方法都是无状态的。即从一个已经转换的流中取某个元素时,结果并不依赖于之前的元素。除此之外还有两个方法在转换流时是需要依赖于之前流中的元素的。一个是distinct方法一个是sorted方法。

distinct方法会根据原始流中的元素返回一个具有相同顺序、去除了重复元素的流,这个操作显然是需要记住之前读取的元素。

List<Integer> myTestList = Lists.newArrayList();
myTestList.add(10);
myTestList.add(39);
myTestList.add(10);
myTestList.add(78);
myTestList.add(10);
List<Integer> distinctList = myTestList.stream()
                        .distinct().collect(Collectors.toList());
System.out.println("distinctList:"+distinctList);

运行结果:distinctList:[10, 39, 78]

sorted方法是需要遍历整个流的,并在产生任何元素之前对它进行排序。因为有可能排序后集合的第一个元素会在未排序集合的最后一位。

List<Integer> myTestList = Lists.newArrayList();
myTestList.add(39);
myTestList.add(78);
myTestList.add(10);
myTestList.add(22);
myTestList.add(56);
List<Integer> sortList = myTestList.stream()
                .sorted(Integer::compareTo).collect(Collectors.toList());
System.out.println("sortList:"+sortList);

运行结果:sortList:[10, 22, 39, 56, 78]

聚合操作

前面已经介绍了流的创建和转换,下面介绍流的聚合,聚合是指将流汇聚为一个值,以便在程序中使用。聚合方法都是终止操作。

max方法和min方法

在前面的代码例子中使用的count方法和sum方法都属于流从聚合方法。还有两个聚合方法是max方法和min方法,分别返回流中最大值和最小值。

List<Integer> hearList = Lists.newArrayList();
hearList.add(15);
hearList.add(32);
hearList.add(5);
hearList.add(232);
hearList.add(56);
hearList.add(29);
hearList.add(94);
Integer maxItem = hearList.stream().max(Integer::compareTo).get();
Integer minItem = hearList.stream().min(Integer::compareTo).get();
System.out.println("max:"+maxItem+",min:"+minItem);

findFirst方法

findFirst方法返回非空集合中的第一个值,它通常与filter方法结合起来使用。

List<Integer> hearList = Lists.newArrayList();
hearList.add(15);
hearList.add(32);
hearList.add(5);
hearList.add(232);
hearList.add(56);
hearList.add(29);
hearList.add(104);
Integer first = hearList.stream().filter(i->i>100).findFirst().get();

findAny方法

findAny方法可以在集合中只要找到任何一个所匹配的元素,就返回,此方法在对流并行执行时十分有效(任何片段中发现第一个匹配元素都会结束计算,串行流中和findFirst返回一样)。

Integer anyItem = hearList.parallelStream().filter(i->i>100).findAny().get();

anyMatch方法

anyMatch方法可以判定集合中是否还有匹配的元素。返回结果是一个boolean类型值。

boolean isHas = hearList.parallelStream().anyMatch(i->i>100);

allMatch方法和noneMatch方法

allMatch方法和noneMatch方法,分别在所有元素匹配和没有元素匹配时返回true。

boolean allHas = hearList.parallelStream().allMatch(i->i>100);
boolean noHas = hearList.parallelStream().noneMatch(i->i>100);

虽然这些方法总是会检查整个流,但是仍然可以通过并行执行来提高速度。 

reduce方法

reduce方法是将流中的元素进行进一步计算的方法。


List<Integer> hearList = Lists.newArrayList();
hearList.add(15);
hearList.add(32);
hearList.add(5);
hearList.add(232);
hearList.add(56);
hearList.add(29);
hearList.add(104);
//求和
Integer sum = hearList.stream().reduce((x,y)->x+y).get();
System.out.println("sum:"+sum);
//简化一下,求和
sum = hearList.stream().reduce(Integer::sum).get();
System.out.println("sum:"+sum);
//含有初始标识的,求和
sum = hearList.stream().reduce(0,(x,y)->x+y);
System.out.println("sum:"+sum);
//对元素的长度进行求和( (total,y)->total+y.toString().length(),类似于一个累加器,会被重复调用)
sum = hearList.stream().reduce(0,(total,y)->total+y.toString().length(),(total1,total2)->total1+total2);
System.out.println("sum:"+sum);
//简化一下,对元素长度进行求和。
sum = hearList.stream().map(Objects::toString).mapToInt(String::length).sum();
System.out.println("sum:"+sum);

收集结果

当处理完流之后,通常是想查看一下结果,而不是将他们聚合为一个值。Collectorts类为我们提供了常用的收集类的各个工厂方法。

收集到集合

例如前面的例子用的要将一个流收集到一个List中,只需要这样写就可以。

List<Integer> thereList = hereList.stream().collect(Collectors.toList());

收集到Set中可以这样用

Set<Integer> thereSet = hereList.stream().collect(Collectors.toSet());

收集到Set时,控制Set的类型,可以这样。

TreeSet<Integer> treeSet = hereList.stream()
                    .collect(Collectors.toCollection(TreeSet::new));

拼接

将字流中的字符串连接并收集起来。

String resultString = stringList.stream().collect(Collectors.joining());

在将流中的字符串连接并收集起来时,想在元素中介添加分隔符,传递个joining方法即可。

String resultString = stringList.stream().collect(Collectors.joining(","));

当流中的元素不是字符串时,需要先将流转成字符串流再进行拼接。

String hereResultString = hereList.stream()
                .map(String::valueOf).collect(Collectors.joining(","));

收集聚合

分别收集流的总和、平均值、最大值或者最小值。

List<Integer> hereList = Lists.newArrayList();
hereList.add(15);
hereList.add(32);
hereList.add(5);
hereList.add(232);
hereList.add(56);
hereList.add(29);
hereList.add(104);

//总和、平均值,最大值,最小值
int sum = hereList.stream().collect(Collectors.summingInt(Integer::intValue));
Double ave = hereList.stream().collect(Collectors.averagingInt(Integer::intValue));
Integer max = hereList.stream().collect(Collectors.maxBy(Integer::compare)).get();
Integer min = hereList.stream().collect(Collectors.minBy(Integer::compare)).get();
System.out.println("sum:"+sum+",ave:"+ave+",max:"+max+",min:"+min);

一次性收集流中的结果,聚合为一个总和,平均值,最大值或最小值的对象。

IntSummaryStatistics summaryStatistics = hereList.stream()
                          .collect(Collectors.summarizingInt(Integer::intValue));
System.out.println(summaryStatistics);

将结果集收集到Map

当我们希望将集合中的元素收集到Map中时,可以使用Collectors.toMap方法。这个方法有两个参数,用来生成Map的key和value。

例如将一个Room对象的high作为键width作为值

Map<Integer,Integer> hwMap = roomList.stream()
                        .collect(Collectors.toMap(Room::getHigh, Room::getWidth));

但是通常还是以具体元素作为值的情况多,可以使用Function.identity()来获取实际元素。

Map<Integer,Room> roomMap = roomList.stream()
                        .collect(Collectors.toMap(Room::getHigh, Function.identity()));

如果多个元素拥有相同的键,在收集结果时会抛出java.lang.IllegalStateException异常。可以使用第三个参数来解决,第三个参数用来确定当出现键冲突时,该如何处理结果,如果当出现键冲突时只保留一个并且是保留已经存在的值时,就是如下方式。

Map<Integer,Room> rMap = roomList.stream()
                .collect(Collectors.toMap(Room::getHigh, Function.identity(),(nowValue,newValue)->nowValue));

如果想指定生成的Map类型,则还需要第三个参数。

TreeMap<Integer,Room> roomTreeMap = roomList.stream()
                .collect(Collectors.toMap(Room::getHigh, 
            Function.identity(),(nowValue,newValue)->newValue,TreeMap::new));

注意:每个toMap方法,都会有一个对应的toConCurrentMap方法,用来生成一个并发Map。

分组分片

在一个集合中,对具有相同特性的值进行分组是一个很常见的功能,在Stream的API中也提供了相应的方法。

分组

还是上面的例子,将一个Room对象集合按照高度分组。

List<Room> roomList = Lists.newArrayList(
new Room(11,23,56),
new Room(11,84,48),
new Room(22,46,112),
new Room(22,75,62),
new Room(22,56,75),
new Room(33,92,224));

Map<Integer,List<Room>> groupMap = roomList.stream().collect(Collectors.groupingBy(Room::getHigh));
System.out.println("groupMap:"+groupMap);

分片

当分类函数是一个返回布尔值的函数时,流元素会被分为两组列表:一组是返回true的元素集合,另一组是返回false的元素集合。这种情况适用partitoningBy方法会比groupingBy更有效率。

例如我们将房间集合分为两组,一组是高度为22的房间,另一组是其他房间。

Map<Boolean,List<Room>> partitionMap = roomList.stream()
                .collect(Collectors.partitioningBy(room->room.getHigh()==22));

扩展功能

下面要介绍的这些方法功能,无论是groupingBy方法还是partitioningBy方法都是支持的。

counting方法会返回收集元素的总个数。

Map<Integer,Long> countMap = roomList.stream()
           .collect(Collectors.groupingBy(Room::getHigh,Collectors.counting()));

summing(Int|Long|Double)方法接受一个取值函数作为参数,来计算总和。

Map<Integer,Integer> sumMap = roomList.stream().
                collect(Collectors.groupingBy(Room::getHigh,Collectors.summingInt(Room::getWidth)));

maxBy方法和minBy方法接受比较器作为参数来计算最大值和最小值。

取出分组中宽度最大和最小的房间。

Map<Integer, Optional<Room>> maxMap = roomList.stream().
                collect(Collectors.groupingBy(Room::getHigh,
                        Collectors.maxBy(Comparator.comparing(Room::getWidth))
                ));
Map<Integer, Optional<Room>> minMap = roomList.stream().
                collect(Collectors.groupingBy(Room::getHigh,
                        Collectors.minBy(Comparator.comparing(Room::getWidth))
                ));

System.out.println("maxMap:"+ JSON.toJSONString(maxMap));
System.out.println("minMap:"+JSON.toJSONString(minMap));

mapping方法会将结果应用到另一个收集器上。

取出分组中宽度最大的房间的宽度。

Map<Integer, Optional<Integer>> collect = roomList.stream().collect(Collectors.groupingBy(Room::getHigh,
                Collectors.mapping(Room::getWidth,
                        Collectors.maxBy(Comparator.comparing(Integer::valueOf)))));

System.out.println("collect:"+JSON.toJSONString(collect));

无论groupingBy或是mapping函数,如果返回类型是int、long、double都可以将元素收集到一个summarystatistics对象中,然后从每组的summarystatistics对象中取出函数值的总和、平均值、总数、最大值和最小值。

Map<Integer,IntSummaryStatistics> summaryStatisticsMap = roomList.stream()
                .collect(Collectors.groupingBy(Room::getHigh,
                Collectors.summarizingInt(Room::getWidth)));

System.out.println("summaryStatisticsMap:"+summaryStatisticsMap);

多级分组

上面的例子我们都是按一个条件进行的一级分组,其实groupingBy是支持多级分组的。

例如第一级我们将房间按照高度分组,第二级按照宽度分组。

Map<Integer,Map<Integer,List<Room>>> multistageMap = roomList.stream().collect(
          Collectors.groupingBy(Room::getHigh,Collectors.groupingBy(Room::getWidth)));
System.out.println("multistageMap:"+JSON.toJSONString(multistageMap));

并行流

Stream的建立,使得并行计算变得容易,但是并行流在使用的时候也是需要注意的。

首先,必须是一个并行流,只要在终止方法执行时,流处于并行模式,那么所有的流操作就都会并行执行。

Stream.of(roomList).parallel();

parallel方法可以将任意的串行流转换为一个并行流。

其次要确保传递给并行流操作的函数是线程安全的。

int[] words = new int[23];
Stream.of(roomList).parallel().forEach(s->{
     if(s.size()<10){
           words[s.size()]++;
     }
});

上面这个例子中的代码就是错误的,传递给并行流的操作并不是线程安全的。可以改为AtomicInteger的对象数组来作为计数器。

我们使在处理集合数据量较大的时候才能体现出并行流的优势,并且目的是为了在保证线程安全的情况下,提升效率,利用多核CPU的资源。

使用Stream的API时,在遍历或处理流的过程中当引用外部变量的时候会默认的将变量当成final变量来处理。所以有些同学就会觉得在遍历的过程中取不出来集合的索引。其实可以换一种思想可以只遍历集合索引,然后在遍历中取值。

IntStream.range(0,roomList.size()).forEach(i->{
       System.out.println(roomList.get(i));
});

 

参考:

 

posted @ 2022-01-22 17:18  残城碎梦  阅读(165)  评论(0编辑  收藏  举报