Java8 中 Stream 的使用

olinonee大约 14 分钟java编程jdk8Stream

1.如何将 List 转 Map(Collectors.toMap)

在实际项目中我们经常会用到 List 转 Map 操作,在过去我们可能使用的是 for 循环遍历的方式。举个例子:

先定义类:

// 简单对象
@Accessors(chain = true) // 链式方法
@Data
class User {
    private String id;
    private String name;
}

然后有这样一个 List:

List<User> userList=Lists.newArrayList(
        new User().setId("A").setName("张三"),
        new User().setId("B").setName("李四"),
        new User().setId("C").setName("王五")
        );

我们希望转成 Map 的格式为:

A-> 张三
B-> 李四
C-> 王五

过去的做法(循环):

Map<String, String> map=new HashMap<>();
for(User user:userList){
    map.put(user.getId(), user.getName());
}

使用 Java8 特性

Java8 中新增了 Stream 特性,使得我们在处理集合操作时更方便了。

以上述例子为例,我们可以一句话搞定:

userList.stream().collect(Collectors.toMap(User::getId, User::getName));

当然,如果希望得到 Map 的 value 为对象本身时,可以这样写:

userList.stream().collect(Collectors.toMap(User::getId, t -> t));

或:

userList.stream().collect(Collectors.toMap(User::getId, Function.identity()));

关于 Collectors.toMap 方法

Collectors.toMap 有三个重载方法:

toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper);
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction);
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier);

参数含义分别是:

  1. keyMapper:Key 的映射函数
  2. valueMapper:Value 的映射函数
  3. mergeFunction:当 Key 冲突时,调用的合并方法
  4. mapSupplier:Map 构造器,在需要返回特定的 Map 时使用

还是用上面的例子,如果 ListuserId 有相同的,使用上面的写法会抛异常:

List<User> userList = Lists.newArrayList(
new User().setId("A").setName("张三"),
new User().setId("A").setName("李四"), // Key 相同
new User().setId("C").setName("王五")
);
userList.stream().collect(Collectors.toMap(User::getId, User::getName));
// 异常:
java.lang.IllegalStateException: Duplicate key 张三
at java.util.stream.Collectors.lambda$throwingMerger$114(Collectors.java:133)
at java.util.HashMap.merge(HashMap.java:1245)
at java.util.stream.Collectors.lambda$toMap$172(Collectors.java:1320)
at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
at Test.toMap(Test.java:17)
...

这时就需要调用第二个重载方法,传入合并函数,如:

userList.stream().collect(Collectors.toMap(User::getId, User::getName, (n1, n2) -> n1 + n2));

// 输出结果:
A-> 张三李四
C-> 王五

第四个参数(mapSupplier)用于自定义返回 Map 类型,比如我们希望返回的 Map 是根据 Key 排序的,可以使用如下写法:

List<User> userList = Lists.newArrayList(
new User().setId("B").setName("张三"),
new User().setId("A").setName("李四"),
new User().setId("C").setName("王五")
);

userList.stream().collect(Collectors.toMap(User::getId, User::getName, (n1, n2) -> n1, TreeMap::new));
// 输出结果:
A-> 李四
B-> 张三
C-> 王五

2.如何将 List<Map> 多个 map 对象相同的 Key 分组并合并重组数据 (*)

假设我们有如下数据,如何将其相同的key进行分组并合并重组?

List<Map>_1

图1
图1

List<Map>_2

图2
图2

如何将 2 个数据进行以 create_dept 进行分组,并合并 jobCountswapCount 列,然后进行数据返回。 传统的写法为(提供思路):

1.将 2 张截图的数据进行合并,用一个 hashSet 来存放 createDept(key); 2.将合并之后的数据以 createDept 的值进行分组; 3.将进行分组的数据作为 key,合并的数据作为 value, 4.遍历对应的 key 的 value,用一个新的 map 去重组数据; 5.还需要判断 key 是否存在 value 的 map 中,不存在取 0,同时进行 put 操作,返回此结果。

