用Java 8+对Map进行分区

Lpp*_*Edd 21 java java-stream

我有一个Map<String, String>和一个List<String>.我想Map根据条件进行分区

foreach(map.key -> list.contains(map.key))
Run Code Online (Sandbox Code Playgroud)

并产生两个Map.最优雅的方式是什么?我在Java 11上,所以你可以在答案中抛出你想要的一切.

我现在要做的是:

map.entrySet()
   .stream()
   .collect(partitioningBy(e -> list.contains(o.getKey())));
Run Code Online (Sandbox Code Playgroud)

但这给了一个Map<Boolean, List<Entry<String, String>>>.

ern*_*t_k 27

您可以使用toMap(作为下游收集器)减少每个组:

Map<String, String> myMap = new HashMap<>();
myMap.put("d", "D");
myMap.put("c", "C");
myMap.put("b", "B");
myMap.put("A", "A");

List<String> myList = Arrays.asList("a", "b", "c");

Map<Boolean, Map<String, String>> result = myMap.entrySet()
        .stream()
        .collect(Collectors.partitioningBy(
                            entry -> myList.contains(entry.getKey()),
                            Collectors.toMap(Entry::getKey, Entry::getValue)
                    )
        );
Run Code Online (Sandbox Code Playgroud)

对于这个例子,这产生了 {false={A=A, d=D}, true={b=B, c=C}}


Nam*_*man 8

虽然partitioningBy是根据条件需要两种替代方案作为输出的方法.然而,另一种出路(对于基于单一条件创建地图很有用)是Collectors.filtering用作:

Map<String, String> myMap = Map.of("d", "D","c", "C","b", "B","A", "A");
List<String> myList = List.of("a", "b", "c");
Predicate<String> condition = myList::contains;

Map<String, String> keysPresentInList = myMap.keySet()
        .stream()
        .collect(Collectors.filtering(condition,
                Collectors.toMap(Function.identity(), myMap::get)));
Map<String, String> keysNotPresentInList = myMap.keySet()
        .stream()
        .collect(Collectors.filtering(Predicate.not(condition),
                Collectors.toMap(Function.identity(), myMap::get)));
Run Code Online (Sandbox Code Playgroud)

或者,如果您可以就地更新现有地图,那么您可以仅使用一行代码根据其键在列表中的存在来保留条目:

myMap.keySet().retainAll(myList);
Run Code Online (Sandbox Code Playgroud)

  • 确实是@Asoub。在这里由达山的答案提出。 (2认同)

Sch*_*uca 6

你不能真正使用流生成两个单独的地图(至少不是以最优雅的方式).不要害怕使用旧的常规forEach,我认为它是一个非常干净的版本:

Map<String, String> contains = new HashMap<>();
Map<String, String> containsNot = new HashMap<>();

for(Map.Entry<String, String> entry : yourMap.entrySet()) {
    if (yourList.contains(entry.getKey())) {
        contains.put(entry.getKey(), entry.getValue());
    } else {
        containsNot.put(entry.getKey(), entry.getValue());
    }
}
Run Code Online (Sandbox Code Playgroud)


Dar*_*hta 6

您可以map通过对原始文件应用过滤进行过滤map,例如:

List<String> list = new ArrayList<>(); //List of values
Map<String, String> map = new HashMap<>();

Map<String, String> filteredMap = map.entrySet()
.stream()
.filter(e -> list.contains(e.getKey()))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
Run Code Online (Sandbox Code Playgroud)

然后,您可以将filteredMap内容与原始内容进行比较,map以提取不存在的条目filteredMap.


Fed*_*ner 5

您可以迭代地图并使用Java 8+中引入的功能:

Map<Boolean, Map<String, String>> result = Map.of(true, new LinkedHashMap<>(), 
                                                  false, new LinkedHashMap<>());
Set<String> set = new HashSet<>(list);
map.forEach((k, v) -> result.get(set.contains(k)).put(k, v));
Run Code Online (Sandbox Code Playgroud)

首先,我们创建一个result包含两个条目的映射,每个分区一个。值为LinkedHashMaps,以便保留插入顺序。

然后,我们HashSet从列表中创建一个,以便调用set.contains(k)是一个O(1)操作(否则,如果执行list.contains(k),这将是O(n)针对地图的每个条目的,因此总时间复杂度为O(n^2),这很糟糕)。

最后,(k, v)根据调用的结果,迭代输入映射并将条目放置在相应的分区中set.contains(k)