JDK8函数式编程(二)

 Stream.collect

Stream.collect可以认为是增强版的Stream.reduce,collect的使用频率比reduce高.

  • collect  vs  reduce?
    • reduce 操作不可变数据
    • collect 操作可变数据
  • collect(Supplier,Accmulator,Combiner)
  • collect(Collector)

 JDK8函数式编程(二)

 我们来看看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()
上一篇:Linux上安装jdk8


下一篇:JDK15都要来了!我却连JDK8还没玩熟------不妨新特性尝个鲜?