web-dev-qa-db-ja.com

Java 7 and 8?

私が持っている場合:

_List<Integer> listInts = { 1, 1, 3, 77, 2, 19, 77, 123, 14, 123... }
_

in Java listIntsからdistinct値のみを含む_List<Integer> listDistinctInts_を作成する効率的な方法は何ですか?

私の当面の考えは、listIntsのすべての値を含む_Set<Integer> setInts_を作成し、List<Integer> listDistinctInts = new ArrayList<>(setInts);を呼び出すことです。

しかし、これは潜在的に非効率的なようです-Java 7?

私はJava 8を使用していませんが、それを使用すると私はこのようなことをすることができると信じています(?):

_List<Integer> listDistinctInts = listInts.stream().distinct().collect(Collectors.toList());
_

これは上記のアプローチよりもパフォーマンスが向上しますか、および/またはJava 8?

最後に、countの個別の要素のみを気にした場合、 listIntsは、Java 7および8)でその値を取得するより効率的な方法があります-最初にすべての別個の要素のリストまたはセットを作成しなくてもよいですか?

私はネイティブのJavaこれを達成し、ホイールの再発明を避ける方法に最も興味がありますが、より良い明瞭性またはパフォーマンスを提供する場合は手巻きのコードまたはライブラリを検討します。関連する質問 Java-個別のオブジェクトのリスト しかし、Java 7と8のアプローチの間のパフォーマンスの違いや、より良いテクニックがあるかどうかについては完全に明確ではありませんか?

20
Matt Coubrough

提供された優れた回答から、提案されたオプションのほとんどをMicroBenchmarkedしました。パフォーマンスに関連するほとんどの重要な質問と同様に、どちらが最適かという答えは"it depend"です。

すべてのテストは [〜#〜] jmh [〜#〜]Java Microbenchmarking Harness を使用して実行されました。

これらのテストのほとんどはJDK 1.8を使用して実行されましたが、JDK 1.7を使用して一部のテストも実行しましたが、そのパフォーマンスがあまり変わらないことを確認するためです(ほぼ同一でした)。これまでに提供された回答から取った以下の手法をテストしました。


1。 Java 8 Stream-stream()を使用するソリューションJava8を使用する場合、可能性として提案しました:

public List<Integer> testJava8Stream(List<Integer> listInts) {
    return listInts.stream().distinct().collect(Collectors.toList());
}

prosモダンJava 8アプローチ、サードパーティの依存関係なし

consJava 8が必要


2。リストへの追加- Victor2748によって提案されたソリューション リストに値がまだ含まれていない場合にのみ、新しいリストが作成および追加されます。また、再割り当てを防ぐために、元のサイズ(可能な限り最大)で宛先リストを事前に割り当てます。

public List<Integer> testAddingToList(List<Integer> listInts) {
    List<Integer> listDistinctInts = new ArrayList<>(listInts.size());
    for(Integer i : listInts)
    {
        if( !listDistinctInts.contains(i) ) { listDistinctInts.add(i); }
    }
    return listDistinctInts;
}

prosどのJavaバージョンでも動作し、セットを作成してコピーする必要はなく、サードパーティの依存関係はありません

cons作成時に既存の値をリストで繰り返し確認する必要があります


3。 GS Collections Fast(現在のEclipseコレクション)- Craig P. Motlinによって提案されたソリューションGS Collectionsライブラリを使用 およびカスタムリストタイプFastList

public List<Integer> testGsCollectionsFast(FastList listFast)
{
    return listFast.distinct();
}

pros伝えられるところでは非常に高速で単純な表現コード、Java 7および8で動作します

consサードパーティのライブラリと、通常のList<Integer>ではなく、FastListが必要です


4。 GS Collections Adapted-FastListソリューションは、従来のArrayList<Integer>ではなくFastListをメソッドに渡す必要があるため、like-for-likeを比較していませんでした。そのため、アダプターメソッドもテストしました。クレイグは提案した:

public List<Integer> testGsCollectionsAdapted(List<Integer> listInts)
{
    return listAdapter.adapt(listInts).distinct();
}

prosFastListを必要とせず、Java 7および8で動作します

consリストを調整する必要があるため、パフォーマンスが低下する可能性があり、サードパーティのライブラリが必要です


5。 Guava ImmutableSet- Louis Wassermanによって提案された方法 コメントで、および 卢声远Shengyuan Lu によって、 Guavaを使用した回答

public List<Integer> testGuavaImmutable(List<Integer> listInts)
{
    return ImmutableSet.copyOf(listInts).asList();
}

pros報告によると非常に高速で、Java 7または8で動作します

consImmutable Listを返し、入力リストでnullを処理できず、サードパーティライブラリが必要です


7。 HashSet-元のアイデア( EverV0idulix およびRadiodefでも推奨)

public List<Integer> testHashSet(List<Integer> listInts)
{
    return new ArrayList<Integer>(new HashSet<Integer>(listInts));
}

prosJava 7および8で動作し、サードパーティの依存関係なし

consリストの元の順序を保持せず、セットを構築してからリストにコピーする必要があります。


6。 LinkedHashSet-HashSetソリューションは元のリストの整数の順序を保持しなかったため、LinkedHashSetを使用して順序を保持するバージョンもテストしました。

public List<Integer> testLinkedHashSet(List<Integer> listInts)
{
    return new ArrayList<Integer>(new LinkedHashSet<Integer>(listInts));
}

pros元の順序を保持し、Java 7および8で動作し、サードパーティの依存関係なし

cons通常のHashSetアプローチと同じくらい高速になる可能性は低い} _


