在Java中将一个集合转换为映射表

15

我有一个集合,想将其转换为映射以便稍后在Guava的Maps.difference()中使用。 我只关心差异中的键。
我想到了这个版本:

private <T> Map<T, T> toMap(Set<T> set) {
  return set.stream().collect(Collectors.toMap(Function.identity(), Function.identity()));
}

然而,我知道通常情况下,一个集合有一个映射的后备字段。这是我用来创建映射的方法:

public static <E> Set<E> newConcurrentHashSet() {
  return Collections.newSetFromMap(new ConcurrentHashMap<E, Boolean>());
}

因为我只需要这些键,所以我想知道是否可以从某种方式上获得这个字段的视图。有什么想法吗?


2
为什么不使用 https://google.github.io/guava/releases/snapshot/api/docs/com/google/common/collect/Sets.html#difference-java.util.Set-java.util.Set-?为什么要将一个集合强制转换成映射表? - luk2302
我想知道哪些项目只在左边,哪些只在右边,哪些是共同的(类似于映射差异)。 - oshai
1
如果你只需要键,为什么要转换成Map?Map本质上是一个带有值的Set,所以你说的话没有意义。 - Andreas
6个回答

28

我最终使用Java 8得出了一个相当简单的一行代码解决方案,如下所示:

Map<String, Foo> map = fooSet.stream().collect(Collectors.toMap(Foo::getKey, e -> e));
  • fooSet 是一组类型为 Foo 的对象集合,即 Set<Foo> fooSet
  • Foo 有一个名为 getKey 的 getter 方法,返回一个字符串

4

开发者回答进行改进:

Map<String, Foo> map = fooSet.stream().collect(Collectors.toMap(Foo::getKey, Function.identity()));

如果你静态导入了 Collectors.toMapFunction.identity:

Map<String, Foo> map = fooSet.stream().collect(toMap(Foo::getKey, identity()));

什么是 Function.identity() - jumping_monkey
1
@jumping_monkey Function.identity() 的文档位于 https://docs.oracle.com/javase/8/docs/api/java/util/function/Function.html#identity-- 它返回一个接受一个参数并在调用时返回该参数的函数。在collect示例中,其效果是将条目流的值按原样取出以构建映射。 - Björn Kahlert
1
哦,f(x) = x。谢谢Björn! - jumping_monkey

4
您可以将一个 Set 转换为一个 Map(键和值从 Set 的元素中获取),如下所示:
private <T> Map<T, T> toMap(Set<T> set) {
    Map<T, T> map = new ConcurrentHashMap<>();
    set.forEach(t -> map.put(t, t));//contains same key and value pair
    return map;
}

1

来自评论:

我想知道哪些项目只在左边,哪些只在右边,哪些相同(类似于映射差异)

使用removeAll()和[retainAll()][3]

示例:

Set<Integer> set1 = new HashSet<>(Arrays.asList(1,3,5,7,9));
Set<Integer> set2 = new HashSet<>(Arrays.asList(3,4,5,6,7));

Set<Integer> onlyIn1 = new HashSet<>(set1);
onlyIn1.removeAll(set2);

Set<Integer> onlyIn2 = new HashSet<>(set2);
onlyIn2.removeAll(set1);

Set<Integer> inBoth = new HashSet<>(set1);
inBoth.retainAll(set2);

System.out.println("set1: " + set1);
System.out.println("set2: " + set2);
System.out.println("onlyIn1: " + onlyIn1);
System.out.println("onlyIn2: " + onlyIn2);
System.out.println("inBoth : " + inBoth);

输出

set1: [1, 3, 5, 7, 9]
set2: [3, 4, 5, 6, 7]
onlyIn1: [1, 9]
onlyIn2: [4, 6]
inBoth : [3, 5, 7]

现在,如果您想知道所有的值及其所在位置,可以使用以下方法(Java 8):
Set<Integer> setA = new HashSet<>(Arrays.asList(1,3,5,7,9));
Set<Integer> setB = new HashSet<>(Arrays.asList(3,4,5,6,7));

Map<Integer, String> map = new HashMap<>();
for (Integer i : setA)
    map.put(i, "In A");
for (Integer i : setB)
    map.compute(i, (k, v) -> (v == null ? "In B" : "In Both"));

System.out.println("setA: " + setA);
System.out.println("setB: " + setB);
map.entrySet().stream().forEach(System.out::println);

输出

setA: [1, 3, 5, 7, 9]
setB: [3, 4, 5, 6, 7]
1=In A
3=In Both
4=In B
5=In Both
6=In B
7=In Both
9=In A

0

请参考类似的答案这里

假设您的原始集合是一组值(原始数据中没有键!),则需要为新创建的映射指定键。Guava的Maps.uniqueIndex可能会有所帮助(请参见这里)。

否则,如果您的原始集合是一组键(原始数据中没有值!)并且您想要保留它们,则需要为新创建的映射指定默认值或特定值。Guava的Maps.toMap在这里可能会有所帮助。(更多信息请参见这里


0
package com.example;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {    
        Set<Foo> s = new HashSet<>();
        s.add(new Foo("cccc"));
        s.add(new Foo("aaaa"));
        s.add(new Foo("bbb"));
        Map<String, Foo> m = s.stream().collect(Collectors.toMap(Foo::getKey, Function.identity()));
        System.out.println(m);
    }
}

class Foo {
    String name;
    Foo(String name){this.name = name;}
    String getKey() {return name;}
}

来自参考资料的重要提示:

返回的Collector不是并发的。对于并行流水线,组合器函数通过将一个映射中的键合并到另一个映射中来操作,这可能是一项昂贵的操作。如果不需要按照遇到的顺序将结果插入到Map中,使用toConcurrentMap(Function, Function)可能会提供更好的并行性能。


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