Java HashSet против HashMap - PullRequest
       3

Java HashSet против HashMap

54 голосов
/ 17 апреля 2011

Я понимаю, что HashSet основан на реализации HashMap, но используется, когда вам нужен уникальный набор элементов. Так почему же в следующем коде при размещении одних и тех же объектов на карте и задании размер обеих коллекций равен 1? Разве размер карты не должен быть 2? Потому что, если размер обеих коллекций одинаков, я не вижу разницы в использовании этих двух коллекций.

    Set testSet = new HashSet<SimpleObject>();
    Map testMap = new HashMap<Integer, SimpleObject>(); 

    SimpleObject simpleObject1 = new SimpleObject("Igor", 1);
    SimpleObject simplObject2 = new SimpleObject("Igor", 1);
    testSet.add(simpleObject1);
    testSet.add(simplObject2);


    Integer key = new Integer(10);

    testMap.put(key, simpleObject1);
    testMap.put(key, simplObject2);

    System.out.println(testSet.size());
    System.out.println(testMap.size());

Выход 1 и 1.

SimpleObject code

public class SimpleObject {

private String dataField1;
private int dataField2;

public SimpleObject(){}

public SimpleObject(String data1, int data2){
    this.dataField1 = data1;
    this.dataField2 = data2;
}

public String getDataField1() {
    return dataField1;
}

public int getDataField2() {
    return dataField2;
}

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result
            + ((dataField1 == null) ? 0 : dataField1.hashCode());
    result = prime * result + dataField2;
    return result;
}

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    SimpleObject other = (SimpleObject) obj;
    if (dataField1 == null) {
        if (other.dataField1 != null)
            return false;
    } else if (!dataField1.equals(other.dataField1))
        return false;
    if (dataField2 != other.dataField2)
        return false;
    return true;
 }
}

Ответы [ 7 ]

129 голосов
/ 17 апреля 2011

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

Разница между ними должна быть очевидна:

  • в Map, в котором пары ключ-значение
  • хранятся в Set вы храните только ключи

На самом деле, HashSet имеет поле HashMap, и всякий раз, когда вызывается add(obj), метод put вызывается на базовой карте map.put(obj, DUMMY) - где фиктивный объект является private static final Object DUMMY = new Object().Таким образом, карта заполняется вашим объектом в качестве ключа и значением, которое не представляет интереса.

7 голосов
/ 17 апреля 2011

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

6 голосов
/ 17 апреля 2011

В случае HashSet, добавление того же самого объекта будет более или менее бесполезным.В случае HashMap, добавив новый ключ, пара значений с существующим ключом перезапишет существующее значение, чтобы установить новое значение для этого ключа.Ниже я добавил проверки equals () в ваш код:

SimpleObject simpleObject1 = new SimpleObject("Igor", 1);
SimpleObject simplObject2 = new SimpleObject("Igor", 1);
//If the below prints true, the 2nd add will not add anything
System.out.println("Are the objects equal? " , (simpleObject1.equals(simpleObject2));
testSet.add(simpleObject1);
testSet.add(simplObject2);


Integer key = new Integer(10);
//This is a no-brainer as you've the exact same key, but lets keep it consistent
//If this returns true, the 2nd put will overwrite the 1st key-value pair.
testMap.put(key, simpleObject1);
testMap.put(key, simplObject2);
System.out.println("Are the keys equal? ", (key.equals(key));
System.out.println(testSet.size());
System.out.println(testMap.size());
1 голос
/ 05 февраля 2016

Ответ прост, потому что это природа HashSets. HashSet внутренне использует HashMap с фиктивным объектом с именем PRESENT в качестве значения, и KEY этого хеш-карты будет вашим объектом.

hash (simpleObject1) и hash (simpObject2) возвращают одно и то же значение типа int. Так?

Когда вы добавляете simpleObject1 в hashset, он помещает это в свой внутренний hashmap с simpleObject1 в качестве ключа. Затем, когда вы добавите (simpObject2), вы получите false, потому что он уже доступен во внутренней хэш-карте в качестве ключа.

В качестве небольшой дополнительной информации, HashSet эффективно использует функцию хеширования для обеспечения производительности O (1), используя объектные контракты equals () и hashCode (). Вот почему hashset не допускает, чтобы null, который не может быть реализован equals () и hashCode (), не являлся объектом.

1 голос
/ 22 апреля 2015

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

0 голосов
/ 04 февраля 2017

public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, Serializable
Этот класс реализует интерфейс Set, поддерживаемый хеш-таблицей (фактически, экземпляром HashMap). Это не дает никаких гарантий относительно порядка итераций множества; в частности, это не гарантирует, что порядок останется постоянным с течением времени. Этот класс разрешает нулевой элемент.

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

Обратите внимание, что эта реализация не синхронизирована. Если несколько потоков обращаются к хэш-набору одновременно, и хотя бы один из потоков изменяет набор, он должен быть синхронизирован извне. Обычно это достигается путем синхронизации с некоторым объектом, который естественным образом инкапсулирует набор. Если такого объекта не существует, набор следует «обернуть» с помощью метода Collections.synchronizedSet. Это лучше всего делать во время создания, чтобы предотвратить случайный несинхронизированный доступ к набору Подробнее

0 голосов
/ 13 октября 2014

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...