2つのキー(キーペア、値)を持つHas​​hMapを作成する方法は?


118

整数の2D配列があります。それらをHashMapに入れて欲しい。しかし、配列インデックスに基づいてHashMapから要素にアクセスしたいと思います。何かのようなもの:

A [2] [5] map.get(2,5)の場合、そのキーに関連付けられた値を返します。しかし、キーのペアでhashMapを作成するにはどうすればよいですか?または一般的に、複数のキー: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つのキーを他のハッシュマップにマッピングすることです。
Mihai8 2013

1
質問の中の質問には答えないでください。あなたの編集は興味深いので、お気軽に回答として投稿してください。
Ole VV

@Crocodeすごい!編集での答えの背後にある数学は魅力的です。2つの整数iとjに対して一般的に機能するかどうか疑問に思っています。
likejudo

@Crocodeは、Nの倍数の場合、iとjが循環しますか?
likejudo

回答:


190

いくつかのオプションがあります:

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 = //...

しかし、パフォーマンスの観点からはひどく、読みやすさと正確さ(リストのサイズを強制する簡単な方法はありません)。

タプルとcaseクラスがあるScalaを見てください(クラス全体Keyをワンライナーで置き換えてください)。


3
こんにちは。hashCodeを実行するとき、他の人はx 'または2つの値を持っています。なぜ31を使うのですか?32ビット整数と関係があると思いましたが、x = 1とy = 0がまだx = 0とy = 31と同じハッシュコードにマッピングされているため、それを考えても意味がありません
pete

1
@pete Joshua Bloch、Effective Java ch 3. s 9.では、「1。定数の非ゼロ値、たとえば17をresultと呼ばれるint変数に保存する」を推奨しています。プライム。参照:stackoverflow.com/questions/3613102/...
fncomp

2
Wrapperキーオブジェクトの代わりに、なぜMap.Entry<K, V>キーとして使用しないのですか?
Roland

1
どうMap<Pair<Key1, Key2>, Value>ですか?
Joaquin Iurchuk

1
次のhashCode()ように1行で実装することもできますObjects.hash(x,y)
xdavidliu

23

独自のキーペアオブジェクトを作成する場合、いくつかのことに直面するはずです。

まず、との実装に注意する必要がhashCode()ありequals()ます。これを行う必要があります。

次に、を実装hashCode()するときは、それがどのように機能するかを理解していることを確認してください。与えられたユーザーの例

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

実際、あなたができる最悪の実装の1つです。その理由は単純です。あなたは多くの等しいハッシュを持っているからです!そして、hashCode()それはまれである傾向があるint値を返すべきです、それは最高です。このようなものを使用してください:

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

これは高速で、-2 ^ 16から2 ^ 16-1(-65536から65535)のキーの一意のハッシュを返します。これは、ほとんどの場合に適合します。ごくまれに、この範囲を超えています。

第3に、実装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;
  }

}

ディメンションインデックスXYパブリックアクセスレベルは、最終的なものであり、機密情報が含まれていないため、与えることができます。私がいるかどうか100%わからないんだけどprivate、アクセスレベルがで正しく動作する任意のキャスト時にケースObjectKey

ファイナルについて不思議に思っている場合は、インスタンス化時に設定され、変更されない値をファイナルとして宣言します。したがって、オブジェクト定数です。


7

複数のキーを持つハッシュマップを作成することはできませんが、複数のパラメーターをキーとして受け取るオブジェクトを作成することはできます。

x値とy値をとるIndexというオブジェクトを作成します。

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
あなたはオーバーライドする必要があるhashCodeequals
トム・ホーティン-タックライン

4
hashCode実装は(2,1)と(1,2)を区別していませんでした
user1947415

1
それは衝突です。ハッシュコードは、オブジェクトごとに異なる値を保証する必要はありません。@ user1947415
Ajak6

6

共通コレクションMultiKeyMapに実装されています


@Wilson私は、ピアレビューを待って、今リンクを修正
computingfreak

