私が持っている場合:
_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のアプローチの間のパフォーマンスの違いや、より良いテクニックがあるかどうかについては完全に明確ではありませんか?
提供された優れた回答から、提案されたオプションのほとんどを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-元のアイデア( EverV0id 、 ulix および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
アプローチと同じくらい高速になる可能性は低い} _
さまざまなサイズの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
メソッドの両方のパフォーマンスが低下するため、個別のFoo
sを選択する場合は独自のプロファイリングを実行してくださいList<Foo>
から。
既に GS Collections を依存関係として持っている場合、それは非常にうまく機能し、ImmutableList Guava アプローチよりも柔軟です。依存関係として持っていない場合、アプリケーションのパフォーマンスにとって明確なアイテムの選択のパフォーマンスが重要である場合、追加することを検討する価値があります。
残念なことに、Java 8ストリームのパフォーマンスはかなり低下したようです。 distinct()
呼び出しをコーディングするよりも、私が使用した方法よりも良い方法があるかもしれないので、コメントやその他の回答はもちろん歓迎します。
注:私はMicroBenchmarkingの専門家ではありませんので、誰かが私の結果や方法論に欠陥を見つけた場合は私に通知してください。回答を修正するよう努めます。
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コレクションのコミッターです。
グアバ は選択可能です:
_ImmutableSet<Integer> set = ImmutableSet.copyOf(listInts);
_
APIは非常に最適化されています。
listInts.stream().distinct()
およびnew LinkedHashSet<>(listInts)
よりも高速です。
new LinkedList(new HashSet(listInts))
を試してください。
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);}
}
これは動作するはずです:
yourlist.stream()。map(equalsおよびhashchodeメソッドをオーバーライドするラッパー:: new).distinct()。map(上記で定義されたラッパー::最終出力を返すメソッド).collect(Collectors.toList());
心配しないで。 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);