Как создать хэш-карту с двумя ключами (Key-Pair, Value)?


у меня есть 2D массив целых чисел. Я хочу, чтобы они были помещены в хэш-карту. Но я хочу получить доступ к элементам из HashMap на основе индекса массива. Что-то вроде:

Для A[2][5],map.get(2,5) который возвращает значение, связанное с этим ключом. Но как создать хэш-карту с парой ключей? Или вообще, несколько ключей: Map<((key1, key2,..,keyN), Value) таким образом,что я могу получить доступ к элементу с помощью get(key1, key2,...Кейн).

изменить : через 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;
11 91

11 ответов:

есть несколько вариантов:

2 габариты

карта карты

Map<Integer, Map<Integer, V>> map = //...
//...

map.get(2).get(5);

объект ключа оболочки

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 из гуавы

Table<Integer, Integer, V> table = HashBasedTable.create();
//...

table.get(2, 5);

Table использует карта карты внизу.

N размеры

уведомления что особенного Key класс-это единственный подход, который масштабируется до n-размеры. Вы также можете рассмотреть:

Map<List<Integer>, V> map = //...

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

может быть, взгляните на Scala, где у вас есть кортежи и case классы (заменяя все Key класса с одним вкладышем).

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

во-первых, вы должны быть в курсе реализации 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). Это будет никогда occure.

вместо if (getClass() != obj.getClass()) в своем equals() лучше использовать if (!(obj instanceof this)). Это будет справедливо даже для подклассов.

так что единственное, что вам нужно сравнить, это на самом деле X и Я. Так что лучший 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.

если вы задаетесь вопросом о финал, я объявляю что - либо как final, значение которого устанавливается на instancing и никогда не меняется-и поэтому является константой объекта.

вы не можете иметь хэш-карту с несколькими ключами, но вы можете иметь объект, который принимает несколько параметров в качестве ключа.

создайте объект с именем 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> чтобы получить свой результат. :)

две возможности. Либо используйте комбинированный ключ:

class MyKey {
    int firstIndex;
    int secondIndex;
    // important: override hashCode() and equals()
}

или карта карты:

Map<Integer, Map<Integer, Integer>> myMap;

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

class Index2D {
  int first, second;

  // overrides equals and hashCode properly here
}

заботясь, чтобы переопределить equals() и hashCode() правильно. Если это кажется большой работой, вы можете рассмотреть некоторые готовые универсальные контейнеры, такие как Pair предоставлено apache commons среди прочих.

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

если они два целых числа, вы можете попробовать быстрый и грязный трюк: Map<String, ?> используя ключ как i+"#"+j.

если ключ i+"#"+j это то же самое, что и j+"#"+i попробовать min(i,j)+"#"+max(i,j).

вы можете создать свой ключевой объект примерно так:

открытый класс 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;
}

}

преимущество этого: он всегда будет убедиться, что вы охватываете все сценарии равных, а также.

Примечание: ваши key1 и key2 должны быть неизменяемыми. Только тогда вы сможете построить стабильный ключевой объект.

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

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();

        }

    }

    }

 }

использовать Pair как ключи для HashMap. JDK не имеет пары, но вы можете использовать стороннюю библиотеку, такую какhttp://commons.apache.org/lang или написать пару тайпе самостоятельно.

вы также можете использовать гуавы таблица для его реализации.

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

//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");