HashSet- und TreeSet-Vergleich

HashSet- und TreeSet-Vergleich

1. Einführung

In diesem Artikel werden zwei der beliebtesten Java-Implementierungen derjava.util.Set-Schnittstelle verglichen -HashSet undTreeSet.

2. Unterschiede

HashSet undTreeSet sind Blätter desselben Zweigs, unterscheiden sich jedoch in wenigen wichtigen Punkten.

2.1. Bestellung

HashSet stores the objects in random order, whereas TreeSet applies the natural order of the elements. Sehen wir uns das folgende Beispiel an:

@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"));
}

Nachdem wir dieString-Objekte zuTreeSet hinzugefügt haben, sehen wir, dass das erste "Awesome" ist, obwohl es ganz am Ende hinzugefügt wurde. Eine ähnliche Operation mitHashSet garantiert nicht, dass die Reihenfolge der Elemente über die Zeit konstant bleibt.

2.2. Null Objekte

Ein weiterer Unterschied ist, dassHashSet 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());
}

Wenn wir versuchen, dasnull-Objekt in einemTreeSet zu speichern, führt die Operation zu einem geworfenenNullPointerException. Die einzige Ausnahme war in Java 7, als es erlaubt war, genau einnull-Element inTreeSet zu haben.

2.3. Performance

Einfach ausgedrückt istHashSet schneller alsTreeSet.

HashSet bietet eine zeitkonstante Leistung für die meisten Operationen wieadd(),remove() undcontains() im Vergleich zurlog (n) Zeit, die von der TreeSet.

Normalerweise können wir sehen, dassthe execution time for adding elements into TreeSet is much better than for the HashSet.

Bitte denken Sie daran, dass die JVM möglicherweise nicht aufgewärmt ist, sodass die Ausführungszeiten abweichen können. Eine gute Diskussion zum Entwerfen und Durchführen von Mikrotests unter Verwendung verschiedenerSet-Implementierungen isthere verfügbar.

2.4. Implementierte Methoden

TreeSet is rich in functionalities, Implementierung zusätzlicher Methoden wie:

  • pollFirst() - um das erste Element zurückzugeben, odernull, wennSet leer ist

  • pollLast() - um das letzte Element abzurufen und zu entfernen odernull zurückzugeben, wennSet leer ist

  • first() - um den ersten Artikel zurückzugeben

  • last(), um das letzte Element zurückzugeben

  • ceiling() - um das kleinste Element zurückzugeben, das größer oder gleich dem angegebenen Element ist, odernull, wenn es kein solches Element gibt

  • lower() - um das größte Element zurückzugeben, das streng kleiner als das angegebene Element ist, odernull, wenn es kein solches Element gibt

Die oben genannten Methoden machenTreeSet viel einfacher und leistungsfähiger alsHashSet.

3. Ähnlichkeiten

3.1. Einzigartige Elemente

SowohlTreeSet als auchHashSet garantieren einduplicate-free collection of elements,, da es Teil der generischenSet-Schnittstelle ist:

@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. Nichtsynchronized

None of the described Set implementations are synchronized. Dies bedeutet, dass wenn mehrere Threads gleichzeitig aufSet zugreifen und mindestens einer der Threads es ändert, es extern synchronisiert werden muss.

3.3. Ausfallsichere Iteratoren

Die vonTreeSet undHashSet zurückgegebenenIterators sind ausfallsicher.

Das bedeutet, dass jede Änderung vonSet zu einem beliebigen Zeitpunkt nach dem Erstellen vonIterator einConcurrentModificationException: auslöst

@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. Welche Implementierung soll verwendet werden?

Beide Implementierungen erfüllen den Vertrag der Idee eines Sets, sodass es dem Kontext überlassen bleibt, welche Implementierung wir verwenden könnten.

Hier einige wichtige Punkte:

  • Wenn wir unsere Einträge sortieren wollen, müssen wir dieTreeSet wählen

  • Wenn wir die Leistung mehr als den Speicherverbrauch schätzen, sollten wir uns fürHashSet entscheiden

  • Wenn wir wenig Speicher haben, sollten wir uns fürTreeSet entscheiden

  • Wenn wir auf Elemente zugreifen möchten, die gemäß ihrer natürlichen Reihenfolge relativ nahe beieinander liegen, sollten wirTreeSet berücksichtigen, da sie eine größere Lokalität aufweisen

  • Die Leistung vonHashSet kann mitinitialCapacity undloadFactor eingestellt werden, was fürTreeSet nicht möglich ist

  • Wenn wir die Einfügereihenfolge beibehalten und von einem konstanten Zeitzugriff profitieren möchten, können wirLinkedHashSet verwenden

5. Fazit

In diesem Artikel haben wir die Unterschiede und Ähnlichkeiten zwischenTreeSet undHashSet behandelt.

Wie immer sind die Codebeispiele für diesen Artikelover on GitHub verfügbar.