2つの文字列配列から共通の要素を返す最も効率的な方法

Javaでは、2つの文字列配列から共通の要素を返す最も効率的な方法は何ですか?私はforループのペアでそれを行うことができますが、それは非常に効率的ではないようです。 10

6 答え

EDITED:

これは1ライナーです:

compareList.retainAll(new HashSet(baseList));

retainAll impl(AbstractCollection内)は this を反復し、引数に contains()を使用します。引数を HashSet にすると、高速検索が行われるため、 retainAll 内のループはできるだけ早く実行されます。

また、 baseList という名前は定数であることを暗示しているので、これをキャッシュするとパフォーマンスが大幅に向上します。

static final Set BASE = Collections.unmodifiableSet(new HashSet(Arrays.asList("one", "two", "three", "etc")));

static void retainCommonWithBase(Collection strings) {
    strings.retainAll(BASE);
}

元のリストを保持する場合は、次のようにします。

static List retainCommonWithBase(List strings) {
   List result = new ArrayList(strings);
   result.retainAll(BASE);
   return result;
}
5
追加された
@SebastienLorberそれを指摘してくれてありがとう。私はあなたのコメントを私の編集に取り入れました
追加された 著者 Bohemian,
retainAllはセットを反復するように見え、セットをルックアップしません(ちょっと変わっていますが、これはすべてのハッシュベースのコレクションに対してオーバーライドされている可能性があります)
追加された 著者 Sebastien Lorber,

私はHashSetsを使用します(そして retainAll ) 1)を参照してください。 ( HashSet は衝突に対処しなければならないかもしれませんが...) List

Set List には異なるセマンティクスがあります(リストは重複要素、nullsを許しています)。

3
追加された
しかし、ルックアップを実行する必要はありません。要素を順番に反復処理する必要があります。
追加された 著者 Oliver Charlesworth,
ブルートフォースよりは良いが、それほど真実ではない。ハッシュセットへの挿入はO(1)ではなく、衝突のおかげでほとんどの場合O(n)でさえありません。
追加された 著者 josefx,

両方の配列をソートします。

ソートされたら、2つのインデックスを使用して、両方のソート済み配列を正確に1回反復処理できます。

これはO(NlogN)になります。

3
追加された
ハッシュの敷物の下で問題を掃除してくれてありがとう、ありがとう:)
追加された 著者 necromancer,
@Burleighあなたはn log nにどのように到着しますか?
追加された 著者 non sequitor,
表記を悪用して多かれ少なかれ:)配列をソートするには、配列の長さにNlogNを指定します。漸近的には、より長い配列を考える必要があります - それをNと呼んでみましょう(そして、文字列間の比較は固定コストであると仮定しましょう。これも真ではありません)。ソートステージはO(NLogN)です。共通の要素を見つけるためには、配列を順番に(O(N)だけ)トラバースすることができます。ここでも、比較は固定コストと仮定しています。私は順序がO(MNlogN)であると言うのがより正確だと思います。ここで、Mはどちらの配列でも一番長い文字列の長さです。
追加された 著者 Burleigh Bear,

What you want is called intersection. See that: Intersection and union of ArrayLists in Java

Hashベースのコレクションを使用すると、特に、最適化されたハッシュコードを持つ文字列では、実際にはより高速なcontains()メソッドが提供されます。


ライブラリをインポートできる場合は、GuavaのSets.intersectionを使用することを検討できます。


編集:

retainAllメソッドについて知らなかった。

HashSetとLinkedHashSetsでオーバーライドされていないと思われるAbstractCollectionの実装は次のとおりです。

public boolean retainAll(Collection c){        boolean modified = false;        イテレータit = iterator();        while(it.hasNext()){            if(!c.contains(it.next())){                it.remove();                modified = true;            }        }        return modified;    }

つまり、コレクションのパラメータにcontains()を呼び出します。 つまり、Listパラメーターを渡すと、繰り返しのたびに、リストの多くのアイテムでequalsコールが発生します。

これは、私が上記のretainAllを使った実装が良いとは思わない理由です。

public  List intersection(List list1, List list2) {
    boolean firstIsBigger = list1.size() > list2.size();
    List big =  firstIsBigger ? list1:list2;
    Set small =  firstIsBigger ? new HashSet(list2) : new HashSet(list1);
    return big.retainsAll(small)
}

最小セットのセットを使用することを選択するのは、セットを構築する方が速いので、大きなリストはかなりうまく反復するからです。

元のリストパラメータの1つが変更される可能性があることに注意してください。コピーを作成するのはあなた次第です...

1
追加された
mmm ArrayListのretainAllメソッドがオーバーライドされます。私はここにIDEを持っていないが、これは私がjavadocで見つけたものです
追加された 著者 Sebastien Lorber,

保持するすべてがリストによってサポートされていません。代わりにセットを使用してください:

import java.util.*;
public class Main {
    public static void main(String[] args) {
        String[] strings1={"a","b","b","c"},strings2={"b","c","c","d"};
        List list=Arrays.asList(strings1);
        //list.retainAll(Arrays.asList(strings2));//throws UnsupportedOperationException
        //System.out.println(list);
        Set set=new LinkedHashSet(Arrays.asList(strings1));
        set.retainAll(Arrays.asList(strings2));
        System.out.println(set);
    }
}
1
追加された
少なくともJDK7では、次のように見えます。 docjar.com/ html/api/java/util/ArrayList.java.html
追加された 著者 Sebastien Lorber,
そうかも知れない。私はまだJava 6を使用しています
追加された 著者 Ray Tayek,

私はインタビューを受けました。この質問は、技術面接の際に私に尋ねたことでした。私の答えは、コード行に従っていた:

public static void main(String[] args) {

        String[] temp1 = {"a", "b", "c"};
        String[] temp2 = {"c", "d", "a", "e", "f"};
        String[] temp3 = {"b", "c", "a", "a", "f"};

        ArrayList list1 = new ArrayList(Arrays.asList(temp1));
        System.out.println("list1: " + list1);
        ArrayList list2 = new ArrayList(Arrays.asList(temp2));
        System.out.println("list2: " + list2);
        ArrayList list3 = new ArrayList(Arrays.asList(temp3));
        System.out.println("list3: " + list3);

        list1.retainAll(list2);
        list1.retainAll(list3);
        for (String str : list1)
            System.out.println("Commons: " + str);
}

出力:

list1: [a, b, c]
list2: [c, d, a, e, f]
list3: [b, c, a, a, f]
Commons: a
Commons: c
0
追加された
あなたのlist1とlist2は要素を印刷せず、アドレスを出力します。
追加された 著者 Bruce Jinru Su,