如何在 Java 中将多个(任意数量的)Set<String> 合并为一个?

问题描述 投票:0回答:6

我有多个

Set<String>
需要合并成一个
Set<String>
。我如何在 Java 中执行此操作?请注意,我正在尽我所能使用
guava
API 来提供帮助。例如,我有 3 个类如下。

public class One {
 public static Set<String> SET = Sets.newHashSet("a","b","c");
}
public class Two {
 public static Set<String> SET = Sets.newHashSet("a","d","e","f");
}
public class Three {
 public static Set<String> SET = Sets.newHashSet("w","x","y","f");
}

现在,我需要将这些集合的任意组合合并为一个。例如,我可能需要合并

  • One.SET
    +
    Two.SET
    +
    Three.SET
    合并为一个生成 { "a","b","c","d","e","f","w","x"," y" },
  • One.SET
    +
    Three.SET
    合二为一产生 { "a","b","c","w","x","y","f" },
  • Two.SET
    +
    Three.SET
    合二为一产生 { "a","d","e","f","w","x","y" },
  • 等等

我创建了一个方法来合并一组集合,

Set<String>[]
,但这不起作用(在 SO 帖子 Creating an array of Sets in Java 中解释)。这是要合并的代码。它有效(编译)。

public static Set<String> immutableSetOf(Set<String>[] sets) {
 Set<String> set = new HashSet<String>();
 for(Set<String> s : sets) {
  set.addAll(s);
 }
 return ImmutableSet.copyOf(set);
}

这是调用代码;它不起作用(无法编译)。

Set<String> set = Utils.immutableSetOf(new Set<String>[] { One.SET, Two.SET });

所以,我修改了我的合并方法来对

List<Set<String>>
而不是
Set<String>[]
进行操作。只有参数类型发生了变化,但为了完整起见,我把它放在这里。

public static Set<String> immutableSetOf(List<Set<String>> sets) {
 Set<String> set = new HashSet<String>();
 for(Set<String> s : sets) {
  set.addAll(s);
 }
 return ImmutableSet.copyOf(set);
}

所以,现在我的调用代码如下所示。

Set<String> set = Utils.immutableSetOf(
 Lists.newArrayList(
  One.SET, Two.SET));

此代码无法编译,因为

Lists.newArrayList(...)
返回的是
Set<String>
而不是
List<Set<String>>
。方法
Lists.newArrayList(...)
被重载了,我传入集合时使用的方法的签名是,
List.newArrayList(Iterable<? extends E> elements)
.

那么,问题是,在考虑调用代码的同时,如何定义一个方法来合并任意数量的

Set<String>
?我注意到编译问题出在调用代码上(而不是合并方法),但也许解决方案也与合并代码有关?

更新:我也尝试过 varargs 但它会产生自己的警告(是否有可能解决“为可变参数创建一个通用的 T 数组”编译器警告?)。合并方法签名现在如下。

public static Set<String> immutableSetOf(Set<String>... sets)

现在调用代码如下,我得到“类型安全:为可变参数参数创建了一个通用的集合数组”。

Set<String> set = Utils.immutableSetOf(One.SET, Two.SET);

更新:对于接受的答案,我做了以下事情。

@SuppressWarnings("unchecked")
Set<String> set = Utils.immutableSetOf(Set[] { One.SET, Two.SET });
java collections guava
6个回答
6
投票

推荐

com.google.common.collect.Sets#union(set1, set2)
进行合并,而不是
Set.addAll
在引擎盖下,因为番石榴已经在您的依赖项中。

原因:它是view,它是内存有效的,也是不可修改的

加:我应该把它作为评论发布,对不起。


3
投票

如何创建输入集的集合,然后使用

flatMap

Set<String> allElements = ImmutableSet.of(
        One.SET,
        Two.SET,
        Three.SET
).stream().flatMap(Collection::stream).collect(Collectors.toSet());

2
投票

在你之前的尝试中,你已经写了

Set<String> set = Utils.immutableSetOf(new Set<String>[] { One.SET, Two.SET });

这不起作用,因为在 java 中不允许创建泛型数组

试试这个:

@SuppressWarnings("unchecked")
Set<String>[] mySet = new Set[] { One.SET, Two.SET };
Set<String> set = Utils.immutableSetOf(mySet);

之所以可行,是因为我们创建了一个新的 Set[] 而没有指定泛型类型并将其分配给引用 Set[] mySet(因为这是一个未经检查的操作,我们必须添加 @SuppressWarnings("unchecked") 到它)


1
投票

您可以使用:

Set<String> set = immutableSetOf(Arrays.asList(One.SET, Two.SET));

使用你对

immutableSetOf(List<Set<String>> sets)
的定义。没有警告或抑制警告。


1
投票

我在想

FluentIterable
(在 18.0 及更高版本中)可以在这里提供帮助,特别是
append
方法

有了这个,我们需要定义一个方便的辅助方法 - 是的,我们将为此使用可变参数。

public <T extends Comparable<T>> Set<T> mergeSets(Set<T> initial,
                                                 Set<T>... rest) {
    FluentIterable<T> result =  FluentIterable.from(initial);
    for(Set<T> set : rest) {
        result = result.append(set);
    }
    return new TreeSet<>(result.toSet());
}

这里的结果集是自然顺序的。如果您不想要

TreeSet
并且之后不想改变您的收藏,则省略
new TreeSet<>
部分,并放松
T
上的绑定。


0
投票

这应该有帮助。 java8 中的在线器 -> Stream.of(sets).flatMap(Set::stream).collect(Collectors.toSet())

© www.soinside.com 2019 - 2024. All rights reserved.