Results

さまざまなサイズのlistIntsの結果を次に示します(結果は最も遅いものから最も速いものへと並べられています)。

1. 0〜50,000の100,000のランダムなintのArrayListとは異なります(大きなリスト、いくつかの重複)

Benchmark                Mode       Samples     Mean   Mean error    Units

AddingToList            thrpt        10        0.505        0.012    ops/s
Java8Stream             thrpt        10      234.932       31.959    ops/s
LinkedHashSet           thrpt        10      262.185       16.679    ops/s
HashSet                 thrpt        10      264.295       24.154    ops/s
GsCollectionsAdapted    thrpt        10      357.998       18.468    ops/s
GsCollectionsFast       thrpt        10      363.443       40.089    ops/s
GuavaImmutable          thrpt        10      469.423       26.056    ops/s

2. 0〜50の1000のランダムな整数のArrayListとは異なります(つまり、中規模のリスト、多くの重複)

Benchmark                Mode       Samples     Mean   Mean error    Units

AddingToList            thrpt        10    32794.698     1154.113    ops/s
HashSet                 thrpt        10    61622.073     2752.557    ops/s
LinkedHashSet           thrpt        10    67155.865     1690.119    ops/s
Java8Stream             thrpt        10    87440.902    13517.925    ops/s
GsCollectionsFast       thrpt        10   103490.738    35302.201    ops/s
GsCollectionsAdapted    thrpt        10   143135.973     4733.601    ops/s
GuavaImmutable          thrpt        10   186301.330    13421.850    ops/s

. 0から100までの100のランダムな整数のArrayListとは異なります(小さなリスト、いくつかの重複)

Benchmark                Mode       Samples     Mean   Mean error    Units

AddingToList            thrpt        10   278435.085    14229.285    ops/s
Java8Stream             thrpt        10   397664.052    24282.858    ops/s
LinkedHashSet           thrpt        10   462701.618    20098.435    ops/s
GsCollectionsAdapted    thrpt        10   477097.125    15212.580    ops/s
GsCollectionsFast       thrpt        10   511248.923    48155.211    ops/s
HashSet                 thrpt        10   512003.713    25886.696    ops/s
GuavaImmutable          thrpt        10  1082006.560    18716.012    ops/s

4. 0〜50の10個のランダムな整数のArrayListとは異なります(小さなリスト、重複が少ない)

Benchmark                Mode       Samples     Mean   Mean error    Units

Java8Stream             thrpt        10  2739774.758   306124.297    ops/s
LinkedHashSet           thrpt        10  3607479.332   150331.918    ops/s
HashSet                 thrpt        10  4238393.657   185624.358    ops/s
GsCollectionsAdapted    thrpt        10  5919254.755   495444.800    ops/s
GsCollectionsFast       thrpt        10  7916079.963  1708778.450    ops/s
AddingToList            thrpt        10  7931479.667   966331.036    ops/s
GuavaImmutable          thrpt        10  9021621.880   845936.861    ops/s

結論

  • リストから個別のアイテムを一度だけ取得し、リストがあまり長くない場合は、これらのメソッドのanyで十分です。

  • 最も効率的な一般的なアプローチは、サードパーティのライブラリから来ました。GSCollectionsとGuavaは見事に機能しました。

  • 最もパフォーマンスの高い方法を選択するときは、リストのサイズと重複の可能性のある数を考慮する必要がある場合があります。

  • 値がまだリストにない場合にのみ新しいリストに追加する素朴なアプローチは、小さなリストに対してはうまく機能しますが、入力リストにいくつかの値以上があるとすぐに、試行された最悪の方法を実行します。

  • Guava ImmutableSet.copyOf(listInts).asList()メソッドは、ほとんどの状況で最も速く動作します。ただし、制限に注意してください。返されるリストはImmutableであり、入力リストにnullを含めることはできません。

  • HashSetメソッドは、サードパーティ以外のアプローチのベストを実行し、通常Java 8ストリームよりも優れていますが、整数を並べ替えます(ユースケースに応じて問題になる場合もあれば、そうでない場合もあります) )。

  • LinkedHashSetアプローチは順序付けを維持しますが、通常、HashSetメソッドよりも当然のことながら驚くべきことではありませんでした。

  • 複雑なHashCode計算を持つデータ型のリストを使用すると、HashSetメソッドとLinkedHashSetメソッドの両方のパフォーマンスが低下するため、個別のFoosを選択する場合は独自のプロファイリングを実行してくださいList<Foo>から。

  • 既に GS Collections を依存関係として持っている場合、それは非常にうまく機能し、ImmutableList Guava アプローチよりも柔軟です。依存関係として持っていない場合、アプリケーションのパフォーマンスにとって明確なアイテムの選択のパフォーマンスが重要である場合、追加することを検討する価値があります。

  • 残念なことに、Java 8ストリームのパフォーマンスはかなり低下したようです。 distinct()呼び出しをコーディングするよりも、私が使用した方法よりも良い方法があるかもしれないので、コメントやその他の回答はもちろん歓迎します。

