Каковы преимущества использования Map над ArrayList класса костюма - PullRequest
2 голосов
/ 11 мая 2019

Сейчас я изучаю Java и изучаю различные виды коллекций, поэтому я узнал о LinkedList, ArrayList и Array [].Теперь я познакомился с типами коллекций Hash, HashSet и HashMap, и я не совсем понял, почему они полезны, поскольку список поддерживаемых ими команд тихо ограничен, а также они отсортированы в случайном порядке иМне нужно переопределить методы equal и HashKey, чтобы он правильно работал с классом.Теперь я не понимаю преимуществ использования этих типов вместо ArrayList класса костюма.Я имею в виду, что Map соединяет 2 объекта как 1, но не лучше ли создать класс, который содержит эти 2 объекта в качестве параметров, и иметь методы получения и изменения их?Если выгода состоит в том, что эти объекты Hash могут содержать только 1 объект с тем же именем, не проще ли будет сделать проверку ArrayList, что тип еще не существует перед его добавлением?

Пока что янаучился выбирать, когда использовать LinkedList, ArrayList или Array [] по правилу «если это действительно просто, используйте Array [], если немного сложнее использовать ArrayList (например, для хранения коллекции определенного класса), и еслисписок динамический, с большим количеством объектов внутри, которые должны изменить порядок в соответствии с удалением или добавлением нового в середине или переходить назад и вперед по списку, а затем использовать LinkedList.

Но я не мог понять, когдаЯ предпочитаю HashMap или HashSet, и я был бы очень рад, если бы вы могли мне это объяснить.

Ответы [ 4 ]

1 голос
/ 12 мая 2019

Позвольте мне помочь вам здесь ...

Хешированные коллекции являются наиболее эффективными для добавления, поиска и удаления данных, поскольку они хэшируют ключ (в HashMap) или элемент (в HashSet) для поиска.место, где они принадлежат в один шаг.Концепция хеширования действительно проста.Это процесс представления объекта в виде числа, которое может работать как его идентификатор.Например, если у вас есть строка в Java, такая как String name = "Jeremy";, и вы напечатали ее хэш-код: System.out.println(name.hashCode());, вы увидите там большое число (-2079637766), которое было создано с использованием значений этого строкового объекта (в этом строковом объекте)., это символы), таким образом, это число можно использовать в качестве идентификатора для этого объекта.

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

Операция, которую они используют для уменьшения этого числа, называется хешированием и выглядит примерно так: Math.abs(-2079637766 % arrayLength);.Это не совсем так, это немного сложнее, но это для упрощения.Допустим, что arrayLength = 16;Оператор% уменьшит это большое число до числа, меньшего 16, чтобы его можно было уместить в массиве.

Именно поэтому коллекция Hashed не будет разрешать дублирование, поскольку при попытке добавить то же самоеобъект или эквивалентный (например, 2 строки с одинаковыми символами), он создаст один и тот же хэш-код и переопределит любое значение в индексе результата.

В своем вопросе вы упомянули, что если вас беспокоитдублирует элементы в ArrayList, мы можем просто проверить, есть ли элемент перед его вставкой, поэтому таким образом нам не нужно использовать HashSet.Но это не очень хорошая идея, потому что если вы вызываете метод list.contains(elem); в ArrayList, он должен идти один за другим, сравнивая элементы, чтобы увидеть, есть ли он там.Если у вас есть 1 миллион элементов в ArrayList, и вы проверяете, есть ли элемент, но его там нет, ArrayList итерировал более 1 миллиона элементов, что не очень хорошо.Но с HashSet, он будет только хэшировать объект и идти прямо туда, где он должен быть в массиве, и проверять, делая это всего за 1 шаг вместо 1 миллиона.Итак, вы видите, насколько эффективен HashSet по сравнению с ArrayList.

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

Вот почему он действительно эффективен и широко используется.

Основное различие между ArrayList и LinkedList:

