将EntrySet的流收集到LinkedHashMap中

37

我想将流收集到一个 LinkedHashMap<String, Object> 中。

我有一个存储在 LinkedHashMap<String, Object> resources 中的 JSON 资源。 然后,我通过对此映射的 EntrySet 进行流过滤来筛选出 JSON 元素。 目前,我正在将流的元素收集到一个普通的 HashMap 中。但在此之后,我会向映射中添加其他元素。我希望这些元素按照插入顺序排列。

final List<String> keys = Arrays.asList("status", "createdDate");

Map<String, Object> result = resources.entrySet()
        .stream()
        .filter(e -> keys.contains(e.getKey()))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

result.put("date", "someDate");
return result;

这就是为什么我想把流收集到一个LinkedHashMap<String, Object>中。我该怎么做呢?


1
我有一个存储在Map<String, Object>资源中的JSON资源。你怎么知道这个映射中的条目是以任何顺序排列的?除非它存储在LinkedHashMap中,否则你如何期望它保持顺序? - Nicholas K
JSON 不关心 JSON 对象键的顺序。 - njzk2
1
是的,我会这样做。我按特定顺序打印它们,并通过将其与我的资源中的另一个JSON进行比较来对该方法进行单元测试。 - xtra
@NicholasK 确实,我给出了错误的类型。应该是 LinkedHashMap<String, Object> resources - xtra
4个回答

84
你可以使用Stream来完成这个操作:
Map<String, Object> result = resources.entrySet()
            .stream()
            .filter(e -> keys.contains(e.getKey()))
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (x, y) -> y, LinkedHashMap::new));

这部分的(x, y) -> y是由于mergeFunction,当找到重复的键时,它会返回第二个键的值。第四部分是mapFactory,它是一个提供新空Map的供应商,结果将被插入其中。


1
你如何在Scala中实现这个? :) - Rimer
当我们只想更改哈希映射实现但保持重复行为时,应该如何操作?JDK方便地将throwingMerger(先前的名称,不一定存在于当前jdk中)作为私有方法保留,并且4个参数版本不允许指定累加器,仅允许指定映射合并器...正确的方式是什么? - Martin Mucha
使用此方法时,如果任何条目值为null,则LinkedHashMap.merge()会抛出NullPointerException异常。 - Sam Barnum
这很棒。Collectors.toMap 看起来比其他 Collectors.to* 选项要复杂一些。难道不应该有一种默认的方法来过滤地图并重建它吗?也就是说,应该对 List<Map.Entry<K, V>> 进行重载 - 我猜在这种情况下,也不会生成重复项...也许我们可以选择原始地图类作为要使用的类? - Ehtesh Choudhury
@MartinMucha 在合并函数中抛出一个 IllegalStateException 异常。查看下面的答案示例。 - Dzeri96

4

另一种使用Map.forEach实现的方式是:

Map<String, Object> result = new LinkedHashMap<>();
resources.forEach((key, value) -> {
    if (keys.contains(key)) {
        result.put(key, value);
    }
});
result.put("date", "someDate");

如果您考虑使用迭代 keySet 作为一种选项:

Map<String, Object> result = new LinkedHashMap<>();
resources.keySet().stream()
        .filter(keys::contains)
        .forEach(key -> result.put(key,resources.get(key)));
result.put("date", "someDate");

4

正如其他人所指出的那样,如果您想提供自己的mapFactory,您还必须提供一个合并函数。被接受的答案实际上改变了三个参数函数的默认行为,即在遇到重复键时抛出异常。这个实现保留了原始的行为:

LinkedHashMap lmp = collection.
                .stream()
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        Map.Entry::getValue,
                        (v1, v2) -> {throw new IllegalStateException("YOUR MESSAGE HERE");},
                        LinkedHashMap::new
                ));

1

被接受的答案包含与默认合并器(抛出异常)不同的合并器。很遗憾,在Java的Collectors类中不存在三个参数的toMap(keyFunction, valueFunction, mapFactory)重载。由于我只需要LinkedHashMap,所以我从该类的内部复制粘贴了必要的部分,并将其作为自定义辅助方法进行了编写。行为与两个参数的toMap相同,除了生成的映射是LinkedHashMap

class Helper {

    private static <K,V,M extends Map<K,V>> BinaryOperator<M> uniqKeysMapMerger() {
        return (m1, m2) -> {
            for (Map.Entry<K,V> e : m2.entrySet()) {
                K k = e.getKey();
                V v = Objects.requireNonNull(e.getValue());
                V u = m1.putIfAbsent(k, v);
                if (u != null) throw new IllegalStateException(String.format("Duplicate key %s (attempted merging values %s and %s)", k, u, v));
            }
            return m1;
        };
    }

    private static <T,K,V> BiConsumer<Map<K,V>,T> uniqKeysMapAccumulator(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends V> valueMapper) {
        return (map, element) -> {
            K k = keyMapper.apply(element);
            V v = Objects.requireNonNull(valueMapper.apply(element));
            V u = map.putIfAbsent(k, v);
            if (u != null) throw new IllegalStateException(String.format("Duplicate key %s (attempted merging values %s and %s)", k, u, v));
        };
    }

    public static <T,K,U> Collector<T,?,Map<K,U>> toLinkedHashMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper) {
        return Collector.of(LinkedHashMap::new, uniqKeysMapAccumulator(keyMapper, valueMapper), uniqKeysMapMerger());
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接