如何在Java中将数组转换为Set

829

我希望能够在Java中将一个数组转换为Set。有一些明显的方法可以做到这一点(即使用循环),但我想要更加简洁的方式,就像:

java.util.Arrays.asList(Object[] a);
任何想法?
19个回答

1398
像这样:
Set<T> mySet = new HashSet<>(Arrays.asList(someArray));

在Java 9+中,如果不可修改的集合是可以接受的:
Set<T> mySet = Set.of(someArray);

在Java 10+中,泛型类型参数可以从数组的组件类型中推断出来。
var mySet = Set.of(someArray);

小心

Set.of 抛出 IllegalArgumentException - 如果 someArray 中存在重复元素。 更多详情请参见:https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Set.html#of(E...)

如果你想要一个不可修改的集合,并且数组中可能有重复元素,请按照以下步骤进行:

var mySet = Set.copyOf(Arrays.asList(array));

11
不需要最后一个<T>,不然这个一行代码很好! - despot
172
错误;Arrays.asList 的时间复杂度为 O(1)。 - SLaks
90
请注意,如果您在原始数据类型数组(如int[])上使用此方法,它将返回一个List<int[]>,因此您应该使用包装类以获得预期的行为。 - T. Markle
6
那只适用于番石榴。 - SLaks
10
几乎每次我都会选择易读性而不是效率:http://blog.codinghorror.com/hardware-is-cheap-programmers-are-expensive/ - David Carboni
显示剩余12条评论

246
Set<T> mySet = new HashSet<T>();
Collections.addAll(mySet, myArray);

这是JDK 6中的Collections.addAll(java.util.Collection, T...)

另外,如果我们的数组装满了基本数据类型呢?

对于JDK < 8,我会编写明显的for循环,在一次操作中进行包装并添加到集合中。

对于JDK >= 8,可供选择的一个不错选项是:

Arrays.stream(intArray).boxed().collect(Collectors.toSet());

5
你可以使用 java.util.Collections.addAll 来完成这个操作。另外,我不建议再使用 Commons Collections,因为它没有泛型,并且现在已经有了 Guava。 - ColinD
14
虽然不是一行代码,但因为比SLaks的答案更有效率而获得了+1的评价。请注意,本翻译仅涉及内容的转换,不包括任何额外的解释或其他信息。 - Adrian
1
@Adrian 我对此有疑问。我认为 addAll 的时间复杂度是 O(n)。 - Steve Powell
1
我相信Adrian的观点是关于SLaks的解决方案创建了一个最终被丢弃的List实例。这种差异的实际影响可能极其微小,但可能取决于您执行此操作的上下文--在这两个选项之间,紧密循环或非常大的集合可能会表现出非常不同的行为。 - JavadocMD
15
根据Java 6的Collections.addAll() javadoc:“这个方便的方法的行为与c.addAll(Arrays.asList(elements))相同,但是在大多数实现中,此方法可能会运行得更快。” - Bert F
显示剩余4条评论

127

使用Guava,您可以做到:

T[] array = ...
Set<T> set = Sets.newHashSet(array);

28
也可以使用ImmutableSet.copyOf(array)来实现。 (我想强调一下也这个词,我猜。) - Kevin Bourrillion
对于一个固定的元素列表,您可以使用:ImmutableSet.of(e1,e2,...,en)。请注意,在创建后,您将无法更改此集合。 - Pikachu
2
请注意,Guava javadoc 表示:“此方法实际上并不是非常有用,并且很可能在未来被弃用。” 它们指向标准的 new HashSet<T>(Arrays.asList(someArray))。请参见 https://google.github.io/guava/releases/19.0/api/docs/com/google/common/collect/Sets.html#newHashSet(E...)。 - Alexander Klimetschek

71

Java 8:

String[] strArray = {"eins", "zwei", "drei", "vier"};

Set<String> strSet = Arrays.stream(strArray).collect(Collectors.toSet());
System.out.println(strSet);
// [eins, vier, zwei, drei]

2
这个任务值得并行处理吗? - khatchad
@RaffiKhatchadourian 这并不一定是并行执行的。Arrays.stream 在流上没有做出任何承诺。你需要在结果流上调用 parallel() 方法来实现并行化。 - Felix S
你也可以调用parallelStream()。回答@RaffiKhatchadourian的问题,可能不需要。尝试测量一下是否注意到任何性能问题。 - Randy the Dev
6
一般情况下,避免使用并行处理。默认情况下,它在整个应用程序中使用单个线程池,而启动和加入线程的开销比顺序流式处理数百个项目还要糟糕。只有在极少数情况下,并行实际上才会带来好处。 - tkruse