Если вы хотите найтиэлемент в месте 500 в ArrayList размера 1000, вы делаете: list.get(500);, и он сделает это за один шаг, потому что ArrayList реализован с массивом, поэтому с этим 500 он идет прямо туда, где находится элементмассив.Но LinkedList реализован не с массивом, а с объектами, указывающими друг на друга.Таким образом, они должны идти линейно и считать от 0, один за другим, пока не доберутся до 500, что не очень эффективно по сравнению с 1 единственным шагом ArrayList.Но когда вам нужно добавить и удалить элементы в ArrayList, иногда необходимо воссоздать массив, чтобы в него поместилось больше элементов, что увеличило накладные расходы.Но этого не происходит с LinkedList, так как никакой массив не должен быть воссоздан, только ссылки на объекты (узлы) должны быть сделаны повторно, что делается за один шаг.

Так что ArrayList хорошкогда вы не будете удалять или добавлять много элементов в структуру, но вы будете много читать из нее.яЕсли вы собираетесь добавить и удалить много элементов, то лучше связать список, так как он выполняет меньше операций с этими операциями.

Почему вам нужно реализовать методы equals (), hashCode () для пользовательских классов, когда вы хотите использовать эти объекты в HashMaps, и реализовать интерфейс Comparable, когда вы хотите использовать эти объекты с TreeMaps?

Исходя из того, что я упоминал ранее для HashMaps, возможно, что 2 разных объекта выдают один и тот же хеш, если это произойдет, Java не переопределит предыдущий или не удалит его, но сохранит их обоих в одном индексе. Вот почему вам нужно реализовать hashCode (), поэтому вы должны убедиться, что ваши объекты не будут иметь действительно простого hashCode, который можно легко скопировать. И причина, по которой рекомендуется переопределить метод equals (), заключается в том, что если коллизия (2 или более объектов совместно используют один и тот же хэш в HashMap) , то как вы их отличите? Хорошо, спрашивая метод equals () этих двух объектов, если они одинаковы. Поэтому, если вы спрашиваете карту, содержит ли она определенный ключ, и в этом индексе он находит 3 элемента, он запрашивает методы equals () этих элементов, если его equals () переданному ключу, если да, возвращает вон тот. Если вы не переопределите метод equals () и не укажете, что именно вы хотите проверить на равенство (например, имя свойства, возраст и т. Д.), Тогда произойдут некоторые нежелательные переопределения внутри HashMap, и вам это не понравится.

Если вы создаете свои собственные классы, скажем, Person, и имеете такие свойства, как name, age, lastName и email, вы можете использовать эти свойства в методе equals (), и если 2 разных объекта передаются, но имеют одинаковые значения в Вы выбрали свойства для равенства, затем вы возвращаете true, чтобы указать, что они одинаковы, или false в противном случае. Как и класс String, если вы делаете s1.equals(s2);, если s1 = new String("John"); и s2 = new String("John");, даже если они являются разными объектами в памяти кучи Java, реализация метода String.equals использует символы, чтобы определить, равны ли объекты, и он возвращает true для этого примера.

Чтобы использовать TreeMap с пользовательскими классами, вам необходимо реализовать Comparable интерфейс , так как TreeMap будет сравнивать и сортировать объекты на основе некоторых свойств, вам необходимо укажите, по каким свойствам будут отсортированы ваши объекты. Будут ли ваши объекты отсортированы по возрасту? По имени? По идентификатору? Или любой другой собственностью, которую вы хотели бы. Затем, когда вы реализуете интерфейс Comparable и переопределяете метод compareTo (UserDefinedClass o) , вы выполняете логику и возвращаете положительное число, если текущий объект больше, чем переданный объект o, 0, если они являются то же самое и отрицательное число, если текущий объект меньше. Таким образом, TreeMap будет знать, как их сортировать, основываясь на возвращенном числе.

0 голосов
/ 11 мая 2019

По поводу вашего вопроса:

"Но я не мог понять, когда предпочесть HashMap или HashSet, и я был бы очень рад, если бы вы могли объяснить мне это"

В HashMap реализован интерфейс Map, который будет использоваться для отображения ключа (K) на значение (V) в постоянном времени и там, где порядок не имеет значения, поэтому вы можете эффективно размещать и извлекать эти данные, если вы сейчасключ.

