DennisKingsman
@DennisKingsman
Студент

Как именно работает map?

Здравствуйте, я задался вопросом о структуре и работе Map, прочитав несколько статей. Ответы на эти вопросы я не смог найти или упустил при беглом чтении.
у меня есть такой класс ключа
public class KeyClass {

    private int firstKeyValue;
    private String secondKeyValue;

    public KeyClass(int firstKeyValue, String secondKeyValue) {
        this.firstKeyValue = firstKeyValue;
        this.secondKeyValue = secondKeyValue;
    }

    public int getFirstKeyValue() {
        return firstKeyValue;
    }

    public void setFirstKeyValue(int firstKeyValue) {
        this.firstKeyValue = firstKeyValue;
    }

    public String getSecondKeyValue() {
        return secondKeyValue;
    }

    public void setSecondKeyValue(String secondKeyValue) {
        this.secondKeyValue = secondKeyValue;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }

        if (obj == null || obj.getClass() != this.getClass()) {
            return false;
        }

        KeyClass o = (KeyClass) obj;

        return (firstKeyValue == o.firstKeyValue) && (secondKeyValue.equals(o.secondKeyValue));
    }

    @Override
    public int hashCode() {
        char[] array = secondKeyValue.toCharArray();

        int sum = 0;

        for(int i = 0; i < array.length; ++i){
            sum += array[i];
        }
        return firstKeyValue + sum;
    }
}


и собстна метода main
public class Main {

    public static void main(String[] args) {
        KeyClass keyClass = new KeyClass(1, "one");

        Map map = new HashMap<KeyClass, String>();

        //завели бакет по этому ключу(номер бакета определяется по хешу)
        map.put(keyClass, "first");

        //поменяли ключ(надеемся на потерю значения)
        keyClass.setFirstKeyValue(23);
        keyClass.setSecondKeyValue(new String("one")); //чтобы даже не в пуле лежала а просто в хипе(чтоб наверняка :в )

        //выведет null (так как элемент утерян) Отлично!!!
        System.out.println("first value " + map.get(keyClass));

        //выведет null (так как такого элемента нет) Отлично!!!
        System.out.println("unexisted value " + map.get("str"));

        //размер 1 так как элемент все равно внтури (несмотря на то, что он утерян)
        //вроде по умолчанию же 16 должен быть???  load factor все дела ?!
        System.out.println("size " + map.size());

        //кладем в новый бакет(старый с "first" все еще существует)
        map.put(keyClass, "alien");
        System.out.println("basket with alien" + map.get(keyClass));

        //тут я решил посмотреть как будет работать хеш код с учетом неинтернированной строки
        System.out.println("hashCode1 : " + keyClass.hashCode());
        System.out.println("hashCode2 : " + new KeyClass(23, "one").hashCode());
        //хеш одинаковый так как по моему переопределению хешкода место в памяти никак не влияет (ну типа да я так и задумывал)
        // но, я в строке использую int значения кодировки символа так что надо рассмотреть следующее
        System.out.println("hashCode3 : " + new KeyClass(23, "eno").hashCode()); //ага!!!!!!(хеш такой же)
        System.out.println("equals method : "+ new KeyClass(23, "one").equals(new KeyClass(23, "eno"))); //дает false, значит я молодец

        //заводим новый бакет
        KeyClass anotherOne = new KeyClass(23, "two");
        map.put(anotherOne, "new");

        //заводим бакет который который будет всегда первым
        map.put(null, null);
        //значение перетерлось так как hashCode одинаков и equals вернул true
        map.put(null, "afterNull");

        //тут происходит перетерание по той же причине что и выше, я вообще рассчитывал на лист
        map.put(keyClass, "second");
        map.put(keyClass, "third");

        //и тут поидее я добавляю элемент в бакет с хешом как у keyClass переменной(тоесть "third" и "addToList" должны сформировать лист)
        map.put(new KeyClass(23, "eno"), "addToList");

        //и тут лежит только third ?!
        //Но ведь в случае если хеш одинаковый и equals true, эл-т затирается, а если hash одинаковый но equals false, то создается лист
        System.out.println("List : " + map.get(keyClass));

        //выведет [afterNull, new, first, third, addToList] но тут мы листа не видим ?!
        System.out.println(Arrays.toString(map.values().toArray()));

    }
}

1) почему значение длины 1 а не то, которое по умолчанию?
2) верны ли мои рассуждения описанные в комментариях?
3) почему мне так и не удалось получить лист?
  • Вопрос задан
  • 252 просмотра
Решения вопроса 1
zagayevskiy
@zagayevskiy Куратор тега Java
Android developer at Yandex
size всего лишь возвращает, сколько ключей реально лежит в папке.
Все твои рассуждения про "завели бакет", "кладем в новый бакет" неверны. Ты ничего не заводишь, это детали реализации хеш-таблицы. Бакет это связный список, в котором лежат пары ключ-значение, у которых хеш ключа равен по модулю количества бакетов. Это и есть тот список, который ты мечтаешь получить, и это так не работает. То есть ты его не получишь, тк это детали реализации.
И далее, ты эту реализацию сломал своими мутабельными ключами. В списке теперь лежит ключ, у которого хеш не соответствует списку. Естественно, мапа не найдет по этому ключу ничего, тк будет искать в другом списке(бакете). Вывод - никогда не мутируй ключи.

//заводим бакет который который будет всегда первым
map.put(null, null);
//значение перетерлось так как hashCode одинаков и equals вернул true
map.put(null, "afterNull");


Нет, всё не так. Нулл обрабатывается просто отдельно, под него отдельный элемент, это не бакет.

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

Сколько в данный момент в мапе бакетов тебя вообще заботить не должно.

Читай код. Видно, что ты где-то нахватался неправильно (или плохо) разжеванной теории, и пытаешься на этом выехать. Большинство твоих вопросов снимут сорсы хешмапы и, в крайнем случае, дебаггер.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы