Hashset vs Treeset

Я всегда любил деревья, такие красивые O(n*lg(n)) и аккуратность их. Однако каждый инженер-программист, которого я когда-либо знал, спросил меня, почему я бы использовал TreeSet. Из CS-фона я не думаю, что это имеет значение для всего, что вы используете, и мне не нужно возиться с хэш-функциями и ведрами (в случае Java).

В каких случаях я должен использовать HashSet над TreeSet?

Ответ 1

HashSet намного быстрее, чем TreeSet (постоянное время и время регистрации для большинства операций, таких как добавление, удаление и содержит), но не предлагает никаких заказов, таких как TreeSet.

HashSet

  • класс предлагает постоянную производительность времени для основных операций (добавление, удаление, наличие и размер).
  • это не гарантирует, что порядок элементов будет оставаться постоянным с течением времени
  • производительность итерации зависит от начальной емкости и коэффициента загрузки HashSet.
    • Достаточно безопасно принимать коэффициент загрузки по умолчанию, но вы можете указать начальную емкость, которая примерно в два раза превышает размер, на который вы ожидаете, что набор будет расти.

TreeSet

  • гарантирует log (n) временную стоимость для основных операций (добавление, удаление и содержит)
  • гарантирует, что элементы набора будут отсортированы (восходящие, естественные или заданные вами через его конструктор) (реализует SortedSet)
  • не предлагает никаких параметров настройки для производительности итерации
  • предлагает несколько удобных методов для работы с упорядоченными наборами, такими как first(), last(), headSet() и tailSet() т.д.

Важные моменты:

  • Оба гарантируют дублирование коллекции элементов
  • Как правило, быстрее добавлять элементы в HashSet, а затем преобразовывать коллекцию в TreeSet для повторного сортированного обхода без дубликатов.
  • Ни одна из этих реализаций не синхронизирована. То есть, если несколько потоков обращаются к набору одновременно, и по крайней мере один из потоков изменяет набор, он должен быть синхронизирован извне.
  • LinkedHashSet в некотором смысле является промежуточным между HashSet и TreeSet. Тем не менее, реализованная как хеш-таблица со связанным списком, проходящим через нее, она предоставляет итерацию с упорядочением вставки, которая не такая же, как отсортированный обход, гарантированный TreeSet.

Поэтому выбор использования полностью зависит от ваших потребностей, но я чувствую, что даже если вам нужна упорядоченная коллекция, вы все равно должны предпочесть HashSet для создания Set, а затем преобразовать его в TreeSet.

  • например SortedSet<String> s = new TreeSet<String>(hashSet);

Ответ 2

Одно из преимуществ, о которых еще не упоминалось в TreeSet, состоит в том, что его имеет большую "локальность", что является сокращением для выражения (1), если две записи находятся рядом в порядке, a TreeSet помещает их рядом друг с другом в структуры данных и, следовательно, в памяти; и (2) в этом размещении используется принцип локальности, в котором говорится, что к подобным данным часто обращается приложение с аналогичной частотой.

Это контрастирует с HashSet, который распространяет записи по всей памяти независимо от их ключей.

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

Ответ 3

HashSet - это O (1) для доступа к элементам, поэтому это, безусловно, имеет значение. Но поддерживать порядок объектов в наборе невозможно.

TreeSet полезен, если для вас важно поддерживать порядок (в терминах значений, а не порядка вставки). Но, как вы заметили, вы торгуете ордером для более медленного времени доступа к элементу: O (log n) для основных операций.

Из javadocs для TreeSet:

Эта реализация обеспечивает гарантированную log (n) временную стоимость для основных операций (add, remove и contains).

Ответ 4

1.HashSet разрешает нулевой объект.

2.TreeSet не разрешает нулевой объект. Если вы попытаетесь добавить нулевое значение, это вызовет исключение 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

Ответ 5

Основная причина использования HashSet заключается в том, что операции (в среднем) O (1) вместо O (log n). Если набор содержит стандартные элементы, вы не будете "возиться с хэш-функциями", как это было сделано для вас. Если набор содержит пользовательские классы, вы должны реализовать hashCode для использования HashSet (хотя эффективная Java показывает, как), но если вы используете TreeSet, вы должны сделать его Comparable или поставить Comparator. Это может быть проблемой, если класс не имеет определенного порядка.

Я иногда использовал TreeSet (или фактически TreeMap) для очень маленьких наборов/отображений (< 10 элементов), хотя я не проверял, есть ли реальный выигрыш при этом. Для больших множеств разница может быть значительной.

Теперь, если вам нужна сортировка, тогда TreeSet подходит, хотя даже тогда, если обновления часты и необходимость в отсортированном результате нечастая, иногда копирование содержимого в список или массив и сортировка их могут быть более быстрыми.

Ответ 6

