Stream.collect
Stream.collect可以认为是增强版的Stream.reduce,collect的使用频率比reduce高.
- collect vs reduce?
- reduce 操作不可变数据
- collect 操作可变数据
- collect(Supplier,Accmulator,Combiner)
- collect(Collector)
我们来看看Collector
- Collector要素
- Supplier:累积数据构造函数
- Accumulator:累积函数,同reduce
- Combiner:合并函数,并行处理场合下用,同reduce
- Finisher:对累积数据做最终转换
- *Characteristics:特征(并发/无序/无finisher)
- Collector需要满足:
- 同一律:Combiner.apply(acc,[])==acc
- 结合律:
- acc.accept(t1),acc.accept(t2) acc1.accept(t1),acc2.accept(t2)
- finisher.apply(acc) == finisher.apply(combiner.apply(t1,t2))
由于Collector的复杂性,官方为我们提供了一个接口Collectors来满足我们的日常需要,我们来看看Collectors API
Collectors API
- toList/to(Concurrent)Map/toSet/toCollection
- counting/averagingXX/joining/summingXX
- groupBy/partitioningBy
- mapping/reducing
我们这边重点讲解Collectors.groupBy这个功能,工作中应该有很多种情况要对List中的元素进行分组,而我们一般会写很多个for循环,if条件,但是使用这个groupby功能就可以是代码变得美观
Collectors.groupBy
- groupingBy(Function) - 单纯分key存放成Map<key,List>,默认使用HashMap;
- groupingBy(Function,Collector) - 分key后,对每个key的元素进行后续collect操作,例如分完类后进行一个统计Collectors.counting,进行一个再分组
- groupingBy(Function,Supplier,Collector) - 同上,允许自定义Map创建,通过Suppiler可以选择自定义Map,例如LinkHashMap,ConcurrentHashMap等Map.
我们先来看一个最简单的例子,groupIngBy(Function)
1 public static void main(String[] args) { 2 //随机生成10个Programmer对象存入 3 List<Programmer> programmers = Util.generate(10); 4 //按Level区分 5 Map<Integer, List<Programmer>> collect = programmers.stream().collect(Collectors.groupingBy(Programmer::getLevel));//Programmer::getLevel == programmer->programmer.getLevel() 6 System.out.println("上面是随机生成的对象,下面是分组后的collect对象"); 7 System.out.println(collect); 8 }
{ name=Programmer83, level=0, salary=36099, output=6, language=JS } { name=Programmer70, level=1, salary=39350, output=3, language=Lisp } { name=Programmer56, level=1, salary=44782, output=1, language=Lisp } { name=Programmer66, level=1, salary=37745, output=4, language=JAVA } { name=Programmer22, level=1, salary=49804, output=4, language=Haskell } { name=Programmer58, level=1, salary=14527, output=8, language=Haskell } { name=Programmer61, level=1, salary=10411, output=6, language=Haskell } { name=Programmer66, level=1, salary=13320, output=0, language=JAVA } { name=Programmer21, level=2, salary=3170, output=3, language=Lisp } { name=Programmer28, level=2, salary=9186, output=5, language=Lisp } { name=Programmer92, level=2, salary=31375, output=8, language=JS } 上面是随机生成的对象,下面是分组后的collect对象 {0=[{ name=Programmer83, level=0, salary=36099, output=6, language=JS }], 1=[{ name=Programmer70, level=1, salary=39350, output=3, language=Lisp }, { name=Programmer56, level=1, salary=44782, output=1, language=Lisp }, { name=Programmer66, level=1, salary=37745, output=4, language=JAVA }, { name=Programmer22, level=1, salary=49804, output=4, language=Haskell }, { name=Programmer58, level=1, salary=14527, output=8, language=Haskell }, { name=Programmer61, level=1, salary=10411, output=6, language=Haskell }, { name=Programmer66, level=1, salary=13320, output=0, language=JAVA }], 2=[{ name=Programmer21, level=2, salary=3170, output=3, language=Lisp }, { name=Programmer28, level=2, salary=9186, output=5, language=Lisp }, { name=Programmer92, level=2, salary=31375, output=8, language=JS }]}
我们再来看一个groupingBy(Function,Collector)
1 public static void main(String[] args) { 2 //随机生成10个Programmer对象存入 3 List<Programmer> programmers = Util.generate(10); 4 //按Level区分,再按language区分 5 Map<Integer, Map<String, List<Programmer>>> collect = programmers.stream().collect( 6 Collectors.groupingBy( 7 Programmer::getLevel, 8 Collectors.groupingBy(Programmer::getLanguage) 9 ) 10 );//Programmer::getLevel == programmer->programmer.getLevel() 11 System.out.println("上面是随机生成的对象,下面是分组后的collect对象"); 12 System.out.println(collect); 13 }
分组后的结果
{ 0 = { Haskell = [{ name = Programmer47, level = 0, salary = 39509, output = 0, language = Haskell }], JS = [{ name = Programmer58, level = 0, salary = 18192, output = 3, language = JS }, { name = Programmer41, level = 0, salary = 49684, output = 8, language = JS }] }, 1 = { JS = [{ name = Programmer52, level = 1, salary = 42060, output = 8, language = JS }, { name = Programmer54, level = 1, salary = 19767, output = 4, language = JS }], Lisp = [{ name = Programmer95, level = 1, salary = 37944, output = 5, language = Lisp }] }, 2 = { JAVA = [{ name = Programmer96, level = 2, salary = 27737, output = 9, language = JAVA }], JS = [{ name = Programmer99, level = 2, salary = 12501, output = 4, language = JS }], Haskell = [{ name = Programmer40, level = 2, salary = 48711, output = 7, language = Haskell }, { name = Programmer84, level = 2, salary = 35680, output = 4, language = Haskell }], Lisp = [{ name = Programmer84, level = 2, salary = 16699, output = 3, language = Lisp }] } }
Collectors.groupBy的返回值一定是一个Map对象,我们关注的是这个Map的key是什么,从而得到我们所需要的分组.
Optional
在JDK8中的地位stream不相上下,Optional主要解决空指针问题,相信我们在开发中会经常碰到NullPointException问题,很多时候我们需要加很多if判断,举个例子
1 private void test(Person person){ 2 //正常的调用 3 if(person!=null){ 4 if(person.getDebit()!=null){ 5 if(person.getDebit().getCard()!=null){ 6 if(person.getDebit().getCard().getBank()!=null){ 7 String bankName1=person.getDebit().getCard().getBank().getName()); 8 } 9 } 10 } 11 } 12 //Optional调用方法 13 String bankName = Optional.ofNullable(person) 14 .map(Person::getDebit) 15 .map(Debit::getCard) 16 .map(Card::getBank) 17 .map(Bank::getName) 18 .orElse(null); 19 20 }
不使用Optional的代码就真的是又长又臭,但是不加上面的if判断,又会引发NullPointException.
Optional API
- orElse(T) -->if x!=null return x else return T
- orElseGet(fn) -->if x!=null return x else return fn()
- ifPresent(fn) -->if x!=null fn()