Stream 简介
一、Stream 概述
简介:
Stream用于进行计算,是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
Stream并不会存储数据。
Stream并不会改变数据源,它会返回一个含有结果集的Stream。
Stream是延迟加载的,在需要结果时才执行。
步骤:
- 创建一个流。
- 将初始流转换为其他流的中间操作,可能包含多个步骤。
- 终止操作,执行终止操作后才执行中间操作链,此后这个流就不能再使用了。
二、Stream的创建
通过java.util.Collection.stream()
方法用集合创建流
public void method(){
ArrayList<User> users = getUsers();
Stream<User> stream = users.stream();//顺序流
Stream<User> userStream = users.parallelStream();//并行流
}
使用java.util.Arrays.stream(T[] array)
方法用数组创建流
public void method(){
Integer[] arr = new Integer[]{1,2,3,4};
Stream<Integer> stream = Arrays.stream(arr);
}
使用Stream
的静态方法:of()、iterate()、generate()
public static<T> Stream<T> of(T... values)
//无限流
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
public static<T> Stream<T> generate(Supplier<? extends T> s)
/**无限流
* iterate:迭代
* generate:生成
*/
public void method(){
Stream.iterate(1, x -> x+2).limit(10).forEach(System.out::println);
Stream.generate(Math::random).limit(10).forEach(System.out::println);
}
三、Stream与parallerStream
stream
和parallelStream
的简单区分: stream
是顺序流,由主线程按顺序对流执行操作,而parallelStream
是并行流,内部以多线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求。例如筛选集合中的奇数,两者的处理不同之处:
Stream中间操作
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理。
而在终止操作时一次性全 部处理,称为“惰性求值”。
操作类:
public class User{
private Stirng name;
private Integer age;
.....
}
一、筛选与切片
方法:
filter(Predicate p) :
过滤,接收 Lambda 从流中排除某些元素 。
distinct():
去重,通过流所生成元素的 hashCode() 和 equals() 去除重复元素 。
limit(long maxSize):
截断,使其元素不超过给定数量 。
skip(long n):
跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一 个空流。与 limit(n) 互补 。
示例:
//filter(Predicate p) 接收 Lambda , 从流中排除某些元素
public void test5(){
ArrayList<User> users = getUsers();
users.stream().filter(e -> e.getAge() >20).forEach(System.out::println);
}
//distinct() 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
public void test6(){
ArrayList<User> users = getUsers();
users.add(new User("周聪",70));
users.add(new User("周聪",70));
users.add(new User("周聪",70));
users.add(new User("周聪",70));
users.stream().distinct().forEach(System.out::println);
}
//limit(long maxSize) 截断流,使其元素不超过给定数量
public void test7(){
ArrayList<User> users = getUsers();
users.stream().limit(3).forEach(System.out::println);
}
//skip(long n) 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一 个空流。与 limit(n) 互补
public void test8(){
ArrayList<User> users = getUsers();
users.stream().skip(2).forEach(System.out::println);
}
二、映射
方法:
map(Function f):
接收一个函数作为参数,该函数会被应用到每个元 素上,并将其映射成一个新的元素。
mapToDouble(ToDoubleFunction f):
接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 DoubleStream。
。。。。
flatMap(Function f):
接收一个函数作为参数,将流中的每个值都换成另 一个流,然后把所有流连接成一个流。
flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper):
接收一个函数作为参数,将流中的每个值都换成DoubleStream流,然后把所有流连接成一个流。
。。。。
示例:
public void method(){
ArrayList<User> users = getUsers();
users.stream()
.filter(e -> e.getAge() < 21)
.map(e -> e.getUsername()).forEach(System.out::println);
}
//flatMap,将{{a,b,c},{1,2,3}} -> a,b,c,1,2,3。
//在这种格式下由于是List包含List,此时就可以使用flatMap,将内部元素List作为多个流,来进行元素之间的分隔,当然你也可以看情况使用。
public void test2(){
ArrayList<List<String>> arrayList = new ArrayList<>();
arrayList.add(new ArrayList<String>(){{add("a");add("b");add("c");}});
arrayList.add(new ArrayList<String>(){{add("1");add("2");add("3");}});
String collect = arrayList.stream().flatMap(e -> {
return e.stream();
}).collect(Collectors.joining(","));
System.out.println(collect); //a,b,c,1,2,3
}
三、排序
方法:
sorted():
产生一个新流,其中按自然顺序排序 。
sorted(Comparator com):
产生一个新流,其中按比较器顺序排序 。
示例:
public void test2(){
list.stream().sorted();
list.stream().sorted((e1,e2) ->{return e2.compareTo(e1);});
}
Stream终止操作
终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例 如:List、Integer,甚至是 void 。
流进行了终止操作后,不能再次使用。
操作类:
public class User{
private Stirng name;
private Integer age;
.....
}
一、匹配与查找
方法:
allMatch(Predicate p):
检查是否匹配所有元素 。
anyMatch(Predicate p):
检查是否至少匹配一个元素 。
noneMatch(Predicate p):
检查是否没有匹配所有元素 。
findFirst():
返回第一个元素 。
findAny():
返回当前流中的任意元素 。
count() :
返回流中元素总数 。
max(Comparator c):
返回流中最大值 。
min(Comparator c) :
返回流中最小值 。
forEach(Consumer c):
内部迭代(使用 Collection 接口需要用户去做迭代, 称为外部迭代。相反,Stream API 使用内部迭 代——它帮你把迭代做了) 。
示例:
/**
*allMatch(Predicate p) 检查是否匹配所有元素
*anyMatch(Predicate p) 检查是否至少匹配一个元素
*noneMatch(Predicate p) 检查是否没有匹配所有元素
*/
public void test12(){
ArrayList<User> users = getUsers();
boolean b = users.stream().allMatch(e -> e.getAge() == 21);
System.out.println(b);
boolean b1 = users.stream().anyMatch(e -> e.getAge() == 21);
System.out.println(b1);
boolean b2 = users.stream().noneMatch(e -> e.getAge() == 70);
System.out.println(b2);
}
/**
*findFirst() 返回第一个元素
*findAny() 返回当前流中的任意元素
*/
public void test13(){
ArrayList<User> users = getUsers();
Optional<User> first = users.stream().findFirst();
System.out.println(first.get());
Optional<User> any = users.stream().findAny();
System.out.println(any.get());
}
/**
*count() 返回流中元素总数
*max(Comparator c) 返回流中最大值
*min(Comparator c) 返回流中最小值
*forEach(Consumer c)内部迭代(使用 Collection 接口需要用户去做迭代, 称为外部迭代。相反,Stream *API 使用内部迭 代——它帮你把迭代做了)
*/
public void test14(){
ArrayList<User> users = getUsers();
long count = users.stream().count();
System.out.println(count);
Optional<Integer> max = users.stream().map(e -> e.getAge()).max(Integer::compareTo);
Optional<String> min = users.stream()
.map(e -> e.getUsername())
.min(String::compareTo);
System.out.println(max);
System.out.println(min);
}
二、规约
map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它来进行网络搜索而出名。
方法:
T reduce(T identity, BinaryOperator<T> accumulator):
可以将流中元素反复结合起来,得到一 个值。返回 T。
reduce(BinaryOperator accumulator):
可以将流中元素反复结合起来,得到一 个值。返回 Optional。
U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner)
非并行流情况下,与双参数的没啥区别。但在并行流情况下,通过ForkJoin,整个元素集合分为多个部分进行accumulator,再将多个部分的计算结果combiner汇总。
示例:
/**
*reduce(T iden, BinaryOperator b):可以将流中元素反复结合起来,得到一 个值。返回 T
*reduce(BinaryOperator b):可以将流中元素反复结合起来,得到一 个值。返回 Optional<T>
*/
public void test15(){
ArrayList<User> users = getUsers();
//获取User总的年龄
Integer ageSum = users.stream()
.map(e -> e.getAge())
.reduce(0, Integer::sum);
System.out.println(ageSum);
//User名字的拼接
Optional<String> nameStr = users.stream()
.map(e -> e.getUsername())
.reduce(String::concat);
System.out.println(nameStr.get());
}
/**
* 三参数情况
* sum1: 为非并行流情况下的结果。
* sum2: 为并行流情况下的结果。
*/
@Test
public void test3(){
Integer sum1 = Stream.of(1, 2, 3).reduce(1, (e1, e2) -> {
return e1 + e2;
}, (e1, e2) -> {
return e1 + e2;
});
System.out.println(sum1); //1 + 2 + 3 + 4 = 7
Integer sum2 = Stream.of(1,2,3,4,5,6,7).parallel().reduce(1, (e1, e2) -> {
System.out.println("线程:" + Thread.currentThread().getName() + "相加" + e1 + " + " + e2 + " = " + (e1+e2));
return e1 + e2;
}, (e1, e2) -> {
System.out.println("线程:" + Thread.currentThread().getName()+"合并, e1 = " +e1 + " e2 = " + e2);
return e1 + e2;
});
System.out.println(sum2);//(1 + 1) + (1 + 2) + (1 + 3) = 9
}
结果:
7
线程:main相加1 + 5 = 6
线程:main相加1 + 4 = 5
线程:main合并, e1 = 5 e2 = 6
线程:ForkJoinPool.commonPool-worker-3相加1 + 1 = 2
线程:main相加1 + 6 = 7
线程:ForkJoinPool.commonPool-worker-3相加1 + 3 = 4
线程:ForkJoinPool.commonPool-worker-2相加1 + 7 = 8
线程:ForkJoinPool.commonPool-worker-2合并, e1 = 7 e2 = 8
线程:ForkJoinPool.commonPool-worker-2合并, e1 = 11 e2 = 15
线程:ForkJoinPool.commonPool-worker-1相加1 + 2 = 3
线程:ForkJoinPool.commonPool-worker-1合并, e1 = 3 e2 = 4
线程:ForkJoinPool.commonPool-worker-1合并, e1 = 2 e2 = 7
线程:ForkJoinPool.commonPool-worker-1合并, e1 = 9 e2 = 26
35
扩展结果:将其改为非并行流
7
线程:main相加1 + 1 = 2
线程:main相加2 + 2 = 4
线程:main相加4 + 3 = 7
线程:main相加7 + 4 = 11
线程:main相加11 + 5 = 16
线程:main相加16 + 6 = 22
线程:main相加22 + 7 = 29
29
三、收集
方法:
collect(Collector c):
将流转换为其他形式。接收一个 Collector 接口的实现,用于给Stream中元素做汇总 的方法。
Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、 Map)。
R collect(Supplier<R> supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner):
和上面的reduce三个参数的有点类似,并行流情况下也使用了ForkJoin,总 - 分 - 总。
supplier ---- 一个创建新结果容器的函数。对于并行执行,此函数可能会被多次调用,并且每次都必须返回一 个新值。
accumulator ---- 用于将附加元素合并到结果中的函数。
combiner ---- 组合两个值的函数,必须与accumulator 函数兼容。
示例:
//并行流情况下的计算,每个分支创建一个ArrayList
@Test
public void test4(){
ArrayList<Integer> list = Stream.of(1, 2, 3)
.parallel()
.collect(ArrayList<Integer>::new,
(e1, e2) -> {
System.out.println("线程:" +
Thread.currentThread().getName() + e2 + " 元素添加 "+e2 + " to " + e1);
e1.add(e2);
},
(e1, e2) -> {
System.out.println("线程:" + Thread.currentThread().getName() + "元素合并" +
e2 + "to" + e1);
e1.addAll(e2);
}
);
System.out.println(list);
}
结果:
线程:main2 元素添加 2 to []
线程:ForkJoinPool.commonPool-worker-11 元素添加 1 to []
线程:ForkJoinPool.commonPool-worker-23 元素添加 3 to []
线程:ForkJoinPool.commonPool-worker-2元素合并[3]to[2]
线程:ForkJoinPool.commonPool-worker-2元素合并[2, 3]to[1]
[1, 2, 3]
扩展结果:非并行流
线程:main1 元素添加 1 to []
线程:main2 元素添加 2 to [1]
线程:main3 元素添加 3 to [1, 2]
[1, 2, 3]
Collectors类
此类用在collect(Collector c)中,为产生Collector 的工具类,类似于Collections,Arrays工具类,内部包含许多的静态方法,用于产生一个Collector 对象。
简单示例:
Set<String> collect2 = list.stream().collect(Collectors.toSet());
Collector<T, A, R>接口:
T:表示原始元素类型
A:表示中间累加类型
R:表示最终结果类型
一、toxxx()
用于将流转换为List、Set。。。
方法:
Collector<T, ?, C> toCollection(Supplier<C> collectionFactory):
用于将结果存放到Collection中,collectionFactory — 为一个适当的,空的Collection容器,比如ArrayList 等 。
Collector<T, ?, List<T>> toList():
用于将结果存放到List中,ArrayList为其容器。
Collector<T, ?, Set<T>> toSet():
用于将结果存放到Set中, HashSet为其容器。
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper):
keyMapper为key的转换器,valueMapper为value的转换器。
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction)
keyMapper为key的转换器,valueMapper为value的转换器。mergeFunction为当key冲突时,选择那个 value。
Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction,
Supplier<M> mapSupplier)
keyMapper为key的转换器,valueMapper为value的转换器。mergeFunction为当key冲突时,选择那个 value。
mapSupplier为使存储元素的map容器(比如HashMap、TreeMap)。
Collector<T, ?, ConcurrentMap<K,U>> toConcurrentMap(
Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction)
keyMapper为key的转换器,valueMapper为value的转换器。mergeFunction为当key冲突时,选择那个 value。
map为ConcurrentHashMap。
Collector<T, ?,。。> toConcurrentMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction,
Supplier<M> mapSupplier)
keyMapper为key的转换器,valueMapper为value的转换器。mergeFunction为当key冲突时,选择那个 value。
mapSupplier为存储元素的容器如何创建,需要是ConcurrentMap的子类
示例:
ArrayList<Integer> collection = Stream
.of(1, 2, 3, 4)
.collect(Collectors.toCollection(ArrayList::new));
List<Integer> list = Stream
.of(1, 2, 3, 4)
.collect(Collectors.toList());
Set<Integer> set = Stream
.of(1, 2, 3, 4)
.collect(Collectors.toSet());
list.stream().collect(Collectors.toMap(e -> {return e + "id";}, e ->{return e + "value";}));
list.stream().collect(Collectors.toMap(e -> {return e + "id";}, e ->{return e + "value";}, (e1,e2) -> {return e1;}));
list.stream().collect(Collectors.toMap(e -> {return e + "id";}, e ->{return e + "value";}, (e1,e2) -> {return e1;},TreeMap::new));
二、joining
对流中的元素按照某个字符进行拼接,并且可以1指定前缀与后缀。
方法:
Collector<CharSequence, ?, String> joining():
内部使用StringBuilder进行拼接,无分隔符,或者分隔符为”“。
Collector<CharSequence, ?, String> joining(CharSequence delimiter):
内部使用StringJoiner进行拼接,delimiter表示分隔符
Collector<CharSequence, ?, String> joining(CharSequence delimiter,
CharSequence prefix,
CharSequence suffix):
内部使用StringJoiner进行拼接,delimiter表示分隔符,prefix为前缀,suffix为后缀。
示例:
String s = Stream.of("1","2","3").collect(Collectors.joining()); //123
String s1 = Stream.of("1","2","3")
.collect(Collectors.joining(",")); //1,2,3
String s2 = Stream.of("1","2","3")
.collect(Collectors.joining(",", "prefix", "suffix")); //prefix1,2,3suffix
三、mapping
对流中的元素进行映射,并将其传递给downstream 下游收集器。通常配合groupingBy等分组方法使用。如果仅仅是对整个元素集合进行reduce-mapping,请使用中间操作toMap()。
方法:
Collector<T, ?, R> mapping(Function<? super T, ? extends U> mapper,
Collector<? super U, A, R> downstream):
通过在collector之前对每个输入元素应用映射函数, 将Collector 接受类型为 T 的元素调整为U类型。
mapper ---- 为映射器,downstream – 为下游收集器。
示例:
//简单示例
Stream.of("1","2")
.collect(Collectors.mapping(e ->{return e + "ha";}, Collectors.toList()));
//获取每个城市的姓氏
Map<City, Set<String>> lastNamesByCity = people.stream()
.collect(groupingBy(Person::getCity,
Collectors.mapping(Person::getLastName,Collectors.toSet())
)
);
四、collectioningAndThen
先通过downstream下游收集器收集元素,再通过finisher转换,完成一下额外的步骤。
方法:
Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream,
Function<R,RR> finisher):
downstream - 为下游收集器, finisher - 应用于下游收集器的函数,用于元素的转换R -》RR
示例:
//调整收集器,最终生成一个不可变的列表
List<String> people = people.stream()
.collect(collectingAndThen(Collecors.toList(),
Collections::unmodifiableList
)
);
五、计算
包括像一些求和、最大值、最小、平均值的一些收集器 。
方法:
Collector<T, ?, Long> counting():
元素数量
Collector<T, ?, Optional<T>> minBy(Comparator<? super T> comparator):
通过传入的比较器求min值
Collector<T, ?, Optional<T>> maxBy(Comparator<? super T> comparator):
通过传入的比较器求max值
Collector<T, ?, Integer> summingInt(ToIntFunction<? super T> mapper):
转换为Integer,再求和sum
Collector<T, ?, Long> summingLong(ToLongFunction<? super T> mapper):
转换为Long,再求和sum
Collector<T, ?, Double> summingDouble(ToDoubleFunction<? super T> mapper):
转换为Double,再求和sum 。
Collector<T, ?, Double> averagingInt(ToIntFunction<? super T> mapper):
转换为Integer,再求平均值average 。
Collector<T, ?, Double> averagingLong(ToLongFunction<? super T> mapper):
转换为Long,再平均值average 。
Collector<T, ?, Double> averagingDouble(ToDoubleFunction<? super T> mapper):
转换为Double,再平均值average 。
Collector<T, ?, IntSummaryStatistics> summarizingInt(ToIntFunction<? super T> mapper):
转换为Integer,获取IntSummaryStatistics对象,该对象包含sum、max、min、average 。
Collector<T, ?, LongSummaryStatistics> summarizingLong(ToLongFunction<? super T> mapper)
Collector<T, ?, DoubleSummaryStatistics> summarizingDouble(ToDoubleFunction<? super T> mapper)
示例:
ArrayList<String> list = new ArrayList<>();
....
Optional<String> min = list.stream().collect(Collectors.minBy((e1, e2) -> {
return e1.compareTo(e2);
}));
Optional<String> max = list.stream().collect(Collectors.maxBy((e1, e2) -> {
return e1.compareTo(e2);
}));
Integer sum1 = list.stream().collect(Collectors.summingInt(e -> {
return Integer.parseInt(e);
}));
Long sum2 = list.stream().collect(Collectors.summingLong(e -> {
return Long.parseLong(e);
}));
Double sum3 = list.stream().collect(Collectors.summingDouble(e -> {
return Double.valueOf(e);
}));
Double average = list.stream().collect(Collectors.averagingInt(e -> {
return Integer.valueOf(e);
}));
六、reducing 规约
对元素进行规约得到一个值,通常配置groupingBy()、partitioningBy()使用,要对流执行简单的 map-reduce,请改用 Streammap(Function) 和 Streamreduce(Object, BinaryOperator) 。
方法:
Collector<T, ?, T> reducing(T identity, BinaryOperator<T> op):
Collector<T, ?, Optional<T>> reducing(BinaryOperator<T> op):
Collector<T, ?, U> reducing(U identity,
Function<? super T, ? extends U> mapper,
BinaryOperator<U> op):
identity —为规约的标识符,也是当没有元素时的返回值。
mapper --为映射器,应用于每个输入值的映射函数。
op — 为具体操作,比如两数相加。
示例:
//求每个城市最高的人
Comparator<Person> byHeight = Comparator.comparing(Person::getHeight);
Map<City, Person> tallestByCity = people.stream().collect(
Collectors.groupingBy(
Person::getCity, Collectors.reducing(BinaryOperator.maxBy(byHeight))
)
);
//counting()就是使用reducing实现的
public static <T> Collector<T, ?, Long>
counting() {
return reducing(0L, e -> 1L, Long::sum);
}
七、分组
当需要根据某些情况进行分组时,可以使用groupingBy()、partitioningBy()。
partitioningBy()为断言型分组、groupingBy()为函数型分组 。
方法:
Collector<T, ?, Map<K, List<T>> groupingBy(Function<? super T, ? extends K> classifier):
classifier — 将输入元素映射到键的分类器函数
Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,
Collector<? super T, A, D> downstream):
classifier — 将输入元素映射到键的分类器函数。
downstream — 下游收集器,可再次分组。
Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
Supplier<M> mapFactory,
Collector<? super T, A, D> downstream):
classifier — 将输入元素映射到键的分类器函数。
mapFactory ---- 产生Map的工厂。
downstream — 下游收集器,可再次分组等操作。
Collector<T, ?, ConcurrentMap<K, List<T>>> groupingByConcurrent(
Function<? super T, ? extends K> classifier):
Collector<T, ?, ConcurrentMap<K, D>> groupingByConcurrent(
Function<? super T, ? extends K> classifier,
Collector<? super T, A, D> downstream)
<T, K, A, D, M extends ConcurrentMap<K, D>> Collector<T, ?, M> groupingByConcurrent(Function<? super T, ? extends K> classifier,
Supplier<M> mapFactory,
Collector<? super T, A, D> downstream)
Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate):
predicate ---- 分组判断
Collector<T, ?, Map<Boolean, D>> partitioningBy(Predicate<? super T> predicate,
Collector<? super T, A, D> downstream)
predicate ---- 分组判断
downstream ----- 下游收集器,可再次分组。
示例:
list.stream().collect(Collectors.groupingBy(e ->{
if (e.equals("1")){
return "group1";
} else if(e.equals("2")){
return "group2";
}
return "group3";
}));
list.stream().collect(Collectors.groupingBy(e ->{
if (e.equals("1")){
return "group1";
} else if(e.equals("2")){
return "group2";
} else {
return "group3";
}
},Collectors.toList()));
list.stream().collect(Collectors.groupingBy(e ->{
if (e.equals("1")){
return "group1";
} else if(e.equals("2")){
return "group2";
} else {
return "group3";
}
},TreeMap::new,Collectors.toList()));
list.stream().collect(Collectors.groupingByConcurrent(e ->{
if (e.equals("1")){
return "group1";
} else if(e.equals("2")){
return "group2";
}
return "group3";
}));
list.stream().collect(Collectors.groupingBy(e ->{
if (e.equals("1")){
return "group1";
} else if(e.equals("2")){
return "group2";
} else {
return "group3";
}
},Collectors.toList()));
list.stream().collect(Collectors.groupingByConcurrent(e ->{
if (e.equals("1")){
return "group1";
} else if(e.equals("2")){
return "group2";
} else {
return "group3";
}
}, ConcurrentHashMap::new,Collectors.toList()));
---------------------------------------------------------------
list.stream().collect(Collectors.partitioningBy(e -> {return e.equals("hello");}));
Map<Boolean, Map<String, List<String>>> collect3 =
list.stream().collect(Collectors.partitioningBy(e -> {
return e.equals("hello");
},
Collectors.groupingBy(e -> {
if (e.equals("group1")) {
return "group1";
}
return "group2";
}
)
)
);