【问题标题】:How to merge multiple (arbitrary number of) Set<String> into one in Java?如何在 Java 中将多个(任意数量的)Set<String> 合并为一个?
【发布时间】:2015-01-31 12:53:08
【问题描述】:

我有多个Set&lt;String&gt;,需要合并为一个Set&lt;String&gt;。我如何在 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&lt;String&gt;[],但这不起作用(在此 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&lt;Set&lt;String&gt;&gt; 而不是 Set&lt;String&gt;[] 上进行操作。只有参数类型发生了变化,但为了完整起见,我把它放在这里。

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&lt;String&gt; 而不是 List&lt;Set&lt;String&gt;&gt;。方法Lists.newArrayList(...)被重载了,我传入set时使用的方法的签名是List.newArrayList(Iterable&lt;? extends E&gt; elements)

那么,问题是,在考虑调用代码的同时,如何定义一个方法来合并任意数量的Set&lt;String&gt;?我注意到编译问题出在调用代码上(不是合并方法),但也许解决方案也与合并代码有关?

更新:我也尝试了可变参数,但它会产生自己的警告 (Is it possible to solve the "A generic array of T is created for a varargs parameter" compiler warning?)。合并方法签名现在如下。

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

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

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

更新:对于接受的答案,我执行了以下操作。

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

【问题讨论】:

    标签: java collections guava


    【解决方案1】:

    推荐 com.google.common.collect.Sets#union(set1, set2) 来获得合并,而不是 Set.addAll,因为 Guava 已经在你的依赖项中。

    原因:是view记忆有效,也是不可修改

    另外:我应该把它作为一个评论发布,对不起。

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

      • 我会使用Stream.of(One.SET, Two.SET, Three.SET) 而不是ImmutableSet.of().stream() 以避免不必要地创建临时ImmutableSet
      【解决方案3】:

      在你之前的尝试中,你写过

      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" ) 到它)

      【讨论】:

      • 这绝对有效,但有没有办法同时指定泛型类型?
      • 据我所知,java 不允许这样做。另外,也没有必要,归根结底,我们需要有一个指定类型的引用,这是可以实现的。
      • 推荐 com.google.common.collect.Sets#union(set1, set2) 以获得合并而不是 Set.addAll 引擎盖,因为 Guava 已经在您的依赖项中。原因:视图是内存有效的,也是不可修改的。
      【解决方案4】:

      你可以使用:

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

      使用您对immutableSetOf(List&lt;Set&lt;String&gt;&gt; sets) 的定义。没有警告或抑制警告。

      【讨论】:

        【解决方案5】:

        我认为FluentIterable(18.0 及更高版本)可以在这里提供帮助,特别是append method

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

        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&lt;&gt; 部分,并放松对 T 的限制。

        【讨论】:

        • warning: [unchecked] Possible heap pollution from parameterized vararg type Set&lt;T&gt;
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-04-26
        • 2013-10-18
        • 1970-01-01
        • 2015-11-15
        • 2021-11-15
        • 2015-03-14
        • 2016-05-15
        相关资源
        最近更新 更多