HashMap и int как ключ

104

Я пытаюсь создать HashMap, в котором будут целые числа в качестве ключей и объекты в качестве значений.

Мой синтаксис:

HashMap<int, myObject> myMap = new HashMap<int, myObject>();

Однако возвращаемая ошибка - Синтаксическая ошибка токена "int", ожидаемые размеры после этого токена - я не понимаю, почему я должен добавить измерение (например, преобразовать int в массив), поскольку мне нужно сохранить только цифру как ключ.

Что я мог сделать?

Заранее спасибо! :)

MrD
источник
14
HashMapне обрабатывает примитивы, только объекты.
Menno
Связанный вопрос SO , но с intценностью, а не с ключом.
cyroxx
5
IntegerВместо этого используйте .
Hot Licks
Лучше автоматизировать int в Integer или просто хранить данные в виде String, что более удобно?
Марчин Эрбель

Ответы:

25

Вы не можете использовать примитив, потому что HashMap использует объект внутри для ключа. Таким образом, вы можете использовать только объект, унаследованный от Object (то есть любой объект).

Это функция put () в HashMap, и, как вы можете видеть, она использует Object для K:

public V put(K key, V value) {
    if (key == null)
        return putForNullKey(value);
    int hash = hash(key);
    int i = indexFor(hash, table.length);
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
        }
    }

    modCount++;
    addEntry(hash, key, value, i);
    return null;
}

Выражение «k = e.key» должно прояснить это.

Я предлагаю использовать обертку типа Integer и autoboxing.

user1883212
источник
137

IntegerВместо этого используйте .

HashMap<Integer, MyObject> myMap = new HashMap<Integer, MyObject>();

Java автоматически добавит ваши intпримитивные значения в Integerобъекты.

Дополнительные сведения об автобоксировании см. В документации Oracle Java.

Габорш
источник
10
Ему также не следует называть классmyObject
Адам Гент
@AdamGent Правильно. Все имена классов должны начинаться с заглавных букв, я исправил рекомендуемый код.
gaborsch
3
Я знаю, что вы знаете :) Я просто хочу убедиться, что ОП знает / учится. Насколько я знаю, он мог указать имя переменной в параметре типа.
Адам Гент
1
Небольшая заметка, лучше использовать Android ArrayMapили SimpleArrayMapAndroid для экономии памяти и повышения производительности ( дополнительная информация )
Ной Хупперт
42

Для всех, кто кодирует Java для устройств Android и оказывается здесь: используйте SparseArrayдля повышения производительности;

private final SparseArray<myObject> myMap = new SparseArray<myObject>();

с этим вы можете использовать int вместо Integer, например;

int newPos = 3;

myMap.put(newPos, newObject);
myMap.get(newPos);
Степоид
источник
7
Помните, что SparseArray медленнее, чем hashmap, но более эффективно использует память. Так что не используйте его для больших наборов данных.
TpoM6oH
Как SparseArray используется для повышения производительности, когда он работает медленнее? Какой из них использовать в моей игре для Android
Snake,
@Snake. SparseArrayЕсли вы выделите кучу вставок для упаковки и распаковки памяти, как в случае с a HashMap, виртуальной машине нужно будет приостановить выполнение для сборки мусора раньше. Это важно, если вы пытаетесь что-то делать часто и быстро.
Джон
1
Помните , что сложность вставки в SparseArrayэто O (п) ( HashMapимеет O (1) ). Это важно при большом количестве элементов. Вставка в начало такого массива происходит намного медленнее.
Владимир Петракович
1
@ М.каземАхгары Не совсем так. put()принимает O(n)(не n log n) для вставки в начале, потому что он находит позицию, а затем сдвигает все следующие элементы. delete()сам по себе действительно принимает O(log n), но следующая вставка или повторение элементов после удаления потребует сборки мусора, которая принимает O(n).
Владимир Петракович
4

Основная причина, по которой HashMap не позволяет использовать примитив в качестве ключей, заключается в том, что HashMap разработан таким образом, что для сравнения ключей он использует метод equals () , и метод может быть вызван только для объекта, а не для примитива.

Таким образом, когда int автоматически преобразуется в Integer, Hashmap может вызывать метод equals () для объекта Integer.

Вот почему вы должны использовать Integer вместо int. Я имею в виду, что hashmap выдает ошибку при вводе int в качестве ключа (не знаю смысла выданной ошибки)

И если вы так думаете, вы можете повысить производительность Map, сделав примитив в качестве ключа, существует библиотека FastUtil, которая содержит реализацию Map с типом int в качестве ключа.

Из-за этого он намного быстрее, чем Hashmap.

Отец Мэтью
источник
1
Нет, основная причина запрета примитивных типов - это стирание типов в Java, которое эффективно превращается Map<Integer, String>во Map<Object, Object>время компиляции. Кстати, есть IdentityHashMap, который использует ==оператор для проверки равенства, который по-прежнему не допускает примитивных типов.
Yoory N. 05
3

HashMap не допускает примитивных типов данных в качестве аргументов. Он может принимать только объекты, поэтому

HashMap<int, myObject> myMap = new HashMap<int, myObject>();

не будет работать.

Вы должны изменить декларацию на

HashMap<Integer, myObject> myMap = new HashMap<Integer, myObject>();

поэтому даже если вы сделаете следующее

myMap.put(2,myObject);

Примитивный тип данных автоматически упаковывается в объект Integer.

8 (int) === boxing ===> 8 (Integer)

Вы можете узнать больше об автобоксе здесь http://docs.oracle.com/javase/tutorial/java/data/autoboxing.html

Лакшми
источник
1

используйте int как объект, а не как примитивный тип

HashMap<Integer, myObject> myMap = new HashMap<Integer, myObject>();
franki3xe
источник
Я написал HashMap <Integer, MyObject> myMap = new HashMap <Integer, MyObject> (); но отображение делает мою проблему с> и <для отображения хорошего ответа
franki3xe
Я знаю, что печатать это больно, но я пытался спасти вас от немедленного -1. Я в отличие от других комментирую перед наказанием (я не -1 вам).
Адам Гент
0

Пожалуйста, используйте HashMap<Integer, myObject> myMap = new HashMap<Integer, myObject>();

Майкл
источник
0

Я не понимаю, почему я должен добавлять измерение (например, превращать int в массив), поскольку мне нужно сохранить только цифру в качестве ключа.

Массив также является объектом, поэтому HashMap<int[], MyObject>это допустимая конструкция, которая использует массивы int в качестве ключей.

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

Йори Н.
источник
1
Будьте осторожны, хеш-значение массива не связано с его содержимым, поэтому два массива с одинаковым содержимым могут хешировать разные значения, что делает их очень плохим ключом.
john16384
0

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

import org.eclipse.collections.impl.map.mutable.primitive.IntObjectHashMap;

public class Check {
    public static void main(String[] args) {
        IntObjectHashMap map = new IntObjectHashMap();

        map.put(5,"It works");
        map.put(6,"without");
        map.put(7,"boxing!");

        System.out.println(map.get(5));
        System.out.println(map.get(6));
        System.out.println(map.get(7));
    }
}

В этом примере выше IntObjectHashMap .

Поскольку вам нужно сопоставление int-> object , также рассмотрите возможность использования YourObjectType[]массива или List<YourObjectType>доступа к значениям по индексу, поскольку map по своей природе является ассоциативным массивом с типом int в качестве индекса.

Alex
источник