Stream中的写法:

// 需要进行合并的数据
private List<Map<String, Object>> getUnionMapList(List<Map<String, Object>> jobCountWithDept,

List<Map<String, Object>> swapCountWithDept) {

    List<Map<String, Object>> unionMapList = new ArrayList<>(Collections.emptyList());
    Set<String> set = new HashSet<>();
    if (CollUtil.isNotEmpty(jobCountWithDept)) {
        unionMapList.addAll(jobCountWithDept);
    }
    if (CollUtil.isNotEmpty(swapCountWithDept)) {
        unionMapList.addAll(swapCountWithDept);
    }

    if (CollUtil.isNotEmpty(unionMapList)) {
        unionMapList = unionMapList.stream()
                .filter(map -> map.get("createDept") != null)
                .collect(Collectors.groupingBy(o -> {
                    // 暂存所有的key
                    set.addAll(o.keySet());
                    // 对创建部门进行分组
                    return Convert.toLong(o.get("createDept"));
                })).values().stream().map(mapList -> {
                    // 合并,当key冲突了,取任意一个
                    Map<String, Object> map = mapList.stream()
                            .flatMap(m -> m.entrySet().stream())
                            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> b));

                    // 如果在合并的map中,不存在对应的创建部门(key)的数据,将对应的数据以创建部门作为key,0作为value
                    set.forEach(k -> {
                        if (!map.containsKey(k)) {
                            map.put(k, 0);
                        }
                    });
                    return map;
                }).collect(Collectors.toList());
    }
    return unionMapList;
}

3.List 集合里面存储 Map,对 Map 里面的某个属性进行排序(*)

在多表查询的过程中常常会遇到查询多个字段组成的集合,并且还要对结果集合里面的某个属性进行排序返回。

List<Map<String, Object>> mapList = new ArrayList();

//排序(传统写法)
if (mapList != null && mapList.size() > 1) {
    Collections.sort(mapList , new Comparator<Map<String, Object>>() {
       @Override
       public int compare(Map<String, Object> o1, Map<String, Object> o2) {
           Integer o1Value = Integer.valueOf(o1.get("count").toString());
           Integer o2Value = Integer.valueOf(o2.get("count").toString());
           // 降序
           return o2Value.compareTo(o1Value);
       }
    });
}

// --------------------------------
// 推荐写法(降序)
mapList.sort((o1,o2)->NumberUtil.compare(Convert.toInt(o1.get("count")), Convert.toInt(o2.get("count"))));

4.Java8-使用 stream.sorted() 对 List 和 Map 排序

java8 中,Comparator() 是一个函数式接口,可以使用 Lambda 表达式实现;

Stream sorted(Comparator<? super T> comparator);

@Data
@AllArgsConstructor
public class DailyDataChartVo {

    /**
     * 日期
     */
    private LocalDate date;

    /**
     * 今日营收
     */
    private BigDecimal revenue;

}

List 排序

按日期排序

List<DailyDataChartVo> list = list.stream()
.sorted(Comparator.comparing(DailyDataChartVo::getDate))
.collect(Collectors.toList());

按日期排序后,逆序

List<DailyDataChartVo> list = list.stream()
.sorted(Comparator.comparing(DailyDataChartVo::getDate).reversed())
.collect(Collectors.toList());

按日期排序后,再按金额排序

List<DailyDataChartVo> list = list.stream()
.sorted(Comparator.comparing(DailyDataChartVo::getDate)
.thenComparing(DailyDataChartVo::getRevenue))
.collect(Collectors.toList());

按金额排序,排序时过滤 Null 值(如果排序的字段为 null,NPE)

List<DailyDataChartVo> list = list.stream()
.filter(c -> Objects.nonNull(c.getRevenue()))
.sorted(Comparator.comparing(DailyDataChartVo::getRevenue))
.collect(Collectors.toList());

按金额排序,Null 值排在最前面

