Поиск * ключа * в Java (Concurrent) HashMap

Могу ли я получить ссылку на фактический ключ в (Concurrent)HashMap (или HashSet), выполнив поиск ключа, который equals() это? Как?

Я ищу что-то вроде getEntry(K key).getKey(), которое будет давать мне одну и ту же ссылку каждый раз, когда я обращаюсь к ключу после первой вставки, поэтому я могу использовать эту ссылку вместо свежесгенерированного ключа для экономии памяти.

(Очевидно, что для этой цели можно выделить специальный HashMap‹K,K›, но на самом деле у меня уже есть карта, и мне было интересно, могу ли я использовать ее ключи для этой цели)


person Just Me    schedule 30.11.2020    source источник
comment
это, похоже, отвечает на аналогичный вопрос (суть: вы должны использовать HashMap‹K,K›. Почему? потому что )   -  person Just Me    schedule 30.11.2020
comment
Я не согласен с тем, что ответ, указанный в обосновании закрытия этого вопроса, отвечает на него, поскольку он не обеспечивает эффективный способ поиска ключа. Ссылка, которую я предоставил, ближе к дому (в основном, она предполагает ответ «нет, вы не можете»), хотя она относится к наборам (так что, может быть, есть способ сделать это с помощью HashMaps?) и не упоминает приложение к памяти сохранение. Пожалуйста, пересмотрите возможность открытия этого вопроса.   -  person Just Me    schedule 30.11.2020
comment
Нет встроенного способа сделать это. Лучшее, что вы можете сделать, как описано в описании, это, например. есть Map<K, Map.Entry<K, V>>.   -  person Louis Wasserman    schedule 30.11.2020
comment
Спасибо @LouisWasserman. Может быть, вы можете открыть вопрос и предоставить это как ответ? Я не думаю, что это дубликат чего-то еще.   -  person Just Me    schedule 01.12.2020
comment
@basil Вопросы были совершенно одинаковыми. Чего, по вашему мнению, не хватало в ответах?   -  person Sotirios Delimanolis    schedule 01.12.2020
comment
@SotiriosDelimanolis связанный ответ является отличным объяснением того, почему Map не предоставляет метод для удовлетворения потребностей этого вопроса. Но этот вопрос требовал решения, а не объяснения. Я купил аргументы автора этого Вопроса с просьбой открыть его снова, а также подумал о потенциальном решении. Если я слишком поторопился с суждением, дайте мне знать, и я снова проголосую за закрытие и удаление своего ответа.   -  person Basil Bourque    schedule 01.12.2020
comment
@BasilBourque Я использовал дубликат этот, а не вещь, которую они связали (если вы это имели в виду). Один из этих ответов по сути такой же, как тот, который вы предоставили здесь.   -  person Sotirios Delimanolis    schedule 01.12.2020
comment
Отвечает ли это на ваш вопрос? Получить ключевой объект из HashMap в Java   -  person cigien    schedule 01.12.2020
comment
@cigien эти вопросы действительно очень близки, но правда в том, что у меня не было проблем с написанием обходного пути, подобного предложенным (также в самом вопросе). Вместо этого я искал элегантное (эффективное, менее оспариваемое потоками) встроенное решение: именно так вы получаете ключ, используя существующий API. И ответ, который Андреас дал ниже, хорошо подходит для этого: вы не можете (без поиска методом грубой силы). Не видел этого в другом месте, за исключением аналогичного обсуждения HashSet, указанного выше.   -  person Just Me    schedule 01.12.2020


Ответы (3)


Вы не можете получить исходный ключ из HashMap, без простого последовательного поиска методом перебора:

K keyToLookup = ...;

K originalKey = map.keySet().stream().filter(keyToLookup::equals).findAny().orElse(null);

Вариант 1. Встроить ключ в значение

