Jak utworzyć Hashmapę z dwoma kluczami (para kluczy, wartość)?

Mam tablicę liczb całkowitych 2D. Chcę je umieścić w Hashmapie. Ale chcę uzyskać dostęp do elementów z HashMap na podstawie indeksu tablicy. Coś w stylu:

Dla[2][5], map.get(2,5) który zwraca wartość powiązaną z tym kluczem. Ale jak utworzyć hashmapę za pomocą pary kluczy? Lub ogólnie, wiele kluczy: Map<((key1, key2,..,keyN), Value) w taki sposób, że mogę uzyskać dostęp do elementu za pomocą get(key1, key2,...keyN).

EDIT: po 3 latach od zamieszczenia pytania chcę dodać trochę więcej do it

Natknąłem się na inną drogę dla NxN matrix.

Indeksy tablicowe, i i j mogą być reprezentowane jako pojedyncze key w następujący sposób:

int key = i * N + j;
//map.put(key, a[i][j]); // queue.add(key); 

I indeksy można wycofać z key w ten sposób:

int i = key / N;
int j = key % N;
Author: Crocode, 2013-02-04

11 answers

Istnieje kilka opcji:

2 Wymiary

Mapa map

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

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

Wrapper key object

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;
    }

}

Implementacja equals() i hashCode() jest tutaj kluczowa. Następnie wystarczy użyć:

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

I:

map.get(new Key(2, 5));

Table z guawy

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

table.get(2, 5);

Table używa mapy map pod spodem.

N Wymiary

Zauważ, że Klasa specjalna Key jest jedynym podejściem, które skaluje się do n-wymiarów. Możesz też consider:

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

Ale to straszne z punktu widzenia wydajności, jak również czytelności i poprawności (nie jest łatwy sposób na wymusić rozmiar listy).

Może przyjrzyj się Scali, gdzie masz krotki i klasy case (zastępujące całą klasę Key klasą jednowierszową).

 164
Author: Tomasz Nurkiewicz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-11 11:37:39

Kiedy tworzysz własny obiekt pary kluczy, powinieneś zmierzyć się z kilkoma rzeczami.

Po pierwsze, powinieneś być świadomy implementacji hashCode() i equals(). Musisz to zrobić.

Po drugie, podczas implementacji hashCode() Upewnij się, że rozumiesz, jak to działa. Podany przykład użytkownika

public int hashCode() {
    return this.x ^ this.y;
}

Jest jedną z najgorszych implementacji, jakie możesz zrobić. Powód jest prosty: masz dużo równych hashów! A {[4] } powinien zwracać wartości int, które wydają się być rzadkie, unikalne w najlepszym razie. Użyj czegoś takiego:

public int hashCode() {
  return (X << 16) + Y;
}

Jest to szybkie i zwraca unikalne skróty dla kluczy od -2^16 do 2^16-1 (od -65536 do 65535). Pasuje to prawie w każdym przypadku. Bardzo rzadko jesteś poza tą granicą.

Po trzecie, podczas implementacji equals() również wiedzieć, do czego jest on używany i być świadomym, jak tworzysz swoje klucze, ponieważ są to obiekty. Często robisz niepotrzebne, jeśli wypowiedzi powodują, że zawsze będziesz miał ten sam wynik.

Jeśli utworzysz takie klucze: map.put(new Key(x,y),V); nigdy nie porównaj referencje kluczy. Ponieważ za każdym razem, gdy chcesz uzyskać dostęp do mapy, zrobisz coś w stylu map.get(new Key(x,y));. Dlatego twoje equals() nie potrzebuje takiego stwierdzenia jak if (this == obj). Nigdy się nie wydarzy.

Zamiast if (getClass() != obj.getClass()) w twoim equals() lepiej Użyj if (!(obj instanceof this)). Będzie on ważny nawet dla podklas.

Więc jedyne co trzeba porównać to w rzeczywistości X i Y. więc najlepszą equals() implementacją w tym przypadku byłoby:

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;
}

Więc w końcu twoja kluczowa klasa jest jak to:

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;
  }

}

Możesz nadać indeksom wymiarów X i Y publiczny poziom dostępu, ze względu na to, że są ostateczne i nie zawierają poufnych informacji. Nie jestem w 100% pewien, czy private poziom dostępu działa poprawnie w w każdym przypadku podczas rzucania Object na Key.

Jeśli zastanawiasz się nad finałami, deklaruję wszystko jako ostateczne, która wartość jest ustawiona na instancjach i nigdy się nie zmienia - a zatem jest stałą obiektową.

 17
Author: chrixle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-23 22:37:57

Nie możesz mieć mapy hashowej z wieloma kluczami, ale możesz mieć obiekt, który przyjmuje wiele parametrów jako klucz.

Utwórz obiekt o nazwie Index, który przyjmuje wartości x i 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;
    }
}

Następnie mieć HashMap<Index, Value>, Aby uzyskać wynik. :)

 5
Author: Brad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-06 05:58:47

Dwie możliwości. Albo użyj kombinowanego klucza:

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

Lub mapa mapy:

Map<Integer, Map<Integer, Integer>> myMap;
 3
Author: Cyrille Ka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-03 22:24:10
 3
Author: Mykhaylo Adamovych,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-20 02:51:51

Utwórz klasę wartości, która będzie reprezentować Twój klucz złożony, na przykład:

class Index2D {
  int first, second;

  // overrides equals and hashCode properly here
}

Dbanie o poprawne nadpisanie equals() i hashCode(). Jeśli wydaje się to dużo pracy, możesz rozważyć kilka gotowych kontenerów generycznych, takich jak Pair dostarczanych między innymi przez Apache commons.

Istnieje również wiele podobnych pytań tutaj, z innymi pomysłami, takimi jak użycie tabeli , chociaż pozwala na różne typy kluczy, co może być przesadą (w wykorzystanie pamięci i złożoność) w Twoim przypadku, ponieważ rozumiem, że Twoje klucze są zarówno liczbami całkowitymi.

 1
Author: BeeOnRope,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:28

Jeśli są to dwie liczby całkowite, możesz spróbować szybkiej i brudnej sztuczki: Map<String, ?> używając klucza jako i+"#"+j.

Jeśli klucz i+"#"+j jest taki sam jak j+"#"+i Spróbuj min(i,j)+"#"+max(i,j).

 1
Author: arutaku,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-24 10:03:28

Możesz utworzyć swój kluczowy obiekt w następujący sposób:

Public class 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;
}

}

Zaletą tego jest to, że zawsze upewni się, że pokrywasz również wszystkie scenariusze równości.

Uwaga: Twoje kluczy1 i kluczy2 powinny być niezmienne. Tylko wtedy będziesz w stanie zbudować stabilny kluczowy obiekt.

 1
Author: Andy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-10 17:29:25

Możemy utworzyć klasę przekazującą więcej niż jeden klucz lub wartość, a obiekt tej klasy może być użyty jako parametr w 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();

        }

    }

    }

 }
 1
Author: Vikas Pal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-25 13:33:49

Użyj Pair jako klucza HashMap. JDK nie ma pary, ale możesz użyć biblioteki stron trzecich, takiej jak http://commons.apache.org/lang lub napisz parę.

 0
Author: MrSmith42,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-03 22:25:10

Możesz również użyć implementacji guava Table.

Tabela przedstawia specjalną mapę, na której można podać dwa klucze w sposób kombinowany, aby odnosić się do pojedynczej wartości. Jest to podobne do tworzenia mapy map.

//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");
 0
Author: slisnychyi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-14 12:56:04