List<DailyDataChartVo> list = list.stream()
.sorted(Comparator.comparing(DailyDataChartVo::getRevenue,
Comparator.nullsFirst(BigDecimal::compareTo)))
.collect(Collectors.toList());

注意 Comparator.nullsFirst 的方法引用中,比较的字段是 BigDecimal 类型的,如果前后类型不一致,会报错:Non-static method cannot be referenced from a static context

按金额排序,Null 值排在最后面

List<DailyDataChartVo> list = list.stream()
.sorted(Comparator.comparing(DailyDataChartVo::getRevenue,
Comparator.nullsLast(BigDecimal::compareTo)))
.collect(Collectors.toList());

Map 排序

按 key 排序

Map<LocalDate, BigDecimal> map = map.entrySet()
.stream()
.sorted(Map.Entry.comparingByKey())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (c1, c2) -> c1, LinkedHashMap::new));

将 map 转换成流,在流中对元素进行排序,排序后,再用 LinkedHashMap 收集来保留顺序

public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() {
    return (Comparator<Map.Entry<K, V>> & Serializable)(c1, c2) -> c1.getKey().compareTo(c2.getKey());
}

Map.Entry.comparingByKey():对任意的 c1, c2 进行比较,然后将结果强制转换成一个可序列化的 Comparator<Map.Entry<K, V>>

Collectors.toMap() 基础

按 key 排序后,逆序

Map<LocalDate, BigDecimal> map = map.entrySet()
.stream()
.sorted(Map.Entry.comparingByKey(Comparator.reverseOrder()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (c1, c2) -> c1, LinkedHashMap::new));

按 value 排序

Map<LocalDate, BigDecimal> map = map.entrySet()
.stream()
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (c1, c2) -> c1, LinkedHashMap::new));

List<Map<String, Object>> 排序

// 降序排序(传统写法,idea 会提示优化)
Collections.sort(resMapList, new Comparator<Map<String, Object>>() {
   @Override
   public int compare(Map<String, Object> o1, Map<String, Object> o2) {
        // 降序
        return Convert.toLong(o2.get("需要排序字段")).compareTo(Convert.toLong(o1.get("需要排序字段")));
   }
});

// 降序排序(简化写法)
resList.sort((o1,o2) -> Convert.toLong(o2.get("需要排序字段")).compareTo(Convert.toLong(o1.get("需要排序字段"))));

5.对于 List<Map<String, Object>> 数据的分组求和

List<Map<String, Object>> testList = CollUtil.newArrayList();

Map<String, Object> map1 = new LinkedHashMap<>();
map1.put("id", 1);
map1.put("allCount", 1);
map1.put("insertCount", 1);
map1.put("updateCount", 1);
map1.put("deleteCount", 1);
testList.add(map1);

Map<String, Object> map2 = new LinkedHashMap<>();
map2.put("id", 1);
map2.put("allCount", 2);
map2.put("insertCount", 2);
map2.put("updateCount", 2);
map2.put("deleteCount", 2);
testList.add(map2);

Map<String, Object> map3 = new LinkedHashMap<>();
map3.put("id", 3);
map3.put("allCount", 3);
map3.put("insertCount", 3);
map3.put("updateCount", 3);
map3.put("deleteCount", 3);
testList.add(map3);

