Javaで2つの文字列セットを組み合わせるより良い方法はありますか?


91

冗長な情報を除外しながら2つの文字列セットを組み合わせる必要があります。これは私が思いついた解決策です。誰かが提案できるより良い方法はありますか?おそらく私が見落としていた何かが組み込まれていますか?グーグルで運がなかった。

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

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

回答:


117

aにSetは重複するエントリが含まれていないため、次の方法で2つを組み合わせることができます。

newStringSet.addAll(oldStringSet);

物事を2回追加するかどうかは関係ありません。セットには、要素が1回だけ含まれます...たとえば、containsメソッドを使用してチェックする必要はありません。


92

あなたはこのワンライナーを使用してそれを行うことができます

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 2018

7
@KKlalala、あなたの要件はどちらが良いかを決定します。addAllStreamsの使用と使用の主な違いは次のとおりです。•使用set1.addAll(set2)すると、の内容が物理的に変更されるという副作用がありset1ます。•ただし、Streamsを使用Setすると、元のSetインスタンスのいずれも変更せずに、常に両方のセットのコンテンツを含む新しいインスタンスが作成されます。私見この答えは、元の内容を期待しながら他の場所で使用された場合に、元のセットに対する副作用や予期しない変更の可能性を回避するため、より優れています。HTH
edwardsmatt

1
これには、不変セットをサポートするという利点もあります。参照:docs.oracle.com/javase/8/docs/api/java/util/...
edwardsmatt


12

定義から、セットには一意の要素のみが含まれます。

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;
}

7

Guavaを使用している場合は、ビルダーを使用して柔軟性を高めることもできます。

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操作はこのセットを効果的に変更して、その値が2つのセットの和集合になるようにします。操作の進行中に指定されたコレクションが変更された場合、この操作の動作は未定義です。

newStringSet.addAll(oldStringSet)

2

パフォーマンスを重視し、2つのセットを保持する必要がなく、そのうちの1つが巨大になる可能性がある場合は、どちらのセットが最大かを確認し、最小から要素を追加することをお勧めします。

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がある場合、100000ではなく10回の操作のみを実行します。


これは次のように、これはメインのaddAllメソッドparametterではない理由を私は想像できないことが非常に良いロジックですpublic boolean addAll(int index, Collection<? extends E> c, boolean checkSizes)
ガスパル

仕様自体のためだと思います:指定されたコレクション内のすべての要素をこのコレクションに追加します。確かに別のメソッドを使用することもできますが、オーバーロードするメソッドと同じ仕様に従わないと、かなり混乱します。
リコラ

はい、私が言っていた他のメソッドのオーバーロードその1
ガスパル

2

Apache Commonを使用している場合はSetUtilsorg.apache.commons.collections4.SetUtils;

SetUtils.union(setA, setB);

これは、SetView不変のを返すことに注意してください。
jaco06 4620年

また、aからsize()を取得することSetViewは、常に線形演算です。
ジャグボット

2
Set.addAll()

指定されたコレクション内のすべての要素がまだ存在しない場合は、このセットに追加します(オプションの操作)。指定されたコレクションもセットである場合、addAll操作はこのセットを効果的に変更して、その値が2つのセットの和集合になるようにします。

newStringSet.addAll(oldStringSet)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.