java 函数式编程 之 Stream API (二)

在上一篇文章中,我们介绍了java8 中 stream API 的简单使用,其中包括对于stream流操作的相关方法,我们可以借助它对于集合进行相关的处理,下面要介绍收集器,就是对于上一篇中的处理之后的数据如何安放做出动作,相比较于指令式的代码,函数式的代码目的就更加清晰,表现就更加形象,而且代码的复用能力更强,结合上一篇中的例子,我们先抛出一个简单的收集器的例子,然后我们再进一步分析。

  1. 例子:(1)将数组中的元素加倍并去除中间重复数据。
    一种解法,使用Collectors静态工厂类进行处理,

     Set integerDouble = integers.stream()
             .map(x -> x * 2)
             .collect(Collectors.toSet());
    

可以看到我们使用到了 collect(Collectors.toSet());

查看源码 :
很明显,HashSet::new, Set::add 等函数引用是对于添加元素到Set集合中的操作。

public static 
Collector> toSet() {
    return new CollectorImpl<>((Supplier>) HashSet::new, Set::add,
                               (left, right) -> { left.addAll(right); return left; },
                               CH_UNORDERED_ID);
}

再进一步,返回了一个CollectImpl对象,带入参数都作为其构造函数的参数。

CollectorImpl(Supplier supplier,
                  BiConsumer accumulator,
                  BinaryOperator combiner,
                  Set characteristics) {
        this(supplier, accumulator, combiner, castingIdentity(), characteristics);
    }

可以看到suppiler -> 数据源提供者
BiConsumer -> 数据消费者 -> 用来提供数据接收和数据
BinaryOperator -> 数据处理器 -> 集合数据
Set -> 数据特性 -> 是否是排序的等等

那么在对于每一次数据流的到来,收集器就按照上面CollectImpl返回对象对数据进行归约操作,最终得到一个Set 集合。

  1. 有哪些常用的收集器?
    (1) Collectors 自带简单型
    Collectors.counting(), 更多请参见Collectors源码

     integers.stream()
             .collect(Collectors.counting()).intValue();
    

(2)自带传参型

如下面这个:如何使用它,选出最大值,数据源是

public static  Collector>
maxBy(Comparator comparator) {
    return reducing(BinaryOperator.maxBy(comparator));
}

实现Comparator :

StringList.stream().collect(Collectors.maxBy(
            (Comparator) (o1, o2) -> {
                int o1_lengh = o1.split(" ").length;
                int o2_lengh = o2.split(" ").length;
                if (o1_lengh > o2_lengh) {
                    return 1;
                } else if (o1_lengh == o2_lengh){
                    return 0;
                }else {
                    return -1;
                }
            }
    )).ifPresent(s -> System.out.println(s));

(3) 分组
我们经常在sql 里面会使用到group by, 那么java8 中有没有提供类似的操作? 使得我们不用命令式编程就可以完成分组操作,答案是有,下面就是一个简单的例子:按照所在城市进行分组,当然也可以进行多分组,需要对上一次分组进行嵌套。

  Map> listMap = personList.stream()
            .collect(Collectors
                    .groupingBy(p -> p.getCity()
                    )
            );

    listMap.forEach((x,y) -> {
        System.out.println(x);
        y.stream().forEach(z -> System.out.println(z.getName()));
    });

(2)分区
按业务条件进行分区,一般就是true or false.

          Map> personLists =               
                personList.stream().collect(
            Collectors.partitioningBy(x -> "Shanghai".equalsIgnoreCase(x.getCity())));
  1. 如何自己实现收集器
    首先我们需要研究下Collectors.
    查看源码,可以发现实际上起到作用的是其内部类 CollectorImpl, 这个类实现了
    Collector 接口,我们可以照样画葫芦,把我们的业务逻辑实现在该实现类中,实现业务需要的收集器实现类。
public static >
Collector toCollection(Supplier collectionFactory) {
    return new CollectorImpl<>(collectionFactory, Collection::add,
                               (r1, r2) -> { r1.addAll(r2); return r1; },
                               CH_ID);
}

public static 
Collector> toList() {
    return new CollectorImpl<>((Supplier>) ArrayList::new, List::add,
                               (left, right) -> { left.addAll(right); return left; },
                               CH_ID);
}

具体:

static class CollectorImpl implements Collector {
    private final Supplier supplier;
    private final BiConsumer accumulator;
    private final BinaryOperator combiner;
    private final Function finisher;
    private final Set characteristics;

    CollectorImpl(Supplier supplier,
                  BiConsumer accumulator,
                  BinaryOperator combiner,
                  Function finisher,
                  Set characteristics) {
        this.supplier = supplier;
        this.accumulator = accumulator;
        this.combiner = combiner;
        this.finisher = finisher;
        this.characteristics = characteristics;
    }

    CollectorImpl(Supplier supplier,
                  BiConsumer accumulator,
                  BinaryOperator combiner,
                  Set characteristics) {
        this(supplier, accumulator, combiner, castingIdentity(), characteristics);
    }

    @Override
    public BiConsumer accumulator() {
        return accumulator;
    }

    @Override
    public Supplier supplier() {
        return supplier;
    }

    @Override
    public BinaryOperator combiner() {
        return combiner;
    }

    @Override
    public Function finisher() {
        return finisher;
    }

    @Override
    public Set characteristics() {
        return characteristics;
    }
}

因此如果我们要实现一个集合器的话,那我们就要实现对应的各个部分:

                  Supplier supplier,
                  BiConsumer accumulator,
                  BinaryOperator combiner,
                  Function finisher,
                  Set characteristics

首先让我们给自己提出一个需求: 收集年龄大于超过23的人,因此这边我们可以定义一下收集器。

public interface Collector
其中T,A,R 分别表示

     * @param  the type of input elements to the reduction operation
     * @param  the mutable accumulation type of the reduction operation         
       (often hidden as an implementation detail)
     * @param  the result type of the reduction operation

我们传入;
(1)suppiler : 数据源的提供者。

@Override
public Supplier> supplier() {
    return () -> {
        return new ArrayList();
    };
}

(2)accumulator : 数据消费则

 @Override
public BiConsumer, Integer> accumulator() {
    return (List list, Integer ele) -> {
        if(isBiggerThan(ele).test(ele)){
            list.add(ele);
        }
    };
}

(3)combiner : 数据操作器

@Override
public BinaryOperator> combiner() {
    return (List x ,List y) -> {
        x.addAll(y);
        return x;
    };
}

(4)Finisher: 结束者

@Override
public Function, List> finisher() {
    return Function.identity();
}

(5)characteristics : 集合器特性

 @Override
public Set characteristics() {
    return Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH));
}

那接下来调用:

    List ageList = Lists.newArrayList(23,4,5,2,67,42);
    ageList.stream().collect(new AgeCollectors()).forEach(System.out::println);

到这边为止,我们就构造了一个简单的自定义的收集器。

你可能感兴趣的:(java 函数式编程 之 Stream API (二))