// 以 id 数据进行分组,然后对重复记录进行一个聚合处理(逻辑运算,这里是相加),也就是每个字段统计数量进行相加,然后再组合返回结果
testList = testList.stream().collect(Collectors.groupingBy(e -> e.get("id"))).values().stream().map(gatewayStatList -> {
   // 获取0主要有2个原因:
   // 1.是有的记录数不存在重复的,只存在单条;
   // 2.是里面存在基础数据,主要是不用重复对 id 进行一个 put 操作,另一种方式是可以直接创建一个 map,然后把 id 进行 put 操作
   Map<String, Object> map = gatewayStatList.get(0);
   map.put("allCount", gatewayStatList.stream().map(s -> new BigDecimal(s.get("allCount").toString())).reduce(BigDecimal.ZERO, BigDecimal::add));
   map.put("insertCount", gatewayStatList.stream().map(s -> new BigDecimal(s.get("insertCount").toString())).reduce(BigDecimal.ZERO, BigDecimal::add));
   map.put("updateCount", gatewayStatList.stream().map(s -> new BigDecimal(s.get("updateCount").toString())).reduce(BigDecimal.ZERO, BigDecimal::add));
   map.put("deleteCount", gatewayStatList.stream().map(s -> new BigDecimal(s.get("deleteCount").toString())).reduce(BigDecimal.ZERO, BigDecimal::add));
   return map;
}).collect(Collectors.toList());

// {id=1, allCount=3, insertCount=3, updateCount=3, deleteCount=3}
// {id=3, allCount=3, insertCount=3, updateCount=3, deleteCount=3}
testList.forEach(System.out::println);

6.对实体对象某字段求和

//1.对int类型list进行求和
Integer[] integerArray = {1, 3, 5, 10, 18};

List<Integer> list = new ArrayList<>(Arrays.asList(integerArray));

IntSummaryStatistics summaryStatistics = list.stream().mapToInt((s) -> s).summaryStatistics();
System.out.println("总和:" + summaryStatistics.getSum());
System.out.println("平均数:" + summaryStatistics.getAverage());
System.out.println("总个数:" + summaryStatistics.getCount());
System.out.println("最大值:" + summaryStatistics.getMax());
System.out.println("最小值:" + summaryStatistics.getMin());

//2.对list中的对象某个数值字段求和
List<OrderReceivablesDetail> userList = new ArrayList<>();
OrderReceivablesDetail user1 = new OrderReceivablesDetail();
user1.setCollectionRatio(10.1);//double类型
user1.setMoney(new BigDecimal(1000.1));//BigDecimal类型
userList.add(user1);
OrderReceivablesDetail user2 = new OrderReceivablesDetail();
user2.setCollectionRatio(20.1);//double类型
user2.setMoney(new BigDecimal(2000.1));//BigDecimal类型
userList.add(user2);
//写法1
double collectionRatio = userList.stream().collect(Collectors.summingDouble(OrderReceivablesDetail::getCollectionRatio));
System.out.println("比例总和 :" + collectionRatio);
//写法2
double collectionRatio2 = userList.stream().mapToDouble(OrderReceivablesDetail::getCollectionRatio).sum();
System.out.println("比例总和2: " + collectionRatio2);

//3.对list中对象某个BigDecimal字段求和
BigDecimal sumMoney = userList.stream().map(OrderReceivablesDetail::getMoney).reduce(BigDecimal.ZERO,BigDecimal::add);
System.out.println("总金额: " + sumMoney);
//说明:summingDouble方法是针对double类型的
//summingLong long类型
//summingInt Int类型

//第二种转换法 方法
//mapToLong long类型
//mapToDouble double类型
//mapToInt Int类型

7.对 List<Map<String,Object>> 某字段进行降序排序并取前 top2

List<Map<String, Object>> testList = CollUtil.newArrayList();

Map<String, Object> map1 = new LinkedHashMap<>();
map1.put("id", 1);
map1.put("allCount", 1);
map1.put("insertCount", 1);
testList.add(map1);

Map<String, Object> map2 = new LinkedHashMap<>();
map2.put("id", 2);
map2.put("allCount", 2);
map2.put("insertCount", 2);
testList.add(map2);

Map<String, Object> map3 = new LinkedHashMap<>();
map3.put("id", 3);
map3.put("allCount", 3);
map3.put("insertCount", 3);
testList.add(map3);

Map<String, Object> map4 = new LinkedHashMap<>();
map4.put("1", 11);
map4.put("2", 23);
map4.put("3", 44);

List<Map<String, Object>> resList = new LinkedList<>();