Основываясь на прекрасном визуальном ответе на Картах от @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               ║
╚══════════════╩═══════════════════════════════════════════════════════════════╝

Ответ 7

Если вы не вставляете достаточное количество элементов, чтобы привести к частым переборам (или коллизиям, если ваш HashSet не может изменять размер), HashSet, безусловно, дает вам преимущество постоянного доступа времени. Но на множестве с большим количеством роста или усадки вы можете получить лучшую производительность с помощью Treesets, в зависимости от реализации.

Амортизированное время может быть близко к O (1) с функциональным красно-черным деревом, если мне служит память. У книги Окасаки было бы лучшее объяснение, чем я могу сделать. (Или посмотрите его список публикаций)

Ответ 8

Реализации HashSet, конечно, намного быстрее - меньше накладных расходов, потому что нет порядка. Хороший анализ различных реализаций набора в Java предоставляется в http://java.sun.com/docs/books/tutorial/collections/implementations/set.html.

В обсуждении также указывается интересный подход "среднего звена" к вопросу "Дерево против хеширования". Java предоставляет LinkedHashSet, который представляет собой HashSet с "связанным с вложением" связанным списком, проходящим через него, то есть последний элемент связанного списка также является последним, вставленным в Hash. Это позволяет избежать нечистоты неупорядоченного хэша, не увеличивая стоимость TreeSet.

Ответ 9

TreeSet - это один из двух отсортированных коллекций (другой TreeMap). Он использует структуру дерева красно-черных (но вы это знали) и гарантирует что элементы будут в порядке возрастания, в соответствии с естественным порядком. Необязательно, вы можете построить TreeSet с помощью конструктора, который позволяет вам собственные правила для того, каким должен быть порядок (вместо того, чтобы полагаться на определенный порядок по классу элементов) с помощью Comparable или Comparator

и A LinkedHashSet - это упорядоченная версия HashSet, которая поддерживает двусвязный список по всем элементам. Используйте этот класс вместо HashSet когда вы заботитесь о порядке итерации. Когда вы выполняете итерацию через HashSet, порядок непредсказуем, а LinkedHashSet позволяет вам перебирать элементы в том порядке, в котором они были вставлены

Ответ 10

Было дано много ответов на основе технических соображений, особенно в отношении производительности. По моему мнению, выбор между TreeSet и HashSet имеет значение.

Но я бы сказал, что выбор должен определяться соображениями концептуальных.

Если для объектов, которые вам нужно манипулировать, естественный порядок не имеет смысла, тогда не используйте TreeSet.

Это сортированный набор, поскольку он реализует SortedSet. Это означает, что вам нужно переопределить функцию compareTo, которая должна соответствовать тому, что возвращает функцию equals. Например, если у вас есть набор объектов класса Student, то я не думаю, что TreeSet имеет смысл, поскольку между учениками нет естественного порядка. Вы можете заказать их по среднему классу, хорошо, но это не "естественный порядок". Функция compareTo возвращает 0 не только тогда, когда два объекта представляют один и тот же ученик, но также, когда два разных ученика имеют одинаковый класс. Во втором случае equals вернет false (если вы не решите, что последнее вернет true, когда два разных ученика имеют один и тот же класс, что делает функцию equals ложной, не говоря уже о неправильном значении).

Обратите внимание, что эта согласованность между equals и compareTo является необязательной, но настоятельно рекомендуется. В противном случае нарушается договор интерфейса Set, что делает ваш код вводящим в заблуждение другим людям, что также может привести к неожиданному поведению.

Эта ссылка может быть хорошим источником информации по этому вопросу.

Ответ 11

Почему есть яблоки, когда вы можете иметь апельсины?

Серьезно парни и девушки - если ваша коллекция большая, читайте и записывайте на gazillions раз, и вы платите за циклы процессора, тогда выбор коллекции имеет значение ТОЛЬКО, если вам НЕТ это делать лучше. Однако в большинстве случаев это не имеет значения - несколько миллисекунд здесь и там остаются незамеченными в человеческих терминах. Если это действительно так важно, почему вы не пишете код на ассемблере или C? [cue другое обсуждение]. Таким образом, дело в том, что если вы счастливы использовать любую коллекцию, которую вы выбрали, и она решает вашу проблему (даже если это не лучший тип коллекции для задачи) выбивается из игры. Программное обеспечение является податливым. Оптимизируйте свой код там, где это необходимо. Дядя Боб говорит, что преждевременная оптимизация - это корень всего зла. Дядя Боб говорит так

Ответ 12

Редактирование сообщений (завершить переписывание) Когда заказ не имеет значения, когда. Оба должны дать Log (n) - было бы полезно увидеть, превышает ли это на пять процентов быстрее, чем другое. HashSet может дать тестирование O (1) в цикле, должно ли оно показать.

Ответ 13

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