2016-06-10 4 views
2
public class HashMapKeySet { 

public static void main(String[] args) { 
    Map<HashCodeSame,Boolean> map=new HashMap(); 

    map.put(new HashCodeSame(10),true); 
    map.put(new HashCodeSame(2),false); 

    for(HashCodeSame i:map.keySet()) 
     System.out.println("Key: "+i+"\t Key Value: "+i.getA()+"\t Value: "+map.get(i)+"\t Hashcode: "+i 
       .hashCode()); 

    System.out.println("\nEntry Set******"); 
    for(Map.Entry<HashCodeSame, Boolean> i:map.entrySet()) 
     System.out.println("Key: "+i.getKey().getA()+"\t Value: "+i.getValue()+"\t Hashcode: "+i.hashCode()); 

    System.out.println("\nValues******"); 
    for(Boolean i:map.values()) 
     System.out.println("Key: "+i+"\t Value: "+map.get(i)+"\t Hashcode: "+i.hashCode()); 

} 

static class HashCodeSame{ 

    private int a; 

    public int getA() { 
     return a; 
    } 

    public void setA(int a) { 
     this.a = a; 
    } 

    HashCodeSame(int a){ 
     this.a=a; 
    } 

    @Override 
    public boolean equals(Object o) { 
     if (this == o) return true; 
     if (o == null || getClass() != o.getClass()) return false; 

     HashCodeSame that = (HashCodeSame) o; 

     return a == that.a; 

    } 

    @Override 
    public int hashCode() { 
     return 1; 
    } 
} 

}Зачем нам нужен LinkedHashMap, если keySet() поддерживает порядок для HashMap?

Если бы вы могли видеть в приведенном выше примере, я явно сделал хэш-код() возвращение 1 во всех случаях, чтобы проверить, что происходит, когда происходит столкновение key.hashcode() в HashMap , Что произойдет, связанный список сохраняется для этих Map.Entry объектов, таких как

1 (key.hashcode()) будет ссылаться на < 2, ложная> будет ссылка на < 10, правда>

(поскольку ложное значение вводится после истинного значения, как я понимаю).

Но когда я делаю keySet(), сначала возвращается значение true, а затем false, вместо того, чтобы сначала возвращать false.

Итак, что я здесь принимаю, поскольку keySet() - это набор и набор, поддерживающий порядок, мы получаем true и false во время итерации. Но, опять же, почему бы нам не сказать, что hashmap поддерживает порядок, так как единственный способ получить по порядку. Или почему мы используем LinkedHashMap?

Key: [email protected] Key Value: 10 Value: true  Hashcode: 1 
Key: [email protected]  Key Value: 2 Value: false Hashcode: 1 

Entry Set****** 
Key: 10 Value: true  Hashcode: 1230 
Key: 2 Value: false Hashcode: 1236 

Values****** 
Key: true Value: null  Hashcode: 1231 
Key: false Value: null  Hashcode: 1237 

Теперь, когда я добавить chsnge метод хэш, чтобы возвращать как

@Override 
    public int hashCode() { 
     return a; 
    } 

Я получаю обратный порядок. Кроме того при добавлении

map.put(new HashCodeSame(10),true); 
    map.put(new HashCodeSame(2),false); 
    map.put(new HashCodeSame(7),false); 
    map.put(new HashCodeSame(3),true); 
    map.put(new HashCodeSame(9),true); 

выход получил есть

Key: [email protected]  Key Value: 2 Value: false Hashcode: 2 
Key: [email protected]  Key Value: 3 Value: false Hashcode: 3 
Key: [email protected]  Key Value: 7 Value: false Hashcode: 7 
Key: [email protected]  Key Value: 9 Value: true  Hashcode: 9 
Key: [email protected]  Key Value: 10 Value: true  Hashcode: 10 

Entry Set****** 
Key: 2 Value: false Hashcode: 1239 
Key: 3 Value: false Hashcode: 1238 
Key: 7 Value: false Hashcode: 1234 
Key: 9 Value: true  Hashcode: 1222 
Key: 10 Value: true  Hashcode: 1221 

Values****** 
Key: false Value: null  Hashcode: 1237 
Key: false Value: null  Hashcode: 1237 
Key: false Value: null  Hashcode: 1237 
Key: true Value: null  Hashcode: 1231 
Key: true Value: null  Hashcode: 1231 

Теперь снова заставляет меня задаться вопросом, почему заказ приходит в отсортированном образом.? Может ли кто-нибудь объяснить мне подробно, как работают методы keySet(), entrySet() hashmap?

+0

это происходит потому, что элементы, добавленные с тем же хэш-код все в конечном итоге в том же ведре, и сохраняется порядок вставки, что это не так, если вы распространили хэш-коды. Использование одного и того же хэш-кода для всех объектов - плохая идея. –

+1

«Зачем нам нужен LinkedHashMap, если keySet() поддерживает порядок для HashMap?» Заказ ключей хэш-карт не определен; если вы видите, что они выходят в порядке, который вы ожидаете, это совпадение и не всегда гарантируется. –

+0

Можете ли вы мне понять внутреннюю реализацию keySet()? Как и в этой http://stackoverflow.com/questions/1882762/is-the-java-hashmap-keyset-iteration-order-consistent link, она задана, keySet всегда в том же порядке, что и введенный, хотя итерация по нему будет дороже, чем повторение связанногоHashMap. – dgupta3091

ответ

6

HashMapне делает имеет определенный порядок итерации, и LinkedHashMapделают имеют заданный порядок итераций.

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

Предположим, например, что вы сделали это:

Map<String, Boolean> map = new HashMap<>(); 
    String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 
    for (int i = 0; i < str.length(); i++) { 
     map.put(str.substring(i, i+1), true); 
    } 
    System.out.println(map.keySet()); 

