天天看點

Java8 Lambda - groupingBy 後再排序、累加、計數、查找...

package com.noob.testThink;

import com.fasterxml.jackson.annotation.JsonFilter;
import com.google.common.collect.Lists;
import com.noob.json.JSON;
import lombok.Data;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.stream.Collectors;

public class GroupingByDownstreamTest {
    @JsonFilter("ignoreVarFilter")
    @Data
    public static class Person {
        private String realName;
        // 任務類型
        private String taskType;
        private int time;
        private BigDecimal decimal;
    }

    public static void main(String[] args) {
        List<Person> list = Lists.newArrayList();
        for (int index = 0; index < 30; index++) {
            Person person = new Person();
            person.setRealName("RealName" + index % 4);
            person.setTaskType("TaskType" + index % 5);
            person.setTime(index % 6);
            person.setDecimal(new BigDecimal(person.getTime()));
            list.add(person);
        }
        // 累加的方式
        int a = list.stream().collect(Collectors.summingInt(Person::getTime));
        System.out.println(a);

        int b = list.stream().mapToInt(Person::getTime).sum();
        System.out.println(b);

        BigDecimal d = list.stream().map(Person::getDecimal).reduce(BigDecimal::add).get();
        System.out.println(d);

       // list.stream().forEach(t->list.remove(t));  List和Map的stream裡也不能直接remove!!! 都會expectedModCount<初始執行個體化疊代器就固定為目前的modCount修改次數>和modCount(remove操作會++)不一緻抛出ConcurrentModificationException

        // 把list轉成1:1的map.
        //  Map<String, Person> e = list.stream().collect(Collectors.toMap(Person::getRealName, Function.identity()));   
       //  這裡因為資料源重複問題會報錯,如果想支援覆寫,需改寫底層的mergeFunction
        /** Exception in thread "main" java.lang.IllegalStateException: Duplicate key GroupingByDownstreamTest.Person(realName=RealName0, taskType=TaskType0, time=0, decimal=0)
         at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)
         at java.util.HashMap.merge(HashMap.java:1254)
         at java.util.stream.Collectors.lambda$toMap$58(Collectors.java:1320)
         at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
         **/
        Map<String, Person> e = list.stream().collect(Collectors.toMap(Person::getRealName, Function.identity(),(k1, k2)-> k1));
        System.out.println(JSON.toJSONString(e));

        // 驗證JSON動态屏蔽某個參數
        System.out.println(JSON.toJSON(list, Person.class, "realName"));

        // RealName分組并排序 (用LinkedHashMap來儲存Map插入的順序)
        Map<String, List<Person>> collect = list.stream().sorted(Comparator.comparing(Person::getRealName).reversed()).
                collect(Collectors.groupingBy(Person::getRealName, LinkedHashMap::new, Collectors.toList()));
        System.out.println(JSON.toJSONString(collect));

        List<Person> c = collect.values().stream().flatMap(Collection::stream).collect(Collectors.toList());
        //再攤開所有的List
        System.out.println(JSON.toJSONString(c));

        // 先RealName分組 -> 隻取taskType
        Map<String, List<String>> collect1 = list.stream().collect(Collectors.groupingBy(Person::getRealName,
                Collectors.mapping(Person::getTaskType, Collectors.toList())));
        System.out.println(JSON.toJSONString(collect1));

        // 先RealName分組 -> 再統計time
        Map<String, IntSummaryStatistics> collect2 = list.stream().collect(Collectors.groupingBy(Person::getRealName, Collectors.summarizingInt(Person::getTime)));
        System.out.println(JSON.toJSONString(collect2));

        Map<String, BigDecimal> collect9 = list.stream().collect(Collectors.groupingBy(Person::getRealName, Collectors.mapping(Person::getDecimal, Collectors.reducing(BigDecimal.ZERO, (x, m) -> x.add(m)))));
        System.out.println(JSON.toJSONString(collect9));

        // 相同key的value相加
        List<Map<String, BigDecimal>> list2 = com.google.common.collect.Lists.newArrayList(collect9, collect9);

        BinaryOperator<Map<String, BigDecimal>> mapBinaryOperator = (x, m) -> {
            x.forEach((key, value) -> m.compute(key, (key2, oldValue) -> { // Map的merge和compute很像,但merge需要傳入的value不為空,否則merge會報錯; 是以用compute更合适。
                if (oldValue == null) return value;
                if (value == null) return oldValue;
                return value.add(oldValue);
            }));
            return m;  // 傳回最全的map
        };
        Map<String, BigDecimal> collect15 = list2.stream().reduce(mapBinaryOperator).orElse(null);
        System.out.println(JSON.toJSONString(collect15));

        // 先RealName分組 -> 再計數
        Map<String, Long> collect8 = list.stream().collect(Collectors.groupingBy(Person::getRealName, Collectors.counting()));
        System.out.println(JSON.toJSONString(collect8));

        // 先RealName分組 -> 再取最大/最小的time
        Map<String, Person> collect3 = list.stream().collect(Collectors.groupingBy(Person::getRealName,
                Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparing(Person::getTime)), Optional::get)));
        System.out.println(JSON.toJSONString(collect3));

        // RealName分組 -> TaskType分組 -> 按time排序
        Map<String, Map<String, TreeSet<Person>>> collect4 = list.stream().collect(Collectors.groupingBy(Person::getRealName,
                Collectors.groupingBy(Person::getTaskType,
                        Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<Person>(Comparator.comparing(Person::getTime)
                        )), Function.identity()))));
        System.out.println(JSON.toJSONString(collect4));

    }

}
           

 上例中使用了 com.fasterxml.jackson.ObjectMapper 來做JSON , 有動态忽略指定屬性的示例。

/**
     * 忽略指定對象屬性!  非線程安全
     *
     * @param bean 執行個體對象
     * @param ignoreVar 忽略屬性
     * @param cla 執行個體對象的類class
     */
    public static String toJSON(Object bean, Class cla, String... ignoreVar) {
        /**
         * 聲明式,缺點:無法動态指定
         * 1、@JsonIgnore 可以直接放在field上面表示要忽略的filed
         * 2、@JsonIgnoreProperties(value = { "id",  "firstName"}) 類級别忽略特定字段
         * 3、@JsonIgnoreType 忽略整個bean 忽略指定類型class的所有字段
         */
        FilterProvider filterProvider = new SimpleFilterProvider()
                .addFilter("ignoreVarFilter", SimpleBeanPropertyFilter.serializeAllExcept(ignoreVar)); // 定義一個過濾器ignoreVarFilter
        objectMapper.setFilters(filterProvider); // 非線程安全

        /**該過濾器要生效,可以:
         * 第一種方式: 在bean的class上指定: @JsonFilter("ignoreVarFilter") ;
         * 第二種方式: 定義一個聲明過濾器ignoreVarFilter的接口,把它和實體class綁定
         */
       // objectMapper.addMixInAnnotations(cla, JsonFilterMixIn.class);

        return toJSONString(bean);
    }

    @JsonFilter("ignoreVarFilter")
    public  interface JsonFilterMixIn {}