java8 自定義收集器Collector
阿新 • • 發佈:2019-02-08
Collectors類的一些靜態方法
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collector.Characteristics;
import java.util.stream.Collectors;
import org.junit.Test;
import com.cbzk.lambda.Employee;
import com.cbzk.lambda.Employee.Status;
/*
* 自定義收集器
* 需要重寫collector介面
* public interface Collector<T, A, R> {
Supplier<A> supplier();
BiConsumer<A, T> accumulator();
Function<A, R> finisher(); // 最後要做的轉換操作
BinaryOperator<A> combiner();
Set<Characteristics> characteristics();
}
T要收集專案的泛型
A累加器的型別
R收集操作得到的物件(不一定是集合)
Characteristics是一個包含三個專案的列舉。
UNORDERED——歸約結果不受流中專案的遍歷和累積順序的影響。
CONCURRENT——accumulator函式可以從多個執行緒同時呼叫,且該收集器可以並行歸
約流。如果收集器沒有標為UNORDERED, 那它僅在用於無序資料來源時才可以並行歸約。
IDENTITY_FINISH——這表明完成器方法返回的函式是一個恆等函式,可以跳過。
這種情況下,累加器物件將會直接用作歸約過程的最終結果。這也意味著,將累加器A不加檢
查地轉換為結果R是安全的。
我們迄今開發的ToListCollector是IDENTITY_FINISH的,因為用來累積流中元素
List已經是我們要的最終結果,用不著進一步轉換了,但它並不是UNORDERED,因為用在有序
流上的時候,我們還是希望順序能夠保留在得到的List中。最後,它是CONCURRENT的,但我們
剛才說過了,僅僅在背後的資料來源無序時才會並行處理
// 通過collect程式碼認識一下:
if (isParallel()
&& (collector.characteristics().contains(Collector.Characteristics.CONCURRENT))
&& (!isOrdered() || collector.characteristics().contains(Collector.Characteristics.UNORDERED))) {
//併發
}
即併發條件:
在Collector.Characteristics.CONCURRENT的基礎上,收集器Collector具有Characteristics.UNORDERED特性或者原始資料是無序的時才應用併發;
String concat = stringStream.collect(StringBuilder::new, StringBuilder::append, StringBuilder::append).toString();
發現了一個小現象
在使用java8的lambda時,
有的編譯報錯並不是因為你寫的有問題,而是你沒有寫返回值造成的;
比如: HashMap<String, Integer> map3 = emps.stream()
.collect(HashMap::new, (m, emp) -> m.put(emp.getName(), emp.getAge()),HashMap::putAll);
直接寫 emps.stream()
.collect(HashMap::new, (m, emp) -> m.put(emp.getName(), emp.getAge()),HashMap::putAll);
就會報錯,編譯器不認識m到底是什麼,估計跟前面的 HashMap<String, Integer>推斷有關;
參考:
http://blog.jobbole.com/104067/
*/
public class StreamDemo6 {
List<Employee> emps = Arrays.asList(
new Employee(102, "李四aa", 59, 6666.66, Status.BUSY),
new Employee(102, "李四aaa", 60, 6666.66, Status.BUSY),
new Employee(101, "張三bb", 18, 9999.99, Status.FREE),
new Employee(103, "王五cc", 28, 3333.33, Status.VOCATION),
new Employee(104, "趙六dd", 8, 7777.77, Status.BUSY),
new Employee(104, "趙六dd", 9, 7777.77, Status.FREE),
new Employee(104, null, 25, 7777.77, Status.FREE),
new Employee(105, "麗麗", null, 5555.55, Status.BUSY)
);
//實現toList的功能
//順序流
@Test
public void test() {
List<Employee> res = emps.stream()
.limit(2)
.collect(Collectors.toList());
//方式1 自定義收集器
List<Employee> res2 = emps.stream()
.limit(2)
.collect(new MyToList<Employee>());
System.out.println(res2);
//方式2 重寫collect方法的三個引數
//這種直接寫的不靈活,而且不容易看懂,且不能設定屬性,其屬性預設為IDENTITY_FINISH,CONCURRENT
// collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);
//注意:第三個合併引數,Collector裡面是BinaryOperator,而collect裡面是BiConsumer
//所以可以直接
List<Employee> res3 = emps.stream()
.limit(2)
// .collect(ArrayList::new, List::add,(list1,list2)->list1.addAll(list2));
.collect(ArrayList::new,List::add,List::addAll);
System.out.println(res3);
//方式3:使用Collector.of 這個可以理解成自定義收集器的 完整封裝版
List<Employee> res4=emps.stream()
.limit(2)
.collect(Collector.of(ArrayList::new, List::add
, (list1,list2)->{list1.addAll(list2);
return list1;}
, new Characteristics[] {Characteristics.IDENTITY_FINISH,Characteristics.CONCURRENT}));
System.out.println(res4);
}
//並行流
// list集合本身是有序的,所以並行不起來
@Test
public void test1() {
List<Employee> res = emps.stream()
.limit(2)
.parallel()
.collect(Collectors.toList());
System.out.println(res);
List<Employee> res2 = emps.stream()
.parallel()
.collect(new MyToList<Employee>());
System.out.println(res2);
List<Employee> res3 = emps.stream()
.parallel()
.collect(ArrayList::new,List::add,List::addAll);
System.out.println(res3);
}
//實現tomap的功能
//順序流
@Test
public void test2() {
//一般轉換map
//它存在2個問題,1.對於重複key報錯 2.對於vlaue為null報錯
// Map<String, Integer> map1 = emps.stream()
// .collect(Collectors.toMap(Employee::getName, Employee::getAge));
// System.out.println(map1);
//解決方式1:使用collect的三個入參 自定義收集器
Map<String,Integer> map2= emps.stream()
.collect(HashMap::new,( map, emp) ->
map.put(emp.getName(),emp.getAge()),Map::putAll);
System.out.println(map2);
//解決方式2:自已自定義收集器
Map<String, Integer> map3 = emps.stream()
.collect(MyToMap.toMap(Employee::getName, Employee::getAge));
System.out.println(map3);
//因為toMap方法裡面使用merge方法,不允許value為null的,所以要使其避免那2個問題,必須繞開merge方法
//解決方式3 使用 Collector.of方法 其實就是方式2的簡寫版
//當使用順序流使用時,(hmap1,hmap2)->{hmap1.putAll(hmap2);return hmap1; 沒有作用,可以直接拋異常處理
//當使用並行流時,(hmap1,hmap2)->{hmap1.putAll(hmap2);return hmap1;就需要這句,但並不一定是並行
Map<String,Integer> map4=emps.stream()
.collect(Collector.of(HashMap::new, ( map, emp) -> map.put(emp.getName(),emp.getAge())
, (hmap1,hmap2)->{ throw new UnsupportedOperationException();}
, new Characteristics[]{Characteristics.IDENTITY_FINISH}));
System.out.println(map4);
//小結:上面的解決方式本質都是:自定義構造器
}
//並行流
//合併函式需要這句:(hmap1,hmap2)->{hmap1.putAll(hmap2);return hmap1;
@Test
public void test2_1() {
//方式1
Map<String,Integer> map1= emps.stream()
.parallel()
.collect(HashMap::new,( map, emp) -> map.put(emp.getName(),emp.getAge()),Map::putAll);
System.out.println(map1);
//方式2
Map<String, Integer> map2 = emps.stream()
.parallel()
.collect(MyToMap.toMap(Employee::getName, Employee::getAge,(hmap1,hmap2)->{hmap1.putAll(hmap2);return hmap1;}));
System.out.println(map2);
//方式3
Map<String,Integer> map3=emps.stream()
.parallel()
.collect(Collector.of(HashMap::new, ( map, emp) -> map.put(emp.getName(),emp.getAge())
, (hmap1,hmap2)->{hmap1.putAll(hmap2);return hmap1;}
, new Characteristics[]{Characteristics.IDENTITY_FINISH}));
System.out.println(map3);
}
}
//如果MyToList不帶泛型,那麼後面的引數就是具體的類了
class MyToList<T> implements Collector<T, List<T>, List<T>>{
//初始化
@Override
public Supplier<List<T>> supplier() {
// return ()->new ArrayList<>();
return ArrayList::new;
}
//累計遍歷,累加器
@Override
public BiConsumer<List<T>, T> accumulator() {
return List::add;
}
//合併結果
@Override
public BinaryOperator<List<T>> combiner() {
return (list1,list2)->{
list1.addAll(list2);
return list1;
};
}
//對結果進行處理
//這裡直接返回
@Override
public Function<List<T>, List<T>> finisher() {
// return e->e;
return Function.identity();
}
//設定屬性
@Override
public Set<Characteristics> characteristics() {
return Collections.unmodifiableSet(EnumSet.of(Characteristics.CONCURRENT,Characteristics.IDENTITY_FINISH));
}
}
//
class EasyToMapCollector<T,K, V> implements Collector<T, Map<K, V>, Map<K, V>> {
private Function<? super T, ? extends K> keyMapper;
private Function<? super T, ? extends V> valueMapper;
private BinaryOperator<Map<K, V>> binOp;
//2個引數的,只能是順序流
public EasyToMapCollector(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends V> valueMapper) {
// this.keyMapper = keyMapper;
// this.valueMapper = valueMapper;
this(keyMapper,valueMapper,null);
}
public EasyToMapCollector(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends V> valueMapper, BinaryOperator<Map<K, V>> binOp) {
this.keyMapper = keyMapper;
this.valueMapper = valueMapper;
this.binOp=binOp;
}
@Override
public BiConsumer<Map<K, V>, T> accumulator() {
return (map, element) -> map.put(keyMapper.apply(element), valueMapper.apply(element));
//如果同一個key對應多個value,想讓key匹配那個部位null的value,可以用
// return (map,element)->map.putIfAbsent(keyMapper.apply(element), valueMapper.apply(element));
}
@Override
public Supplier<Map<K, V>> supplier() {
return HashMap::new;
}
@Override
public BinaryOperator<Map<K, V>> combiner() {
// return null;
//如果不用這個方法最好是拋異常
// throw new UnsupportedOperationException();
return binOp;
}
@Override
public Function<Map<K, V>, Map<K, V>> finisher() {
return Function.identity();
}
@Override
public Set<Characteristics> characteristics() {
return Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));
}
}
final class MyToMap {
public static <T,K, V> Collector<T, ?, Map<K, V>> toMap(Function<T, K> f1, Function<T, V> f2) {
return new EasyToMapCollector<T,K,V>(f1, f2);
}
public static <T,K, V> Collector<T, ?, Map<K, V>> toMap(Function<T, K> f1, Function<T, V> f2,BinaryOperator<Map<K, V>> binOp) {
return new EasyToMapCollector<T,K,V>(f1, f2,binOp);
}
}