Сравнение HashSet и TreeSet

Сравнение HashSet и TreeSet

1. Вступление

В этой статье мы сравним две самые популярные Java-реализации интерфейсаjava.util.Set -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 мы видим, что первый из них - «Awesome», хотя он был добавлен в самом конце. Аналогичная операция, выполняемая сHashSet, не гарантирует, что порядок элементов останется постоянным с течением времени.

2.2. Null Объекты

Другое отличие состоит в том, что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, когда разрешалось иметь ровно один элементnull вTreeSet.

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() - вернуть первый элемент, илиnull, еслиSet пуст

  • pollLast() - получить и удалить последний элемент или вернутьnull, еслиSet пуст

  • first() - вернуть первый элемент

  • last(), чтобы вернуть последний элемент

  • ceiling() - вернуть наименьший элемент, больший или равный данному элементу, илиnull, если такого элемента нет

  • lower() - вернуть самый большой элемент, строго меньший, чем данный элемент, илиnull, если такого элемента нет

Упомянутые выше методы делаютTreeSet намного проще в использовании и более мощным, чемHashSet.

3. сходства

3.1. Уникальные элементы

ИTreeSet, иHashSet гарантируютduplicate-free collection of elements,, так как это часть общего интерфейсаSet:

@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 одновременно, и хотя бы один из потоков изменяет его, то он должен быть синхронизирован извне.

3.3. Отказоустойчивые итераторы

Iterators, возвращаемыеTreeSet иHashSet, работают без сбоев.

Это означает, что любая модификацияSet в любое время после созданияIterator вызовет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.