回答:
HashSetはTreeSetよりもはるかに高速です(追加、削除、包含などのほとんどの操作での一定時間とログ時間)。TreeSetのような順序の保証はありません。
SortedSet
)first()
、last()
、headSet()
、およびtailSet()
などHashSet
とTreeSet
。リンクリストが実行されるハッシュテーブルとして実装されますが、TreeSetで保証されているソート済みトラバーサルとは異なる挿入順の反復を提供します。したがって、使用方法の選択は完全にニーズに依存しますが、順序付けられたコレクションが必要な場合でも、HashSetを使用してセットを作成し、それをTreeSetに変換することをお勧めします。
SortedSet<String> s = new TreeSet<String>(hashSet);
aについてまだ言及されていない利点の1つTreeSet
は、その「局所性」が大きいことTreeSet
です。(2)この配置は、類似性のデータが類似の頻度でアプリケーションによってアクセスされることが多いと述べている局所性の原理を利用しています。
これはHashSet
、キーが何であれ、エントリをメモリ全体に分散するとは対照的です。
ハードドライブからの読み取りのレイテンシコストがキャッシュまたはRAMからの読み取りのコストの数千倍であり、データが局所性で実際にアクセスさTreeSet
れる場合、これははるかに優れた選択肢です。
TreeSet
/ の実装はTreeMap
局所性が最適化されていません。赤黒木を表すために4次のbツリーを使用して、局所性とキャッシュパフォーマンスを向上させることは可能ですが、それは実装が機能する方法ではありません。代わりに、各ノードは、独自のキー、独自の値、その親、およびその左右の子ノードへのポインターを格納します。これは、TreeMap.EntryのJDK 8ソースコードで明らかです。
HashSet
要素にアクセスするためのO(1)なので、それは確かに重要です。ただし、セット内のオブジェクトの順序を維持することはできません。
TreeSet
順序を維持することが重要な場合(挿入順序ではなく値に関して)は便利です。ただし、すでに述べたように、要素にアクセスするためのより遅い時間で注文を交換しています。基本的な操作ではO(log n)です。
この実装は、基本的な操作に保証されたlog(n)時間コストを提供します(
add
、remove
およびcontains
)の。
1.HashSetはnullオブジェクトを許可します。
2.TreeSetはnullオブジェクトを許可しません。null値を追加しようとすると、NullPointerExceptionがスローされます。
3.HashSetはTreeSetよりもはるかに高速です。
例えば
TreeSet<String> ts = new TreeSet<String>();
ts.add(null); // throws NullPointerException
HashSet<String> hs = new HashSet<String>();
hs.add(null); // runs fine
null
どちらにしてもあなたのセットに本当に追加すべきではありません。
TreeSet<String> badassTreeSet = new TreeSet<String>(new Comparator<String>() { public int compare(String string1, String string2) { if (string1 == null) { return (string2 == null) ? 0 : -1; } else if (string2 == null) { return 1; } else { return string1.compareTo(string2); } } }); badassTreeSet.add("tree"); badassTreeSet.add("asdf"); badassTreeSet.add(null); badassTreeSet.add(null); badassTreeSet.add("set"); badassTreeSet.add("tree"); System.out.println(badassTreeSet);
@shevchykによるマップでの素敵な視覚的回答に基づくここに私の見解があります:
╔══════════════╦═════════════════════╦═══════════════════╦═════════════════════╗
║ Property ║ HashSet ║ TreeSet ║ LinkedHashSet ║
╠══════════════╬═════════════════════╬═══════════════════╬═════════════════════╣
║ ║ no guarantee order ║ sorted according ║ ║
║ Order ║ will remain constant║ to the natural ║ insertion-order ║
║ ║ over time ║ ordering ║ ║
╠══════════════╬═════════════════════╬═══════════════════╬═════════════════════╣
║ Add/remove ║ O(1) ║ O(log(n)) ║ O(1) ║
╠══════════════╬═════════════════════╬═══════════════════╬═════════════════════╣
║ ║ ║ NavigableSet ║ ║
║ Interfaces ║ Set ║ Set ║ Set ║
║ ║ ║ SortedSet ║ ║
╠══════════════╬═════════════════════╬═══════════════════╬═════════════════════╣
║ ║ ║ not allowed ║ ║
║ Null values ║ allowed ║ 1st element only ║ allowed ║
║ ║ ║ in Java 7 ║ ║
╠══════════════╬═════════════════════╩═══════════════════╩═════════════════════╣
║ ║ Fail-fast behavior of an iterator cannot be guaranteed ║
║ Fail-fast ║ impossible to make any hard guarantees in the presence of ║
║ behavior ║ unsynchronized concurrent modification ║
╠══════════════╬═══════════════════════════════════════════════════════════════╣
║ Is ║ ║
║ synchronized ║ implementation is not synchronized ║
╚══════════════╩═══════════════════════════════════════════════════════════════╝
ほとんどの理由HashSet
は、操作がO(log n)ではなく(平均して)O(1)であるためです。セットに標準アイテムが含まれている場合、「ハッシュ関数をいじる」ことはありません。セットにカスタムクラスが含まれている場合は、hashCode
使用するために実装する必要がありますHashSet
(ただし、Effective Javaで方法が示されています)。ただし、使用するTreeSet
場合は、作成するComparable
か、を指定する必要がありますComparator
。クラスに特定の順序がない場合、これは問題になる可能性があります。
非常に小さなセット/マップ(<10アイテム)を使用するTreeSet
(または実際にTreeMap
)こともありますが、実際に利益があるかどうかは確認していません。大きなセットの場合、その差はかなり大きくなる可能性があります。
ソートが必要な場合TreeSet
は適切ですが、更新が頻繁でソート結果の必要性が低い場合でも、コンテンツをリストまたは配列にコピーしてソートする方が高速な場合があります。
もちろん、HashSetの実装ははるかに高速です-順序付けがないため、オーバーヘッドは少なくなります。JavaのさまざまなSet実装の適切な分析は、http://java.sun.com/docs/books/tutorial/collections/implementations/set.htmlで提供されています。ます。
そこでの議論は、ツリー対ハッシュの質問に対する興味深い「中間的な」アプローチも指摘しています。JavaはLinkedHashSetを提供します。これは、「挿入指向」のリンクリストが実行されているHashSetです。つまり、リンクリストの最後の要素も最後にハッシュに挿入されます。これにより、ツリーセットのコストの増加を招くことなく、順序付けられていないハッシュの無秩序さを回避できます。
TreeSetのは、 2つのソートコレクション(他のビーイングのTreeMap)の一つです。これは赤黒ツリー構造を使用します(ただし、ご存知のとおり)。要素が自然順序に従って昇順であることを保証します。必要に応じて、ComparableまたはComparatorを使用して、コレクションが(要素のクラスで定義された順序に依存するのではなく)順序をどうするかについての独自のルールをコレクションに与えることができるコンストラクターでTreeSetを構築できます。
そしてA LinkedHashSetのは、すべての要素間の二重リンクリストを維持HashSetの順序付けられたバージョンです。反復順序を気にする場合は、HashSetの代わりにこのクラスを使用してください。HashSetを反復するときの順序は予測できませんが、LinkedHashSetを使用すると、要素が挿入された順序で要素を反復できます。
特にパフォーマンスに関する技術的な考慮事項に基づいて、多くの答えが出されました。私によると、間の選択TreeSet
とHashSet
問題。
しかし、私はむしろ選択は最初に概念的な考慮事項によって駆動されるべきであると言いたいです。
操作する必要があるオブジェクトについて、自然な順序付けが意味をなさない場合は、を使用しないでくださいTreeSet
。
を実装してSortedSet
いるため、ソートされたセットです。つまりcompareTo
、関数をオーバーライドする必要があるということですequals
。これは、関数を返すものと一致している必要があります。たとえば、Studentというクラスのオブジェクトのセットがある場合、私はTreeSet
、生徒間で自然な順序付けがないため、 returnが意味をなす。あなたはそれらを平均グレードで注文することができますが、これは「自然な注文」ではありません。関数compareTo
、2つのオブジェクトが同じ生徒を表す場合だけでなく、2つの異なる生徒が同じ成績を持っている場合も、 0が返さ。2番目のケースでは、equals
はfalseを返します(2人の異なる学生が同じ成績を持っている場合に後者をtrueにすると、equals
機能が誤解を招くような意味になり、間違った意味を持たない場合を除きます)。との
間のこの一貫性はオプションですが、強く注意してくださいお勧めします。そうしないと、インターフェイスの規約が破られて、コードが他の人に誤解を与え、予期しない動作を引き起こす可能性もあります。equals
compareTo
Set
このリンクは、この質問に関する良い情報源になるかもしれません。
オレンジが作れるのに、なぜリンゴが作られるの?
真剣に考えている人やギャル-コレクションが膨大で、何億回も読み書きされ、CPUサイクルにお金を払っている場合、パフォーマンスの向上が必要な場合にのみ、コレクションの選択が関係します。ただし、ほとんどの場合、これは実際には問題になりません-数ミリ秒が人間の言葉で気づかれることはありません。それが本当に重要なのであれば、なぜアセンブラまたはCでコードを作成しないのですか?[別の議論の手がかり]。つまり、重要なのは、選択した任意のコレクションを使用して満足している場合であり、それが問題を解決します(たとえそれがタスクに特に最適なタイプのコレクションでなくても)、自分をノックアウトします。ソフトウェアは順応性があります。必要に応じてコードを最適化します。ボブおじさんは、早期最適化はすべての悪の根源だと言います。ボブおじさんはそう言っています
メッセージ編集(完全書き換え)順序を問わない場合はその時。どちらもLog(n)を与えるはずです-どちらかが他方よりも5%以上速いかどうかを確認することは実用的です。HashSetは、O(1)テストをループで実行して、そうであるかどうかを明らかにする必要があります。
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
public class HashTreeSetCompare {
//It is generally faster to add elements to the HashSet and then
//convert the collection to a TreeSet for a duplicate-free sorted
//Traversal.
//really?
O(Hash + tree set) > O(tree set) ??
Really???? Why?
public static void main(String args[]) {
int size = 80000;
useHashThenTreeSet(size);
useTreeSetOnly(size);
}
private static void useTreeSetOnly(int size) {
System.out.println("useTreeSetOnly: ");
long start = System.currentTimeMillis();
Set<String> sortedSet = new TreeSet<String>();
for (int i = 0; i < size; i++) {
sortedSet.add(i + "");
}
//System.out.println(sortedSet);
long end = System.currentTimeMillis();
System.out.println("useTreeSetOnly: " + (end - start));
}
private static void useHashThenTreeSet(int size) {
System.out.println("useHashThenTreeSet: ");
long start = System.currentTimeMillis();
Set<String> set = new HashSet<String>();
for (int i = 0; i < size; i++) {
set.add(i + "");
}
Set<String> sortedSet = new TreeSet<String>(set);
//System.out.println(sortedSet);
long end = System.currentTimeMillis();
System.out.println("useHashThenTreeSet: " + (end - start));
}
}