map4.forEach((k,v) -> {
   // {id=1, allCount=1, insertCount=1}
   // {id=2, allCount=2, insertCount=2}
   testList.forEach(map -> {
       if (StrUtil.equals(Convert.toStr(map.get("id")), k)) {
           Map<String, Object> resMap = new LinkedHashMap<>();
           Long allCount = Convert.toLong(map.get("allCount"));
           Long insertCount = Convert.toLong(map.get("insertCount"));
           resMap.put("allCount", allCount);
           resMap.put("insertCount", insertCount);
           resMap.put("deleteCount", v);
           resList.add(resMap);
       }
   });
});
// 未排序前的数据
System.out.println("------排序前的数据------");
// ------排序前的数据------
// {allCount=1, insertCount=1, deleteCount=11}
// {allCount=2, insertCount=2, deleteCount=23}
// {allCount=3, insertCount=3, deleteCount=44}
resList.forEach(System.out::println);

// 降序排序(传统写法,idea会提示优化)
Collections.sort(resMapList, new Comparator<Map<String, Object>>() {
   @Override
   public int compare(Map<String, Object> o1, Map<String, Object> o2) {
      // 降序
      return Convert.toLong(o2.get("allCount")).compareTo(Convert.toLong(o1.get("allCount")));
   }
});


// 降序排序(简化写法)
resList.sort((o1,o2) -> Convert.toLong(o2.get("allCount")).compareTo(Convert.toLong(o1.get("allCount"))));

// 取前2条
List<Map<String, Object>> resMapList = resList.stream().limit(2).collect(Collectors.toList());
System.out.println("------排序后的取top2的数据------");
// ------排序后的取top2的数据------
// {allCount=3, insertCount=3, deleteCount=44}
// {allCount=2, insertCount=2, deleteCount=23}
resMapList.forEach(System.out::println);

8.对 List<Map<String,Object>>Map<String,Long> 数据重组返回

如下情况适用于 Map<String,Long> 中的 K 与 List<Map<String,Object>> 中的 K 一样,将不同的 V 进行重组,并返回 List<Map<String,Object>> 类型的数据。

List<Map<String, Object>> testList = CollUtil.newArrayList();

Map<String, Object> map1 = new LinkedHashMap<>();
map1.put("id", 1);
map1.put("allCount", 1);
map1.put("insertCount", 1);
testList.add(map1);

Map<String, Object> map2 = new LinkedHashMap<>();
map2.put("id", 2);
map2.put("allCount", 2);
map2.put("insertCount", 2);
testList.add(map2);

Map<String, Object> map3 = new LinkedHashMap<>();
map3.put("id", 3);
map3.put("allCount", 3);
map3.put("insertCount", 3);
testList.add(map3);

Map<String, Object> map4 = new LinkedHashMap<>();
// 这里的key与前面map中的id相等,value为新的值来表示(deleteCount)
map4.put("1", 11);
map4.put("2", 23);
map4.put("3", 44);

List<Map<String, Object>> resList = new LinkedList<>();

// 重组
map4.forEach((k,v) -> {
   testList.forEach(map -> {
      if (StrUtil.equals(Convert.toStr(map.get("id")), k)) {
         Map<String, Object> resMap = new LinkedHashMap<>();
         Long allCount = Convert.toLong(map.get("allCount"));
         Long insertCount = Convert.toLong(map.get("insertCount"));
         resMap.put("allCount", allCount);
         resMap.put("insertCount", insertCount);
         resMap.put("deleteCount", v);
         resList.add(resMap);
      }
   });
});
// {allCount=1, insertCount=1, deleteCount=11}
// {allCount=2, insertCount=2, deleteCount=23}
// {allCount=3, insertCount=3, deleteCount=44}
resList.forEach(System.out::println)

9.groupingBy 分组相关使用

Java8 的 groupingBy 实现集合的分组,类似 Mysql 的 group by 分组功能,注意得到的是一个 map

