Подтвердить что ты не робот

Java HashSet против HashMap

Я понимаю, что 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;
 }
}
4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

Ответ 3

В случае HashSet добавление того же объекта будет более или менее no-op. В случае 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());

Ответ 4

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

Ответ 5

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

hash (simpleObject1) и hash (simplObject2) возвратит тот же int. Так?

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

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

Ответ 6

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

Ответ 7

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

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

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