使用Java 8流API按多个属性对对象进行分组

时间:2017-05-25 02:56:58

标签: java java-8 java-stream

鉴于我们有一个银行清单,每个银行都有多个办事处,

public class Bank {
   private String name;
   private List<String> branches;
   public String getName(){
       return name;
   }
   public List<String> getBranches(){
       return branches;
   }
}

例如:

Bank "Mizuho": branches=["London", "New York"]
Bank "Goldman": branches = ["London", "Toronto"]

根据银行名单,我会得到每个城市的银行代表地图。在上面的例子中,我需要

的结果
Map["London"] == ["Mizuho", "Goldman"]
Map["New York"] == ["Mizuho"]
Map["Toronto"] == ["Goldman"]

如何使用Java 8 API实现该结果?使用pre-Java8很容易,但很冗长。 谢谢。

4 个答案:

答案 0 :(得分:6)

Map<String, Set<Bank>> result = new HashMap<>();
for (Bank bank : banks) {
    for (String branch : bank.getBranches()) {
        result.computeIfAbsent(branch, b -> new HashSet<Bank>()).add(bank);
    }
}

答案 1 :(得分:4)

 banks.flatMap(bank -> bank.getBranches()
             .stream()
             .map(branch -> new AbstractMap.SimpleEntry<>(branch, bank)))
       .collect(Collectors.groupingBy(
             Entry::getKey, 
             Collectors.mapping(Entry::getValue, Collectors.toList())));

结果将是:

{London=[Mizuho, Goldman], NewYork=[Mizuho], Toronto=[Goldman]}

答案 2 :(得分:1)

您可以使用接受供应商,累加器功能和组合器功能的Stream.collect版本来执行此操作,如下所示:

Map<String, List<Bank>> result = banks.stream()
    .collect(
        HashMap::new,
        (map, bank) -> bank.getBranches().forEach(branch ->
            map.computeIfAbsent(branch, k -> new ArrayList<>()).add(bank)),
        (map1, map2) -> map2.forEach((k, v) -> map1.merge(k, v, (l1, l2) -> {
            l1.addAll(l2);
            return l1;
        })));

答案 3 :(得分:0)

我认为@JB Nizet提供的解决方案是最简单/最有效的解决方案之一。它也可以由forEach重写

banks.forEach(b -> b.getBranches().forEach(ch -> result.computeIfAbsent(ch, k -> new ArrayList<>()).add(b)));

Stream与AbacusUtil

的另一个简短解决方案
Map<String, List<Bank>> res = Stream.of(banks)
          .flatMap(e -> Stream.of(e.getBranches()).map(b -> Pair.of(b, e)))
          .collect(Collectors.toMap2());