有没有一种方法可以将Java 8中的分组列表串联在一起?

cre*_*kre 5 java java-8 java-stream

我处于一种奇怪的情况下,其中有一个JSON API,该数组将以邻域字符串作为键的数组和以餐厅字符串作为值的数组,将GSON解析为Restaurant对象(String为邻域定义了A List<String>,餐馆)。系统将该数据存储在地图中,该地图的键是社区名称,值是该社区中的一组餐厅名称。因此,我想实现一个函数,该函数接收来自API的输入,按邻域对值进行分组,并连接餐馆列表。

受Java 8的约束,我无法使用更新的构造(例如flatMapping)在一行中完成所有操作,而我发现的最佳解决方案是此解决方案,该解决方案在连接这些列表之前使用中间映射存储一组List放入Set中以作为值存储在最终映射中:

public Map<String, Set<String>> parseApiEntriesIntoMap(List<Restaurant> restaurants) {
    if(restaurants == null) {
      return null;
    }
    Map<String, Set<String>> restaurantListByNeighborhood = new HashMap<>();
    // Here we group by neighborhood and concatenate the list of restaurants into a set
    Map<String, Set<List<String>>> map =
        restaurants.stream().collect(groupingBy(Restaurant::getNeighborhood,
                              Collectors.mapping(Restaurant::getRestaurantList, toSet())));
    map.forEach((n,r) -> restaurantListByNeighborhood.put(n, Sets.newHashSet(Iterables.concat(r))));

    return restaurantListByNeighborhood;
  }
Run Code Online (Sandbox Code Playgroud)

我觉得必须有一种方法可以摆脱该中间图,并一站式完成所有工作……某人是否有更好的解决方案可以让我做到这一点?

Nam*_*man 3

您可以使用 Java-8 简单地使用定义toMapmergeFunction

public Map<String, Set<String>> parseApiEntriesIntoMap(List<Restaurant> restaurants) {
    // read below about the null check
    return restaurants.stream()
            .collect(Collectors.toMap(Restaurant::getNeighborhood,
                    r -> new HashSet<>(r.getRestaurantList()), (set1, set2) -> {
                        set1.addAll(set2);
                        return set1;
                    }));
}
Run Code Online (Sandbox Code Playgroud)

除此之外,应该确保方法中第一个代码块的检查和结果

if(restaurants == null) {
  return null;
}
Run Code Online (Sandbox Code Playgroud)

另一方面,当处理 Collections 和时Map,它应该是多余的,因为上面的代码将根据流和收集操作本身的性质为空 List 返回空 Map。

注意:此外,如果您在未来的升级中可能需要更多相关的代码flatMapping,您可以使用此答案中提供的实现


或者在这种情况下,不使用流的解决方案看起来类似于使用Map.merge. 它将使用类似BiFunction的:

public Map<String, Set<String>> parseApiEntriesIntoMap(List<Restaurant> restaurants) {
    Map<String, Set<String>> restaurantListByNeighborhood = new HashMap<>();
    for (Restaurant restaurant : restaurants) {
        restaurantListByNeighborhood.merge(restaurant.getNeighborhood(),
                new HashSet<>(restaurant.getRestaurantList()),
                (strings, strings2) -> {
                    strings.addAll(strings2);
                    return strings;
                });
    }
    return restaurantListByNeighborhood;
}
Run Code Online (Sandbox Code Playgroud)

  • 我宁愿在循环中使用 `restaurantListByNeighborhood.computeIfAbsent(restaurant.getNeighborhood(), key -&gt; new HashSet&lt;&gt;()).addAll(restaurant.getRestaurantList());` ...并且自从您提到了 `flatMapping` 以来,最好还提到完整的解决方案,即 `restaurants.stream().collect(groupingBy(Restaurant::getNeighborhood, flatMapping(r -&gt; r.getRestaurantList().stream(), toSet()))) ;`,正如我在问答中看到的那样。 (2认同)