HashSetとTreeSetの比較
1. 前書き
この記事では、java.util.Setインターフェースの最も一般的な2つのJava実装であるHashSetとTreeSetを比較します。
2. 違い
HashSetとTreeSetは同じブランチの葉ですが、いくつかの重要な点で異なります。
2.1. ご注文
HashSet stores the objects in random order, whereas TreeSet applies the natural order of the elements.次の例を見てみましょう。
@Test
public void givenTreeSet_whenRetrievesObjects_thenNaturalOrder() {
Set set = new TreeSet<>();
set.add("example");
set.add("is");
set.add("Awesome");
assertEquals(3, set.size());
assertTrue(set.iterator().next().equals("Awesome"));
}
StringオブジェクトをTreeSetに追加した後、最初のオブジェクトは最後に追加されたにもかかわらず、「素晴らしい」であることがわかります。 HashSetで実行される同様の操作は、要素の順序が時間の経過とともに一定に保たれることを保証するものではありません。
2.2. Nullオブジェクト
もう1つの違いは、HashSet can store null objects, while TreeSet does not allow them:
@Test(expected = NullPointerException.class)
public void givenTreeSet_whenAddNullObject_thenNullPointer() {
Set set = new TreeSet<>();
set.add("example");
set.add("is");
set.add(null);
}
@Test
public void givenHashSet_whenAddNullObject_thenOK() {
Set set = new HashSet<>();
set.add("example");
set.add("is");
set.add(null);
assertEquals(3, set.size());
}
nullオブジェクトをTreeSetに格納しようとすると、操作によってNullPointerExceptionがスローされます。 唯一の例外は、Java 7で、TreeSetにnull要素を1つだけ含めることが許可されていた場合です。
2.3. パフォーマンス
簡単に言えば、HashSetはTreeSetよりも高速です。
HashSetは、add()、remove()、contains()などのほとんどの操作で一定時間のパフォーマンスを提供しますが、log(n)時間はTreeSet.
通常、the execution time for adding elements into TreeSet is much better than for the HashSetを確認できます。
JVMがウォームアップされていない可能性があるため、実行時間が異なる可能性があることに注意してください。 さまざまなSet実装を使用してマイクロテストを設計および実行する方法についての適切な説明が利用可能ですhere。
2.4. 実装されたメソッド
TreeSet is rich in functionalities、次のような追加のメソッドを実装します。
-
pollFirst() –最初の要素を返すか、Setが空の場合はnullを返します
-
pollLast() –最後の要素を取得して削除するか、Setが空の場合はnullを返します
-
first() –最初のアイテムを返す
-
最後のアイテムを返すlast() –
-
ceiling() –指定された要素以上の最小要素を返すか、そのような要素がない場合はnullを返します
-
lower() –指定された要素よりも厳密に小さい最大の要素を返すか、そのような要素がない場合はnullを返します
上記の方法により、TreeSetはHashSetよりもはるかに使いやすく強力になります。
3. 類似点
3.1. ユニークな要素
TreeSetとHashSetはどちらも、duplicate-free collection of elements,が汎用のSetインターフェースの一部であるため、duplicate-free collection of elements,を保証します。
@Test
public void givenHashSetAndTreeSet_whenAddDuplicates_thenOnlyUnique() {
Set set = new HashSet<>();
set.add("example");
set.add("example");
assertTrue(set.size() == 1);
Set set2 = new TreeSet<>();
set2.add("example");
set2.add("example");
assertTrue(set2.size() == 1);
}
3.2. synchronizedではありません
None of the described Set implementations are synchronized.これは、複数のスレッドがSetに同時にアクセスし、少なくとも1つのスレッドがそれを変更する場合、外部で同期する必要があることを意味します。
3.3. フェイルファストイテレータ
TreeSetおよびHashSetによって返されるIteratorsはフェイルファストです。
つまり、Iteratorが作成された後はいつでも、Setを変更すると、ConcurrentModificationException:がスローされます。
@Test(expected = ConcurrentModificationException.class)
public void givenHashSet_whenModifyWhenIterator_thenFailFast() {
Set set = new HashSet<>();
set.add("example");
Iterator it = set.iterator();
while (it.hasNext()) {
set.add("Awesome");
it.next();
}
}
4. どの実装を使用しますか?
どちらの実装もセットのアイデアの契約を満たしているため、どちらの実装を使用するかはコンテキスト次第です。
覚えておくべきいくつかの簡単なポイントを次に示します。
-
エントリを並べ替えたままにしておきたい場合は、TreeSetを選択する必要があります。
-
メモリ消費よりもパフォーマンスを重視する場合は、HashSetを使用する必要があります。
-
メモリが不足している場合は、TreeSetを選択する必要があります
-
自然な順序に従って互いに比較的近い要素にアクセスする場合は、局所性が高いため、TreeSetを検討することをお勧めします。
-
HashSetのパフォーマンスは、initialCapacityとloadFactorを使用して調整できますが、TreeSetでは調整できません。
-
挿入順序を維持し、一定時間のアクセスを利用したい場合は、LinkedHashSetを使用できます。
5. 結論
この記事では、TreeSetとHashSetの違いと類似点について説明しました。
いつものように、この記事のコード例は利用可能なover on GitHubです。