Обходной путь для HashMap, конечно, состоит в том, чтобы ключевой объект был частью значения:

  • Имея ключевой объект как часть объекта значения, который часто является неотъемлемым, например. сопоставление имени пользователя с пользовательским объектом. Может потребоваться изменить объект-значение, а также может потребоваться удаление и повторное добавление записи карты при ее обновлении для ссылки на другой объект-значение.

  • В отдельном Map<K, K>. Менее эффективен, так как приходится дважды смотреть вверх.

  • Изменив значение на пару ключ/значение, например. Map<K, Entry<K, V>>. Вероятно, это лучшее решение, но оно требует осторожности, чтобы ключевой объект Entry всегда был исходным ключом.


Вариант 2. Используйте NavigableMap

Если Map можно изменить с HashMap на NavigableMap, например. a TreeMap, он поддерживает извлечение исходного ключевой объект с карты, напр. с помощью ceilingEntry(K key)​ метод.

Ключевой объект должен реализовывать Comparable или TreeMap можно использовать собственный Comparator. В любом случае реализация должна быть совместима с равными.

Не все типы ключей могут определять относительный порядок, поэтому использование NavigableMap может оказаться невозможным.

K keyToLookup = ...;

Entry<K,​V> entry = map.ceilingEntry​(keyToLookup);
if (entry != null && entry.getKey().equals(keyToLookup)) {
    K originalKey = entry.getKey();
    V value = entry.getValue();
    // code here
} else {
    // key not found
}
person Andreas    schedule 30.11.2020

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

Тем не менее, вы не можете достичь того, чего хотите, с помощью HashMap из коробки. Самое близкое, что вы могли бы сделать, это расширить его, чтобы открыть метод getNode(...), что позволит вам получить доступ к ключу.

person dagnelies    schedule 30.11.2020

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

Хотя я еще не пробовал этот код, я думаю, вы можете сделать это, получив Set ключей карты. Затем конвертируйте в List. Найдите нужный объект в этом списке, извлекая ссылку из элемента списка.

Map< Car , Integer > map = … ;
Car car = new Car( … ) ;
map.put( car , 42 ) ;

Set< Car > carSet = map.keySet() ;
List< Car > carList = List.copyOf( carSet ) ;
Car similarCar = … ;
int index = carList.indexOf( similarCar ) ;
Car originalCar = carList.get( index ) ;  

boolean same = ( car == originalCar ) ;

Вы также можете создать поток из набора ключей, ища совпадение, при этом поток останавливается при первом совпадении.

person Basil Bourque    schedule 30.11.2020
comment
Это довольно неэффективно. Если вы собираетесь выполнять линейное сканирование, вы можете просто использовать Iterator из Set, дополнительная копия и indexOf не нужны. - person Savior; 01.12.2020
comment
Я думаю, что смысл вопроса в том, чтобы сделать это без использования последовательного перебора грубой силы. --- К вашему сведению: слишком много кода для чего-то, что можно сделать проще, используя map.keySet().stream().filter(similarCar::equals).findAny().orElse(null) - person Andreas; 01.12.2020
comment
@Andreas Андреас У меня была та же идея, что и в вашем комментарии, но я пытался явно выделить шаги в более длинном непотоковом коде. - person Basil Bourque; 01.12.2020
comment
@Andreas Где вопрос касается эффективности поиска или предотвращения последовательного поиска? Единственная проблема, которую я вижу в эффективности, — это желание сэкономить память за счет доступа к ссылке на исходный объект, используемый в качестве ключа. Я подозреваю, что некоторые из вас читают в Вопросе больше, чем там. - person Basil Bourque; 01.12.2020
comment
@BasilBourque Одной из основных причин использования карты является производительность поиска. Поскольку вопрос уже рассматривает HashMap<K,K>, очевидно, что производительность вызывает беспокойство. --- Кроме того, поскольку заявленной причиной вопроса является оптимизация памяти, ожидается, что карта станет большой (в противном случае это не будет большой проблемой), поэтому последовательный поиск станет реальным узким местом производительности. --- Я подозреваю, что вы игнорируете ключевой аспект проблемы, когда просто отвечаете: выполните последовательный поиск грубой силы. - person Andreas; 01.12.2020