注:私はMicroBenchmarkingの専門家ではありませんので、誰かが私の結果や方法論に欠陥を見つけた場合は私に通知してください。回答を修正するよう努めます。

30
Matt Coubrough

Eclipse Collections (以前の GS Collections )を使用している場合は、distinct()メソッドを使用できます。

_ListIterable<Integer> listInts = FastList.newListWith(1, 1, 3, 77, 2, 19, 77, 123, 14, 123);
Assert.assertEquals(
        FastList.newListWith(1, 3, 77, 2, 19, 123, 14),
        listInts.distinct());
_

Setに変換してからリストに戻す代わりにdistinct()を使用する利点は、distinct()が元のリストの順序を保持し、各要素の最初の出現を保持することです。 SetとListの両方を使用して実装されます。

_MutableSet<T> seenSoFar = UnifiedSet.newSet();
int size = list.size();
for (int i = 0; i < size; i++)
{
    T item = list.get(i);
    if (seenSoFar.add(item))
    {
        targetCollection.add(item);
    }
}
return targetCollection;
_

元のリストをGSコレクションタイプに変換できない場合、ListAdapterを使用して同じAPIを取得できます。

_MutableList<Integer> distinct = ListAdapter.adapt(integers).distinct();
_

セットの作成を回避する方法はありません。それでも、UnifiedSetはHashSetよりも効率的であるため、速度がいくらか向上します。

必要なのがnumber個の個別のアイテムだけである場合、リストを作成せずにセットを作成する方が効率的です。

_Verify.assertSize(7, UnifiedSet.newSet(listInts));
_

Eclipse Collections 8.0にはJava 8が必要です。EclipseCollections 7.xはJava 8で問題なく動作しますが、Java 5のみが必要です。

注:私はEclipseコレクションのコミッターです。

3
Craig P. Motlin

グアバ は選択可能です:

_ImmutableSet<Integer> set = ImmutableSet.copyOf(listInts);
_

APIは非常に最適化されています。

listInts.stream().distinct()およびnew LinkedHashSet<>(listInts)よりも高速です。

new LinkedList(new HashSet(listInts))を試してください。

1
Everv0id

listIntsチェックに値を追加する場合:

int valueToAdd;
//...
if (!listInts.contains(valueToAdd)) {listInts.add(valueToAdd)}

既存のリストがある場合は、for-eachステートメントを使用して、そのリストから「区別」したいすべての値を新しいリストにコピーします。

List<Integer> listWithRepeatedValues;
List<Integer> distinctList;
//...
for (Integer i : listWithRepeatedValues) {
    if (!listInts.contains(valueToAdd)) {distinctList.add(i);}
}
0
Victor2748

これは動作するはずです:

yourlist.stream()。map(equalsおよびhashchodeメソッドをオーバーライドするラッパー:: new).distinct()。map(上記で定義されたラッパー::最終出力を返すメソッド).collect(Collectors.toList());

0
Righto

心配しないで。 HashSetの使用は、重複を排除するための非常に簡単で効率的な方法です。

    Set<Integer> uniqueList = new HashSet<>();
    uniqueList.addAll(listInts);   // Add all elements eliminating duplicates

    for (int n : uniqueList)       // Check the results (in no particular order)
        System.out.println(n);

    System.out.println("Number distinct values: " + uniqueList.size());

より具体的なシナリオでは、可能な値の範囲がわかっている場合に備えて、listIntsが非常に大きい一方で、それほど大きくありません。
リスト内で考えられる一意のエントリの数を数える最も効率的な方法は次のとおりです。

    boolean[] counterTable = new boolean[124];
    int counter = 0;

    for (int n : listInts)
        if (!counterTable[n]) {
            counter++;
            counterTable[n] = true;
        }

    System.out.println("Number of distinct values: " + counter);
0
ulix