51

可变参数也可以使用!

Stream.of(T... values).collect(Collectors.toSet());

2
比2-3行代码好得多。 - senseiwu

37

Java 8

我们还可以使用Stream。我们可以通过多种方式获取流:

Set<String> set = Stream.of("A", "B", "C", "D").collect(Collectors.toCollection(HashSet::new));
System.out.println(set);

String[] stringArray = {"A", "B", "C", "D"};
Set<String> strSet1 = Arrays.stream(stringArray).collect(Collectors.toSet());
System.out.println(strSet1);

// if you need HashSet then use below option.
Set<String> strSet2 = Arrays.stream(stringArray).collect(Collectors.toCollection(HashSet::new));
System.out.println(strSet2);
Collectors.toSet()方法源代码显示,元素逐个添加到一个HashSet中,但规范不保证它将是一个HashSet

“返回的Set的类型、可变性、可序列化性或线程安全性都没有任何保证。”

因此最好使用后一种选项。输出结果为:[A, B, C, D] [A, B, C, D] [A, B, C, D]

不可变Set(Java 9)

Java 9引入了Set.of静态工厂方法,为提供的元素或数组返回不可变的Set。
@SafeVarargs
static <E> Set<E> of​(E... elements)

详情请参阅Immutable Set静态工厂方法

Immutable Set(Java 10)

我们还可以通过两种方式获取不可变的集合:

  1. Set.copyOf(Arrays.asList(array))
  2. Arrays.stream(array).collect(Collectors.toUnmodifiableList());

方法Collectors.toUnmodifiableList()在内部使用了Java 9中引入的Set.of。更多信息请参见我的回答


1
+1 for Stream.of() - 我不知道这个。关于Collectors.toSet()的一个小问题:你说规范不能保证逐个添加元素,但这就是“将...累积到新的Set中”的意思。而且它更易读 - 在我看来更可取,如果你不需要具体类型、可变性、可序列化性和线程安全性的保证。 - Andrew Spencer
@AndrewSpencer 规范并不保证集合实现将是 HashSet。它只保证它将是一个 Set,这就是我的意思。希望我已经澄清了。 - akhil_mittal
抱歉,谢谢,我误解了它的意思,认为“规范不保证逐个添加”,而不是“规范不保证HashSet”。建议进行编辑以澄清。 - Andrew Spencer

19

在执行Arrays.asList(array)之后,你可以执行Set set = new HashSet(list);

这里是一段示例代码,你可以编写:

public <T> Set<T> GetSetFromArray(T[] array) {
    return new HashSet<T>(Arrays.asList(array));
}

我希望有一种方法可以直接从数组返回一个集合,这样的方法存在吗? - user130076
1
如果你非常渴望的话,你可以自己编写:) - Petar Minchev

18

已经有许多优秀的答案,但大多数都不适用于基本类型数组(如int[]long[]char[]byte[]等)。

在Java 8及以上版本中,您可以使用以下方式对该数组进行装箱:

Integer[] boxedArr = Arrays.stream(arr).boxed().toArray(Integer[]::new);

然后使用流将其转换为集合:

Stream.of(boxedArr).collect(Collectors.toSet());

12
Eclipse Collections 中,以下内容可以正常工作:
Set<Integer> set1 = Sets.mutable.of(1, 2, 3, 4, 5);
Set<Integer> set2 = Sets.mutable.of(new Integer[]{1, 2, 3, 4, 5});
MutableSet<Integer> mutableSet = Sets.mutable.of(1, 2, 3, 4, 5);
ImmutableSet<Integer> immutableSet = Sets.immutable.of(1, 2, 3, 4, 5);

Set<Integer> unmodifiableSet = Sets.mutable.of(1, 2, 3, 4, 5).asUnmodifiable();
Set<Integer> synchronizedSet = Sets.mutable.of(1, 2, 3, 4, 5).asSynchronized();
ImmutableSet<Integer> immutableSet = Sets.mutable.of(1, 2, 3, 4, 5).toImmutable();

注意:我是 Eclipse Collections 的提交者


7
我从上面的建议中撷取了以下内容,您可以参考…非常好!
/**
 * Handy conversion to set
 */
public class SetUtil {
    /**
     * Convert some items to a set
     * @param items items
     * @param <T> works on any type
     * @return a hash set of the input items
     */
    public static <T> Set<T> asSet(T ... items) {
        return Stream.of(items).collect(Collectors.toSet());
    }
}

对于上述情况,Arrays.stream 可能比 Stream.of 更好。 - Ashley Frieze

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