И HashSet реализует интерфейс Set, но внутренне использует и HashMap, его роль должна использоваться как Set, то есть вы не должны извлекать элемент, вы просто проверяете, что внабор или нет (в основном).

В HashMap у вас может быть идентичное значение, а в наборе вы не можете (потому что это свойство набора).

Относительно этого вопроса:

Если преимущество состоит в том, что эти объекты Hash могут содержать только 1 объект с тем же именем,> не проще ли сделать проверку ArrayList на то, что тип еще не> существует перед его добавлением?

При работе со сбором вы можете основывать свой выбор конкретного на представлении данных, а также на том, как вы хотите получить доступ к этим данным и хранить их, как вы получаете к ним доступ?Вам нужно их отсортировать?Поскольку каждая реализация может иметь различную сложность (https://en.wikipedia.org/wiki/Time_complexity),, это становится важным.

Использование документа,

Для ArrayList:

Операция добавления выполняетсяв амортизированном постоянном времени, то есть для добавления n элементов требуется время O (n). Все остальные операции выполняются за линейное время (грубо говоря).

Для HashMap:

Эта реализация обеспечивает постоянную производительность для базовых операций (get и put), при условии, что хеш-функция правильно распределяет элементы по сегментам. Итерации по представлениям коллекции требуют времени, пропорционального «емкости» экземпляра HashMap (количество сегментов) плюс его размер (число отображений значения ключа). Таким образом, очень важно не устанавливать слишком высокую начальную емкость (или слишком низкий коэффициент загрузки), если производительность итерации важна.

Так что по поводу сложности времени.

Вы можете выбрать еще более нетипичный сборник для определенных задач :).

0 голосов
/ 11 мая 2019

Это имеет мало общего с Java, и выбор зависит главным образом от требований к производительности, но есть фундаментальное отличие, которое необходимо подчеркнуть. Концептуально списки - это типы коллекций, которые сохраняют порядок вставки и могут иметь дубликаты, наборы больше похожи на пакеты предметов, у которых нет определенного порядка и нет дубликатов. Конечно, различные реализации могут найти способ обойти это (например, TreeSet).

Сначала давайте проверим разницу между ArrayList и LinkedList. Связанный список - это набор узлов, где каждый узел содержит значение и ссылку на следующий и предыдущий узлы. Это делает вставку элемента в связанный список вопросом добавления узла в конец списка, что является быстрой операцией, поскольку память не должна быть смежной, пока узел сохраняет ссылку на следующий узел. С другой стороны, доступ к определенному элементу требует прохождения всего списка до его обнаружения.

Список массивов, как следует из названия, оборачивает массив. Доступ к элементам в массиве с использованием его индекса - это прямой доступ, но вставка элемента подразумевает изменение размера массива для включения нового элемента, поэтому занимаемая им память является смежной, что в этом случае делает записи немного тяжелее.

HashMap работает как словарь, где для каждого ключа есть значение. Поведение вставки будет в основном зависеть от того, как реализованы функции hashCode и equals объекта, используемого в качестве ключа. Если hashCode двух ключей одинаковы, возникает коллизия хэшей, поэтому equals будет использоваться для определения того, является ли это тот же ключ или нет. Если equals то же самое, то это тот же ключ, поэтому значение заменяется. Если нет, новое значение добавляется в коллекцию. Доступ к значениям и их запись в основном зависят от вычисления хэша ключа с последующим прямым доступом к значению, что делает обе операции действительно быстрыми, O (1).

Набор во многом похож на хэш-карту без части «значения», поэтому он следует тем же правилам, касающимся реализации операций hashCode и equals для добавленной стоимости.

Возможно, будет полезно немного изучить нотацию Big-O и сложность алгоритмов. Если вы начинаете с Java, я настоятельно рекомендую книгу Джошуа Блоха Эффективная Java .

Надеюсь, это поможет вам копать дальше.

0 голосов
/ 11 мая 2019

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

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

Имеет ли это смысл?

...