Как создать HashMap с двумя ключами (Key-Pair, Value)?
У меня есть 2D-массив целых чисел. Я хочу, чтобы они были помещены в HashMap. Но я хочу получить доступ к элементам из HashMap на основе индекса массива. Что-то вроде:
Для A [2] [5], map.get(2,5)
, который возвращает значение, связанное с этим ключом. Но как создать хэш-карту с помощью пары ключей? Или, в общем, несколько ключей: Map<((key1, key2,..,keyN), Value)
таким образом, что я могу получить доступ к элементу с помощью get (key1, key2,... keyN).
РЕДАКТИРОВАТЬ: через 3 года после размещения вопроса я хочу добавить немного больше
Я столкнулся с другим способом для NxN matrix
.
Индексы массивов, i
и j
могут быть представлены как один key
следующим образом:
int key = i * N + j;
//map.put(key, a[i][j]); // queue.add(key);
И индексы могут быть восстановлены из key
следующим образом:
int i = key / N;
int j = key % N;
Ответы
Ответ 1
Есть несколько вариантов:
2 размера
Карта карт
Map<Integer, Map<Integer, V>> map = //...
//...
map.get(2).get(5);
Ключевой объект Wrapper
public class Key {
private final int x;
private final int y;
public Key(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Key)) return false;
Key key = (Key) o;
return x == key.x && y == key.y;
}
@Override
public int hashCode() {
int result = x;
result = 31 * result + y;
return result;
}
}
Реализация equals()
и hashCode()
имеет решающее значение. Тогда вы просто используете:
Map<Key, V> map = //...
а также:
map.get(new Key(2, 5));
Table<Integer, Integer, V> table = HashBasedTable.create();
//...
table.get(2, 5);
Table
использует карту карт внизу.
N размеры
Обратите внимание, что специальный класс Key
является единственным подходом, который масштабируется до n-измерений. Вы также можете рассмотреть:
Map<List<Integer>, V> map = //...
но это ужасно с точки зрения производительности, а также читабельности и корректности (нелегкий способ навязать размер списка).
Может быть, взгляните на Scala, где у вас есть кортежи и классы case
(замена всего класса Key
однострочным).
Ответ 2
Когда вы создаете свой собственный объект пары ключей, вы должны столкнуться с несколькими вещами.
Во-первых, вы должны знать об использовании hashCode()
и equals()
. Вам нужно будет это сделать.
Во-вторых, при реализации hashCode()
убедитесь, что вы понимаете, как это работает. Данный пример пользователя
public int hashCode() {
return this.x ^ this.y;
}
на самом деле является одной из худших реализаций, которые вы можете сделать. Причина проста: у вас много равных хэшей! И hashCode()
должен возвращать значения int, которые, как правило, редки, уникальны в лучшем случае. Используйте что-то вроде этого:
public int hashCode() {
return (X << 16) + Y;
}
Это быстро и возвращает уникальные хэши для ключей между -2 ^ 16 и 2 ^ 16-1 (от -65536 до 65535). Это подходит практически в любом случае. Очень редко вы выходите из этой границы.
В-третьих, при реализации equals()
также знать, для чего он используется, и знать о том, как вы создаете свои ключи, поскольку они являются объектами. Часто вы делаете ненужные утверждения, потому что вы всегда будете иметь одинаковый результат.
Если вы создадите такие ключи: map.put(new Key(x,y),V);
, вы никогда не сравните ссылки своих ключей. Потому что каждый раз, когда вы хотите получить карту, вы будете делать что-то вроде map.get(new Key(x,y));
. Поэтому ваш equals()
не требует выражения типа if (this == obj)
. Он будет никогда.
Вместо if (getClass() != obj.getClass())
в equals()
лучше использовать if (!(obj instanceof this))
. Он будет действителен даже для подклассов.
Итак, единственное, что вам нужно сравнить, это на самом деле X и Y. Таким образом, лучшая реализация equals()
в этом случае будет:
public boolean equals (final Object O) {
if (!(O instanceof Key)) return false;
if (((Key) O).X != X) return false;
if (((Key) O).Y != Y) return false;
return true;
}
Итак, в конце ваш ключевой класс выглядит следующим образом:
public class Key {
public final int X;
public final int Y;
public Key(final int X, final int Y) {
this.X = X;
this.Y = Y;
}
public boolean equals (final Object O) {
if (!(O instanceof Key)) return false;
if (((Key) O).X != X) return false;
if (((Key) O).Y != Y) return false;
return true;
}
public int hashCode() {
return (X << 16) + Y;
}
}
Вы можете указать индексы измерения X
и Y
уровня общего доступа из-за того, что они являются окончательными и не содержат конфиденциальной информации. Я не на 100% уверен, что уровень доступа private
работает корректно в любом случае при нажатии Object
на Key
.
Если вы задаетесь вопросом о финале, я объявляю что-либо как окончательное, значение которого устанавливается на instancing и никогда не изменяется - и, следовательно, является константой объекта.
Ответ 3
У вас не может быть хэш-карта с несколькими ключами, но вы можете иметь объект, который принимает несколько параметров в качестве ключа.
Создайте объект под названием Index, который принимает значения x и y.
public class Index {
private int x;
private int y;
public Index(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public int hashCode() {
return this.x ^ this.y;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Index other = (Index) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
}
Затем получите HashMap<Index, Value>
, чтобы получить результат.:)
Ответ 4
Реализовано в общих коллекциях MultiKeyMap
Ответ 5
Две возможности. Или используйте комбинированный ключ:
class MyKey {
int firstIndex;
int secondIndex;
// important: override hashCode() and equals()
}
Или карта карты:
Map<Integer, Map<Integer, Integer>> myMap;
Ответ 6
Используйте Pair
как клавиши для HashMap
. JDK не имеет пары, но вы можете использовать библиотеку третьей стороны, такую как http://commons.apache.org/lang или написать собственный тэп пары.
Ответ 7
Создайте класс значений, который будет представлять ваш составной ключ, например:
class Index2D {
int first, second;
// overrides equals and hashCode properly here
}
чтобы правильно отрегулировать equals()
и hashCode()
. Если это похоже на большую работу, вы можете рассмотреть некоторые готовые общие универсальные контейнеры, такие как Pair
, предоставляемые другими пользователями apache.
Здесь также есть много похожих вопросов, с другими идеями, такими как использование Guava Table, хотя позволяет клавишам иметь разные типы, которые могут быть чрезмерными (в использовании и сложности памяти) в вашем случае, так как я понимаю, что ваши ключи являются целыми числами.
Ответ 8
Если это два целых числа, вы можете попробовать быстрый и грязный трюк: Map<String, ?>
с помощью клавиши i+"#"+j
.
Если клавиша i+"#"+j
совпадает с j+"#"+i
try min(i,j)+"#"+max(i,j)
.
Ответ 9
Вы можете создать свой ключевой объект примерно так:
открытый класс MapKey {
public Object key1;
public Object key2;
public Object getKey1() {
return key1;
}
public void setKey1(Object key1) {
this.key1 = key1;
}
public Object getKey2() {
return key2;
}
public void setKey2(Object key2) {
this.key2 = key2;
}
public boolean equals(Object keyObject){
if(keyObject==null)
return false;
if (keyObject.getClass()!= MapKey.class)
return false;
MapKey key = (MapKey)keyObject;
if(key.key1!=null && this.key1==null)
return false;
if(key.key2 !=null && this.key2==null)
return false;
if(this.key1==null && key.key1 !=null)
return false;
if(this.key2==null && key.key2 !=null)
return false;
if(this.key1==null && key.key1==null && this.key2 !=null && key.key2 !=null)
return this.key2.equals(key.key2);
if(this.key2==null && key.key2==null && this.key1 !=null && key.key1 !=null)
return this.key1.equals(key.key1);
return (this.key1.equals(key.key1) && this.key2.equals(key2));
}
public int hashCode(){
int key1HashCode=key1.hashCode();
int key2HashCode=key2.hashCode();
return key1HashCode >> 3 + key2HashCode << 5;
}
}
Преимущество этого: всегда будет следить за тем, чтобы вы покрывали весь сценарий равных.
ПРИМЕЧАНИЕ. Ваш ключ1 и ключ2 должны быть неизменными. Только тогда вы сможете построить стабильный ключевой объект Object.
Ответ 10
Для этого также можно использовать реализацию guava Table.
Таблица представляет собой специальную карту, в которой два ключа могут быть указаны совместно, чтобы ссылаться на одно значение. Это похоже на создание карты карт.
//create a table
Table<String, String, String> employeeTable = HashBasedTable.create();
//initialize the table with employee details
employeeTable.put("IBM", "101","Mahesh");
employeeTable.put("IBM", "102","Ramesh");
employeeTable.put("IBM", "103","Suresh");
employeeTable.put("Microsoft", "111","Sohan");
employeeTable.put("Microsoft", "112","Mohan");
employeeTable.put("Microsoft", "113","Rohan");
employeeTable.put("TCS", "121","Ram");
employeeTable.put("TCS", "122","Shyam");
employeeTable.put("TCS", "123","Sunil");
//get Map corresponding to IBM
Map<String,String> ibmEmployees = employeeTable.row("IBM");
Ответ 11
мы можем создать класс для передачи более одного ключа или значения, и объект этого класса можно использовать в качестве параметра на карте.
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;
public class key1 {
String b;
String a;
key1(String a,String b)
{
this.a=a;
this.b=b;
}
}
public class read2 {
private static final String FILENAME = "E:/studies/JAVA/ReadFile_Project/nn.txt";
public static void main(String[] args) {
BufferedReader br = null;
FileReader fr = null;
Map<key1,String> map=new HashMap<key1,String>();
try {
fr = new FileReader(FILENAME);
br = new BufferedReader(fr);
String sCurrentLine;
br = new BufferedReader(new FileReader(FILENAME));
while ((sCurrentLine = br.readLine()) != null) {
String[] s1 = sCurrentLine.split(",");
key1 k1 = new key1(s1[0],s1[2]);
map.put(k1,s1[2]);
}
for(Map.Entry<key1,String> m:map.entrySet()){
key1 key = m.getKey();
String s3 = m.getValue();
System.out.println(key.a+","+key.b+" : "+s3);
}
// }
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null)
br.close();
if (fr != null)
fr.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
Ответ 12
Вы можете скачать его по ссылке ниже:
https://github.com/VVS279/DoubleKeyHashMap/blob/master/src/com/virtualMark/doubleKeyHashMap/DoubleKeyHashMap.java
https://github.com/VVS279/DoubleKeyHashMap
Вы можете использовать двойной ключ: значение hashmap,
DoubleKeyHashMap<Integer, Integer, String> doubleKeyHashMap1 = new
DoubleKeyHashMap<Integer, Integer, String>();
DoubleKeyHashMap<String, String, String> doubleKeyHashMap2 = new
DoubleKeyHashMap<String, String, String>();