java小技巧(四)--利用lambada 对list进行分组汇总
对一个List进行分组汇总的lambada写法:
list.stream().collect(Collectors.groupingBy(TestDTO::getId, Collectors.summarizingInt(TestDTO::getValue)));
上面TestDTO::getValue是进行整形汇总,但是如果TestDTO::getValue是BigDecimal类型的怎么办,写法如下:
list.stream().collect(Collectors.groupingBy(TestDTO::getId, Collectors.reducing(BigDecimal.ZERO, TestDTO::getValue, BigDecimal::add)));
多个list合并成一个list
比如有下面数据结构:
List<List<String>> list = new ArrayList<>(); List<String> sub1 = new ArrayList<>(); sub1.add("1111"); sub1.add("2222"); sub1.add("3333"); sub1.add("4444"); list.add(sub1); List<String> sub2 = new ArrayList<>(); sub2.add("aaaa"); sub2.add("bbbb"); sub2.add("cccc"); sub2.add("dddd"); list.add(sub2); List<String> sub3 = new ArrayList<>(); sub3.add("a001"); sub3.add("b002"); sub3.add("c003"); sub3.add("d004"); list.add(sub3);
合并成一个List的写法:
List<String> myList = list.stream().flatMap(Collection::stream).collect(Collectors.toList());
求平均值:
list.stream().collect(Collectors.groupingBy(TestDTO::getId, Collectors.averagingInt(TestDTO::getValue)));
如果TestDTO::getValue是BigDecimal类型的,则不能这么用,需要自己写一个工具类:
1 @FunctionalInterface 2 public interface ToBigDecimalFunction<T> { 3 4 BigDecimal applyAsBigDecimal(T value); 5 }
1 public final class CustomCollectors { 2 3 private static final Set<Collector.Characteristics> CH_NOID = Collections.emptySet(); 4 5 private CustomCollectors() {} 6 7 8 @SuppressWarnings("unchecked") 9 private static <I, R> Function<I, R> castingIdentity() { 10 return i -> (R) i; 11 } 12 13 /** 14 * Simple implementation class for {@code Collector}. 15 * 16 * @param <T> the type of elements to be collected 17 * @param <R> the type of the result 18 */ 19 @SuppressWarnings("hiding") 20 static class CollectorImpl<T, A, R> implements Collector<T, A, R> { 21 private final Supplier<A> supplier; 22 private final BiConsumer<A, T> accumulator; 23 private final BinaryOperator<A> combiner; 24 private final Function<A, R> finisher; 25 private final Set<Characteristics> characteristics; 26 27 CollectorImpl(Supplier<A> supplier, 28 BiConsumer<A, T> accumulator, 29 BinaryOperator<A> combiner, 30 Function<A,R> finisher, 31 Set<Characteristics> characteristics) { 32 this.supplier = supplier; 33 this.accumulator = accumulator; 34 this.combiner = combiner; 35 this.finisher = finisher; 36 this.characteristics = characteristics; 37 } 38 39 CollectorImpl(Supplier<A> supplier, 40 BiConsumer<A, T> accumulator, 41 BinaryOperator<A> combiner, 42 Set<Characteristics> characteristics) { 43 this(supplier, accumulator, combiner, castingIdentity(), characteristics); 44 } 45 46 @Override 47 public BiConsumer<A, T> accumulator() { 48 return accumulator; 49 } 50 51 @Override 52 public Supplier<A> supplier() { 53 return supplier; 54 } 55 56 @Override 57 public BinaryOperator<A> combiner() { 58 return combiner; 59 } 60 61 @Override 62 public Function<A, R> finisher() { 63 return finisher; 64 } 65 66 @Override 67 public Set<Characteristics> characteristics() { 68 return characteristics; 69 } 70 } 71 72 //求和方法 73 public static <T> Collector<T, ?, BigDecimal> summingBigDecimal(ToBigDecimalFunction<? super T> mapper) { 74 return new CollectorImpl<>( 75 () -> new BigDecimal[]{new BigDecimal(0)}, 76 (a, t) -> { a[0] = a[0].add(mapper.applyAsBigDecimal(t), MathContext.DECIMAL32); }, 77 (a, b) -> { a[0] = a[0].add(b[0], MathContext.DECIMAL32) ; return a; }, 78 a -> a[0], CH_NOID); 79 } 80 81 82 //求最大,这里的最小MIN值,作为初始条件判断值,如果某些数据范围超过百亿以后,可以根据需求换成Long.MIN_VALUE或者Double.MIN_VALUE 83 public static <T> Collector<T, ?, BigDecimal> maxBy(ToBigDecimalFunction<? super T> mapper) { 84 return new CollectorImpl<>( 85 () -> new BigDecimal[]{new BigDecimal(Integer.MIN_VALUE)}, 86 (a, t) -> { a[0] = a[0].max(mapper.applyAsBigDecimal(t)); }, 87 (a, b) -> { a[0] = a[0].max(b[0]) ; return a; }, 88 a -> a[0], CH_NOID); 89 } 90 91 //求最小,这里的最大MAX值,作为初始条件判断值,如果某些数据范围超过百亿以后,可以根据需求换成Long.MAX_VALUE或者Double.MAX_VALUE 92 public static <T> Collector<T, ?, BigDecimal> minBy(ToBigDecimalFunction<? super T> mapper) { 93 return new CollectorImpl<>( 94 () -> new BigDecimal[]{new BigDecimal(Integer.MAX_VALUE)}, 95 (a, t) -> { a[0] = a[0].min(mapper.applyAsBigDecimal(t)); }, 96 (a, b) -> { a[0] = a[0].min(b[0]) ; return a; }, 97 a -> a[0], CH_NOID); 98 } 99 100 /** 101 * 返回一个平均值 102 * @param newScale 保留小数位数 103 * @param roundingMode 小数处理方式 104 * #ROUND_UP 进1 105 * #ROUND_DOWN 退1 106 * #ROUND_CEILING 进1截取:正数则ROUND_UP,负数则ROUND_DOWN 107 * #ROUND_FLOOR 退1截取:正数则ROUND_DOWN,负数则ROUND_UP 108 * #ROUND_HALF_UP >=0.5进1 109 * #ROUND_HALF_DOWN >0.5进1 110 * #ROUND_HALF_EVEN 111 * #ROUND_UNNECESSARY 112 */ 113 //求平均,并且保留小数 114 public static <T> Collector<T, ?, BigDecimal> averagingBigDecimal(ToBigDecimalFunction<? super T> mapper, int newScale, int roundingMode) { 115 return new CollectorImpl<>( 116 () -> new BigDecimal[]{new BigDecimal(0),new BigDecimal(0)}, 117 (a, t) -> { a[0] = a[0].add(mapper.applyAsBigDecimal(t)); a[1] = a[1].add(BigDecimal.ONE); }, 118 (a, b) -> { a[0] = a[0].add(b[0]) ; return a; }, 119 a -> a[0].divide(a[1],MathContext.DECIMAL32).setScale(newScale,roundingMode), CH_NOID); 120 } 121 }
那么针对BigDecimal类型的求平均值可以写成如下:
1 Map<Integer, BigDecimal> result1 = list.stream().collect(Collectors.groupingBy(TestDTO::getId, CustomCollectors.averagingBigDecimal(TestDTO::getValue, 2, ROUND_DOWN)));