对集合按照单个属性分组、分组计数、排序

List<String> items = Arrays.asList("apple", "apple", "banana", "apple", "orange", "banana", "papaya");

// 分组
Map<String, List<String>> result1 = items.stream().collect(
   Collectors.groupingBy(
      Function.identity()
   )
);
//{papaya=[papaya], orange=[orange], banana=[banana, banana], apple=[apple, apple, apple]}
System.out.println(result1);

// 分组计数
Map<String, Long> result2 = items.stream().collect(
    Collectors.groupingBy(
        Function.identity(), Collectors.counting()
    )
);

// {papaya=1, orange=1, banana=2, apple=3}
System.out.println(result2);
Map<String, Long> finalMap = new LinkedHashMap<>();

//分组, 计数和排序
result2.entrySet().stream()
.sorted(Map.Entry.<String, Long>comparingByValue().reversed())
.forEachOrdered(e -> finalMap.put(e.getKey(), e.getValue()));

// {apple=3, banana=2, papaya=1, orange=1}
System.out.println(finalMap);

集合按照多个属性分组

  1. 多个属性拼接出一个组合属性
public static void main(String[] args) {
    User user1 = new User("zhangsan", "beijing", 10);
    User user2 = new User("zhangsan", "beijing", 20);
    User user3 = new User("lisi", "shanghai", 30);
    List<User> list = new ArrayList<User>();
    list.add(user1);
    list.add(user2);
    list.add(user3);
    Map<String, List<User>> collect = list.stream().collect(Collectors.groupingBy(e -> fetchGroupKey(e)));
    //{zhangsan#beijing=[User{age=10, name='zhangsan', address='beijing'}, User{age=20, name='zhangsan', address='beijing'}],
    // lisi#shanghai=[User{age=30, name='lisi', address='shanghai'}]}
    System.out.println(collect);
}

private static String fetchGroupKey(User user){
    return user.getName() +"#"+ user.getAddress();
}
  1. 嵌套调用 groupBy
User user1 = new User("zhangsan", "beijing", 10);
User user2 = new User("zhangsan", "beijing", 20);
User user3 = new User("lisi", "shanghai", 30);
List<User> list = new ArrayList<User>();
list.add(user1);
list.add(user2);
list.add(user3);
Map<String, Map<String, List<User>>> collect = list.stream().collect(
   Collectors.groupingBy(
    User::getAddress, Collectors.groupingBy(User::getName)
   )
);
System.out.println(collect);
  1. 使用 Arrays.asList

我有一个与Web访问记录相关的域对象列表。这些域对象可以扩展到数千个。 我没有资源或需求将它们以原始格式存储在数据库中,因此我希望预先计算聚合并将聚合的数据放在数据库中。 我需要聚合在5分钟窗口中传输的总字节数,如下面的 sql 查询

select
    round(request_timestamp, '5') as window, --round timestamp to the nearest 5 minute
    cdn,
    isp,
    http_result_code,
    transaction_time,
    sum(bytes_transferred)
from web_records
group by
    round(request_timestamp, '5'),
    cdn,
    isp,
    http_result_code,
    transaction_time

在 java 8 中,我当前的第一次尝试是这样的,我知道这个解决方案类似于 Group by multiple field names in java 8

Map<Date, Map<String, Map<String, Map<String, Map<String, Integer>>>>>>> aggregatedData =
    webRecords
    .stream()
    .collect(Collectors.groupingBy(WebRecord::getFiveMinuteWindow,
        Collectors.groupingBy(WebRecord::getCdn,
            Collectors.groupingBy(WebRecord::getIsp,
                Collectors.groupingBy(WebRecord::getResultCode,
                    Collectors.groupingBy(WebRecord::getTxnTime,
                        Collectors.reducing(0,
                            WebRecord::getReqBytes(),
                                Integer::sum)))))));

这是可行的,但它是丑陋的,所有这些嵌套的地图是一个噩梦!要将地图“展平”或“展开”成行,我必须这样做