@computingfreakは好意的な見方が出てきたようです。フラー!NBこれは私見の最良の答えです。非常に有用であるが(結局のところ)ありふれた機能の一部について、専門家のApacheエンジニアと何時間もの競争を繰り広げたいと思わない限り。
マイクげっ歯類

4

2つの可能性。組み合わせたキーを使用します。

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

またはマップのマップ:

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

2
ここでパフォーマンスやメモリの使用を気にしない場合(つまり、マップが小さい場合)、または同じ最初のインデックスを持つ多くのキーがある場合にのみマップのマップを使用します-このソリューションはHashMapオブジェクトのオーバーヘッドを支払うことを意味するためすべての一意の最初のインデックス。
BeeOnRope 2013

1
この回答を改善するために、オーバーライドとメソッドについての情報をここに示します。hashCodeequals
Pshemo 2013

2

Pairキーとしてa を使用しますHashMap。JDKにはペアがありませんが、http: //commons.apache.org/langなどのサードパーティのライブラリを使用するか、独自のペアtaypeを作成できます。


1

次のような、複合キーを表す値クラスを作成します。

class Index2D {
  int first, second;

  // overrides equals and hashCode properly here
}

オーバーライドequals()hashCode()正しく行うように注意してください。それが多くの作業のように思われる場合、次のような既製の汎用コンテナを検討することができますPairは、Apache Commonsによって提供さ。

ここにも同様の質問がたくさんあります。たとえば、GuavaのTableの使用など、他のアイデアもありますが、キーにさまざまなタイプを持たせることができます。これは、キーが両方とも整数であることを理解しているため、(メモリの使用と複雑さにおいて)やり過ぎになる可能性があります。


1

それらが2つの整数である場合はMap<String, ?>、キーをとして使用して、すばやく簡単なトリックを試すことができますi+"#"+j

キーi+"#"+jj+"#"+itryと同じ場合min(i,j)+"#"+max(i,j)


2
本当に悪い考え。第一に、それはただ悪いです。第二に、このテクニックは、異なるキーが同じものにマッピングされStringて陽気な結果をもたらす他のタイプにコピーされます。
トムホーティン-タックライン

それはように私には思えるi#j = j#iときi == j使用してmin/maxトリックすることはないだろう。
Matthieu 2014年

1
@Matthieu 5#55#5交換の違いは何ですか?
エンリー2015

@enreyなし。それが私が指摘していたことです。それはあなたがあなたの鍵について持っている知識に本当に依存します。
Matthieu 2015

@Matthieu aha私はあなたが何を意味するかを理解しています。@arutakuが意味したのは、の5#3よう3#5に同じハッシュを使用したいときであり、次にmin / maxを使用し3#5てこの順序で適用すると思います。
エンリー2015

1

これには、グアバテーブルの実装を使用することもできます。

テーブルは、1つの値を参照するために2つのキーを組み合わせて指定できる特別なマップを表しています。これは、マップのマップを作成することに似ています。

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

1

次のようなキーオブジェクトを作成できます。

パブリッククラス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;
}

}

これの利点は次のとおりです。これにより、常にEqualsのすべてのシナリオをカバーできるようになります。

:key1とkey2は不変である必要があります。そうして初めて、安定したキーオブジェクトを構築できるようになります。


1

複数のキーまたは値を渡すクラスを作成し、このクラスのオブジェクトをマップのパラメーターとして使用できます。

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

        }

    }

    }

 }

0

以下のリンクからダウンロードできます。https//github.com/VVS279/DoubleKeyHashMap/blob/master/src/com/virtualMark/doubleKeyHashMap/DoubleKeyHashMap.java

https://github.com/VVS279/DoubleKeyHashMap

二重キーを使用できます:値ハッシュマップ、

   DoubleKeyHashMap<Integer, Integer, String> doubleKeyHashMap1 = new 
   DoubleKeyHashMap<Integer, Integer, String>();

   DoubleKeyHashMap<String, String, String> doubleKeyHashMap2 = new 
   DoubleKeyHashMap<String, String, String>();
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.