В результате

[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z] 

Эй! Все в порядке! Ну, причина в том, что функция hashCode() String довольно паршивая, и это исключительно паршиво для односимвольных строк. Вот String's hashCode() specification. По сути, это сумма и умножение, но для односимвольной строки это всего лишь значение Unicode этого char. Таким образом, хэш-коды односимвольных строк выше 65, 66, ... 90. Внутренняя таблица HashMap всегда равна двум, и в этом случае она составляет 64 записи. Используемая запись таблицы - это значение ключа hashCode(), сдвинутое вправо на 16 бит и XORed с самим собой, по модулю размера таблицы. (See the code here.) Таким образом, эти строки односимвольных в конечном итоге в последовательных ведрах в HashMap таблицы, в позиции массива 1, 2, ... 26.

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

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

Map<HashCodeSame, Boolean> map = new HashMap<>(); 
    for (int i = 0; i < 8; i++) { 
     map.put(new HashCodeSame(i), true); 
    } 
    System.out.println(map.keySet()); 

(Я добавил toString() метод, который делает очевидную вещь) результат:.

[HCS(0), HCS(1), HCS(2), HCS(3), HCS(4), HCS(5), HCS(6), HCS(7)] 

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

Но подождите! В JDK 8 HashMap преобразует ведро из линейного связанного списка в сбалансированное дерево, если в том же ведро появляется слишком много записей. Это происходит, если более 8 записей попадают в одно и то же ведро. Давайте попробуем, что:

Map<HashCodeSame, Boolean> map = new HashMap<>(); 
    for (int i = 0; i < 20; i++) { 
     map.put(new HashCodeSame(i), true); 
    } 
    System.out.println(map.keySet()); 

Результат:

[HCS(5), HCS(0), HCS(1), HCS(2), HCS(3), HCS(4), HCS(6), 
HCS(18), HCS(7), HCS(11), HCS(16), HCS(17), HCS(15), HCS(13), 
HCS(14), HCS(8), HCS(12), HCS(9), HCS(10), HCS(19)] 

Суть заключается в том, что HashMap делает не поддерживать определенный порядок итераций. Если вам нужен конкретный порядок итераций, вы должны указать LinkedHashMap или отсортированную карту, например TreeMap. К сожалению, HashMap имеет порядок итераций, который достаточно стабилен и предсказуем, по сути, достаточно предсказуем, чтобы убаюкать людей, думая, что его порядок четко определен, на самом деле это не так.


Чтобы помочь в борьбе с этой проблемой, в JDK 9, новые реализации хэш на основе коллекции будут рандомизации их порядок итерации от запуска к запуску. Например:

Set<String> set = Set.of("A", "B", "C", "D", "E", 
          "F", "G", "H", "I", "J"); 
    System.out.println(set); 

Это распечатывается следующее при запуске в различных вызовах в JVM:.

[I, H, J, A, C, B, E, D, G, F] 
[C, B, A, G, F, E, D, J, I, H] 
[A, B, C, H, I, J, D, E, F, G] 

(Итерация порядок является стабильным в пределах одного пробега JVM Кроме того, существующие коллекции, такие а HashMap сделать не бы их порядок итерации рандомизированы.)

+0

Спасибо за такое замечательное объяснение.! – dgupta3091

+0

Я не согласен с тем, что 'String.hashCode' является паршивым, даже при сужении представления в одном символе' String'. Все одиночные символы 'String' имеют четкий хэш-код, поэтому неясно, чего вы ожидаете от него. Выполнение произвольных преобразований этого значения в надежде на то, что реализация конкретной хэш-карты получит выгоду? Поскольку это дизайнерское решение этой конкретной реализации хеш-карты, чтобы использовать полномочия двух в качестве размера, это также задача реализации этой хэш-карты для выполнения соответствующих преобразований, особенно учитывая, как часто она уже изменилась ... – Holger

+0

@Holger 'String. hashCode' предоставляет различные хеш-коды, поэтому в этом отношении все нормально, но он не распространяет значения в 32-битном хэш-кодовом пространстве. Вот где это паршиво. Хорошее распределение важно для таких вещей, как закрытое хеширование или если вы хотите разбить таблицу на параллельную обработку. 'HashMap' пытался по-разному смешивать биты, но для коротких строк это неэффективно, так как их хэш-коды имеют так много нулей. –

0

Ответ на ваш вопрос в Java документе для LinkedHashMap

хэша-таблицы и реализация связанного списка интерфейса Map, с предсказуемым порядком итерации. Эта реализация отличается от HashMap тем, что она поддерживает двусвязный список, проходящий через все его записи. Этот связанный список определяет порядок итераций, который обычно является порядком, в котором ключи были вставлены в карту (порядок вставки). Обратите внимание, что порядок вставки не изменяется, если ключ повторно вставлен в карту. (Ключ k повторно вставляется в карту m, если m.put (k, v) вызывается, когда m.containsKey (k) возвращает true непосредственно перед вызовом.)

Эта реализация избавляет своих клиентов от неуказанных , обычно хаотическое упорядочение, предоставляемое HashMap (и Hashtable), без увеличения стоимости, связанной с TreeMap. Он может быть использован для создания копии карты, которая имеет тот же порядок, что и оригинал, независимо от реализации оригинальной карты:

void foo(Map m) { 
    Map copy = new LinkedHashMap(m); 
    ... 
} 
+0

Моя проблема заключается в том, что при работе над JDK 1.8 я могу видеть, что порядок сохранен, независимо от того, сколько раз я удаляю или добавляю элемент. Как это происходит? – dgupta3091

 Смежные вопросы

  • Нет связанных вопросов^_^