for (Date window : aggregatedData.keySet()) {
    for (String cdn : aggregatedData.get(window).keySet()) {
        for (String isp : aggregatedData.get(window).get(cdn).keySet()) {
            for (String resultCode : aggregatedData.get(window).get(cdn).get(isp).keySet()) {
                for (String txnTime : aggregatedData.get(window).get(cdn).get(isp).get(resultCode).keySet()) {
                    Integer bytesTransferred = aggregatedData.get(window).get(cdn).get(distId).get(isp).get(resultCode).get(txnTime);
                    AggregatedRow row = new AggregatedRow(window, cdn, distId...

如你所见,这是相当混乱和难以维持。 有谁知道更好的方法吗?任何帮助都将不胜感激。 我想知道是否有更好的方法来展开嵌套的映射,或者是否有一个库允许您对集合进行分组。

最佳答案

最简单的方法是使用 Arrays.asList

Function<WebRecord, List<Object>> keyExtractor = wr ->
    Arrays.<Object>asList(wr.getFiveMinuteWindow(), wr.getCdn(), wr.getIsp(),
    wr.getResultCode(), wr.getTxnTime());

Map<List<Object>, Integer> aggregatedData = webRecords.stream().collect(
    Collectors.groupingBy(keyExtractor, Collectors.summingInt(WebRecord::getReqBytes)));

在这种情况下,键是按固定顺序列出的 5 个元素。不是很面向对象,但很简单。或者,您可以定义自己的表示自定义键的类型,并创建适当的 hashCode/equals 实现。

10.filter、distinct 使用

filter使用

用来对 stream 中的元素进行过滤的方法

源码中是这样定义的

Stream<T> filter(Predicate<? super T> predicate);

它需要传入一个 Predicate 实现类 即

public interface Predicate<T> {
    boolean test(T t);
}

即传入一个泛型t,返回一个 boolean 而 filter 方法则通过对 test 方法返回的 boolean 进行判断,以确定是否进行过滤。 下方使用 lambda 表达式,其中 "->" 左边表示 方法 boolean test(T t) 中的 t,后边内容即为业务逻辑,可以理解为 return 部分,

简单应用

以下这段代码使用 filter 进行了对偶数的过滤

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 6, 7, 7, 1);
    List<Integer> result = list.stream().filter(i -> i % 2 == 0).collect(toList());
    System.out.println(result);
}

其输出结果如下

[2, 4, 6, 6]

复杂对象过滤

接下来通过对象的属性进行过滤 apple 类,其中有属性为 color 则过滤时如下

public static void main(String[] args) {
    List<Apple> list = Arrays.asList(new Apple("green", 150), new Apple("yellow", 120), new Apple("green", 170));
    List<Apple> green = list.stream().filter(a -> a.getColor().equals("green")).collect(toList());
    System.out.println(green);
}

输出结果如下

[Apple{color='green', weight=150}, Apple{color='green', weight=170}]

distinct 使用

用来对 stream 中的元素进行去重的方法,该方法不需要传参,且返回亦是 stream

简单应用

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 6, 7, 7, 1);
List<Integer> result = list.stream().distinct().collect(toList());
System.out.println(result);

其输出结果如下

[1, 2, 3, 4, 5, 6, 7]

复杂对象去重

这里复杂对象去重的原理与 hashset 去重原理一致,该类将重写 hashCode()equals()

这里简单的重写了 Apple 中的这两个方法

@Override
public int hashCode() {
    return this.getColor().hashCode();
}

@Override
public boolean equals(Object obj) {
    return this.hashCode() == obj.hashCode();
}

接下来业务代码如下

List<Apple> list = Arrays.asList(new Apple("green", 150), new Apple("yellow", 120), new Apple("green", 170));
List<Apple> collect = list.stream().distinct().collect(toList());
System.out.println(collect);

其结果如下

[Apple{color='green', weight=150}, Apple{color='yellow', weight=120}]