有没有更好的方法在Java中组合两个字符串集?


90

在过滤掉冗余信息时,我需要组合两个字符串集,这是我想出的解决方案,有没有人可以建议的更好方法?也许我忽略了内置的东西?谷歌没有任何运气。

Set<String> oldStringSet = getOldStringSet();
Set<String> newStringSet = getNewStringSet();

for(String currentString : oldStringSet)
{
    if (!newStringSet.contains(currentString))
    {
        newStringSet.add(currentString);
    }
}

Answers:


116

由于aSet不包含重复的条目,因此可以通过以下方式将两者合并:

newStringSet.addAll(oldStringSet);

两次添加都没有关系,该集合只包含一次元素...例如,不需要使用containsmethod进行检查。


88

您可以使用此一线

Set<String> combined = Stream.concat(newStringSet.stream(), oldStringSet.stream())
        .collect(Collectors.toSet());

使用静态导入,看起来更好

Set<String> combined = concat(newStringSet.stream(), oldStringSet.stream())
        .collect(toSet());

另一种方法是使用flatMap方法:

Set<String> combined = Stream.of(newStringSet, oldStringSet).flatMap(Set::stream)
        .collect(toSet());

同样,任何集合都可以轻松地与单个元素组合

Set<String> combined = concat(newStringSet.stream(), Stream.of(singleValue))
        .collect(toSet());

这比addAll好吗?
KKlalala '18

7
@KKlalala,您的要求将确定哪个更好。addAll与使用Streams之间的主要区别在于:•usingset1.addAll(set2)具有物理上更改内容的副作用set1。•但是,使用Streams将始终导致Set包含两个集合的内容的新实例,而无需修改任何原始Set实例。恕我直言,此答案更好,因为如果在预期原始内容的同时将其用于其他地方,则可以避免副作用和对原始内容进行意外更改的可能性。HTH
edwardsmatt

1
这也具有支持不可变集的优点。请参阅:docs.oracle.com/javase/8/docs/api/java/util/…–
edwardsmatt

34

用相同的番石榴

Set<String> combinedSet = Sets.union(oldStringSet, newStringSet)

2
Sets :: union是一个很棒的BinaryOperator,可与Collectors.reducing()一起使用。
mskfisher

12

在定义集中,Set仅包含唯一元素。

Set<String> distinct = new HashSet<String>(); 
 distinct.addAll(oldStringSet);
 distinct.addAll(newStringSet);

为了增强您的代码,您可以为此创建通用方法

public static <T> Set<T> distinct(Collection<T>... lists) {
    Set<T> distinct = new HashSet<T>();

    for(Collection<T> list : lists) {
        distinct.addAll(list);
    }
    return distinct;
}

6

如果您使用的是番石榴,则还可以使用构建器来获得更大的灵活性:

ImmutableSet.<String>builder().addAll(someSet)
                              .addAll(anotherSet)
                              .add("A single string")
                              .build();

4

只需使用newStringSet.addAll(oldStringSet)。无需检查重复项,因为Set实现已执行此操作。



3
 newStringSet.addAll(oldStringSet);

这将产生s1和s2的并集


2

使用boolean addAll(Collection<? extends E> c)
将指定集合中的所有元素(如果尚不存在)添加到该集合中(可选操作)。如果指定的集合也是一个集合,则addAll操作会有效地修改此集合,以使其值为两个集合的并集。如果在操作进行过程中修改了指定的集合,则此操作的行为是不确定的。

newStringSet.addAll(oldStringSet)

2

如果您关心性能,并且不需要保留两个集合而其中一个集合可能很大,那么我建议您检查哪个集合最大,并从最小的元素中添加元素。

Set<String> newStringSet = getNewStringSet();
Set<String> oldStringSet = getOldStringSet();

Set<String> myResult;
if(oldStringSet.size() > newStringSet.size()){
    oldStringSet.addAll(newStringSet);
    myResult = oldStringSet;
} else{
    newStringSet.addAll(oldStringSet);
    myResult = newStringSet;
}

这样,如果您的新集合有10个元素,而旧集合有100 000个元素,那么您将只执行10次运算,而不是100 000次。


这是一个非常好的逻辑,我无法想象为什么它不在主要的addAll方法参数中,例如public boolean addAll(int index, Collection<? extends E> c, boolean checkSizes)
Gaspar

我猜是由于规范本身:将指定集合中的所有元素添加到此集合中。您确实可以使用另一种方法,但是如果它没有遵循与其重载的方法相同的规范,那将非常令人困惑。
Ricola

是的,我是说其他方法重载一个
加斯帕


2
Set.addAll()

如果指定集合中的所有元素尚不存在,则将它们添加到此集合中(可选操作)。如果指定的集合也是一个集合,则addAll操作会有效地修改此集合,使其值是两个集合的并集

newStringSet.addAll(oldStringSet)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.