Java Language
地図
サーチ…
前書き
java.util.Mapインタフェースは、キーとその値の間のマッピングを表します。マップに重複キーを含めることはできません。各キーは最大で1つの値にマップできます。
Map
はインターフェイスなので、インターフェイスを使用するためには、そのインターフェイスの具体的な実装をインスタンス化する必要があります。いくつかのMap
実装があり、主にjava.util.HashMap
とjava.util.TreeMap
が使用されます
備考
マップは、 キーごとに関連する値を持つキーを格納するオブジェクトです。キーとその値は、 キー/値のペアまたはエントリと呼ばれることもあります 。通常、マップには次の機能があります。
- データはキー/値のペアでマップに格納されます。
- マップには、特定のキーのエントリが1つしか含まれていない場合があります。マップに特定のキーを持つエントリが含まれていて、同じキーを持つ2番目のエントリを格納しようとすると、2番目のエントリが最初のエントリに置き換わります。つまり、これはキーに関連付けられた値を変更します。
- マップは、キーがマップに存在するかどうかをテストし、キーに関連付けられた値をフェッチし、キーと値のペアを削除するための高速操作を提供します。
最も一般的に使用されるマップ実装はHashMapです。文字列や数字のキーでうまく動作します。
HashMapなどのプレーンマップは順序付けされていません。キーと値のペアを反復することで、個々のエントリを任意の順序で返すことができます。制御された方法でマップエントリを反復処理する必要がある場合は、次を参照する必要があります。
TreeMapのようなソートされたマップは、自然順序でキーを反復します(または、 Comparatorを指定することで指定できる順序で)。たとえば、数字をキーとしてソートされたマップは、数値順にその項目を反復することが期待されます。
LinkedHashMapは、マップに挿入された順序と同じ順序で、または最近アクセスされた順序でエントリを反復することを許可します。
要素を追加する
- 添加
Map<Integer, String> map = new HashMap<>();
map.put(1, "First element.");
System.out.println(map.get(1));
出力: First element.
- オーバーライド
Map<Integer, String> map = new HashMap<>();
map.put(1, "First element.");
map.put(1, "New element.");
System.out.println(map.get(1));
出力: New element.
HashMap
例として使用します。 Map
インタフェースを実装する他の実装も同様に使用できます。
複数の項目を追加する
V put(K key,V value)
を使うことができます:
このマップ内の指定されたキーに指定された値を関連付けます(オプション)。以前マップにキーのマッピングが含まれていた場合、古い値は指定された値に置き換えられます。
String currentVal;
Map<Integer, String> map = new TreeMap<>();
currentVal = map.put(1, "First element.");
System.out.println(currentVal);// Will print null
currentVal = map.put(2, "Second element.");
System.out.println(currentVal); // Will print null yet again
currentVal = map.put(2, "This will replace 'Second element'");
System.out.println(currentVal); // will print Second element.
System.out.println(map.size()); // Will print 2 as key having
// value 2 was replaced.
Map<Integer, String> map2 = new HashMap<>();
map2.put(2, "Element 2");
map2.put(3, "Element 3");
map.putAll(map2);
System.out.println(map.size());
出力:
3
多くの項目を追加するには、次のような内部クラスを使用できます。
Map<Integer, String> map = new HashMap<>() {{
// This is now an anonymous inner class with an unnamed instance constructor
put(5, "high");
put(4, "low");
put(1, "too slow");
}};
匿名の内部クラスの作成は必ずしも効率的ではなく、メモリリークを招く可能性があるので、可能であれば、代わりにイニシャライザブロックを使用してください。
static Map<Integer, String> map = new HashMap<>();
static {
// Now no inner classes are created so we can avoid memory leaks
put(5, "high");
put(4, "low");
put(1, "too slow");
}
上記の例は、マップを静的にします。また、静的でないコンテキストでも、すべての静的なものを取り除くことで使用できstatic
。
これに加えて、ほとんどの実装でputAll
サポートされています。これは、あるマップのすべてのエントリを次のように別のマップに追加できます。
another.putAll(one);
Java 8からのマップのデフォルトメソッドの使用
マップインタフェースのJava 8で導入されたデフォルトメソッドの使用例
- getOrDefaultの使用
キーにマップされた値を返します。キーが存在しない場合は、デフォルト値を返します。
Map<Integer, String> map = new HashMap<>();
map.put(1, "First element");
map.get(1); // => First element
map.get(2); // => null
map.getOrDefault(2, "Default element"); // => Default element
- forEachを使用する
各マップエントリで 'アクション'で指定された操作を実行できます
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
map.forEach((key, value) -> System.out.println("Key: "+key+ " :: Value: "+value));
// Key: 1 :: Value: one
// Key: 2 :: Value: two
// Key: 3 :: Value: three
- replaceAllを使用する
キーがある場合のみ新しい値で置き換えられます
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("john", 20);
map.put("paul", 30);
map.put("peter", 40);
map.replaceAll((key,value)->value+10); //{john=30, paul=40, peter=50}
- putIfAbsentの使用
キーが存在しないか、またはnullにマップされている場合は、キーと値のペアがマップに追加されます
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("john", 20);
map.put("paul", 30);
map.put("peter", 40);
map.putIfAbsent("kelly", 50); //{john=20, paul=30, peter=40, kelly=50}
削除の使用
指定された値に関連付けられている場合にのみキーを削除します。
Map<String, Integer> map = new HashMap<String, Integer>(); map.put("john", 20); map.put("paul", 30); map.put("peter", 40); map.remove("peter",40); //{john=30, paul=40}
置き換えを使用する
キーが存在する場合、値は新しい値に置き換えられます。キーが存在しない場合、何もしません。
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("john", 20);
map.put("paul", 30);
map.put("peter", 40);
map.replace("peter",50); //{john=20, paul=30, peter=50}
map.replace("jack",60); //{john=20, paul=30, peter=50}
- computeIfAbsentの使用
このメソッドは、マップにエントリを追加します。キーは関数で指定され、値はマッピング関数の適用結果です
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("john", 20);
map.put("paul", 30);
map.put("peter", 40);
map.computeIfAbsent("kelly", k->map.get("john")+10); //{john=20, paul=30, peter=40, kelly=30}
map.computeIfAbsent("peter", k->map.get("john")+10); //{john=20, paul=30, peter=40, kelly=30} //peter already present
- computeIfPresentを使う
このメソッドは、エントリを追加するか、マップ内の既存のエントリを変更します。そのキーを持つエントリが存在しない場合は何もしません
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("john", 20);
map.put("paul", 30);
map.put("peter", 40);
map.computeIfPresent("kelly", (k,v)->v+10); //{john=20, paul=30, peter=40} //kelly not present
map.computeIfPresent("peter", (k,v)->v+10); //{john=20, paul=30, peter=50} // peter present, so increase the value
- 計算を使う
このメソッドは、キーの値を新しく計算された値で置き換えます
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("john", 20);
map.put("paul", 30);
map.put("peter", 40);
map.compute("peter", (k,v)->v+50); //{john=20, paul=30, peter=90} //Increase the value
- マージの使用
キーが存在しない場合はキーを値に置き換えます。キーが存在する場合は、新しく計算された値に置き換えます。キーがマップから削除されます。新しい値がnullの場合はマップに追加されます。
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("john", 20);
map.put("paul", 30);
map.put("peter", 40);
//Adds the key-value pair to the map, if key is not present or value for the key is null
map.merge("kelly", 50 , (k,v)->map.get("john")+10); // {john=20, paul=30, peter=40, kelly=50}
//Replaces the value with the newly computed value, if the key is present
map.merge("peter", 50 , (k,v)->map.get("john")+10); //{john=20, paul=30, peter=30, kelly=50}
//Key is removed from the map , if new value computed is null
map.merge("peter", 30 , (k,v)->map.get("nancy")); //{john=20, paul=30, kelly=50}
地図をクリアする
Map<Integer, String> map = new HashMap<>();
map.put(1, "First element.");
map.put(2, "Second element.");
map.put(3, "Third element.");
map.clear();
System.out.println(map.size()); // => 0
マップの内容を反復する
マップでは、マップのキー、値、またはキーと値のペアにコレクションとしてアクセスできるメソッドが用意されています。これらのコレクションを反復することができます。たとえば、次のマップを考えます。
Map<String, Integer> repMap = new HashMap<>();
repMap.put("Jon Skeet", 927_654);
repMap.put("BalusC", 708_826);
repMap.put("Darin Dimitrov", 715_567);
マップキーを反復する:
for (String key : repMap.keySet()) {
System.out.println(key);
}
印刷物:
ダーリン・ディミトロフ
ジョン・スケート
BalusC
keySet()
はマップのキーをSet
として提供します。キーは重複する値を含むことができないため、 Set
が使用されます。セットを反復すると、順番に各キーが生成されます。 HashMapsは順序付けされていないので、この例ではキーは任意の順序で返されます。
マップ値を反復する:
for (Integer value : repMap.values()) {
System.out.println(value);
}
印刷物:
715567
927654
708826
values()
は、マップの値をCollection
として返します。コレクションを反復すると、順番に各値が返されます。ここでも、値は任意の順序で返されます。
キーと値を一緒に繰り返す
for (Map.Entry<String, Integer> entry : repMap.entrySet()) {
System.out.printf("%s = %d\n", entry.getKey(), entry.getValue());
}
印刷物:
ダーリン・ディミトロフ= 715567
ジョン・スケート= 927654
BalusC = 708826
entrySet()
は、 Map.Entry
オブジェクトのコレクションを返します。 Map.Entryは各エントリのキーと値にアクセスします。
マップのマージ、結合、合成
putAll
を使用して、あるマップのすべてのメンバーを別のマップに配置します。マップにすでに存在するキーには、対応する値が上書きされます。
Map<String, Integer> numbers = new HashMap<>();
numbers.put("One", 1)
numbers.put("Three", 3)
Map<String, Integer> other_numbers = new HashMap<>();
other_numbers.put("Two", 2)
other_numbers.put("Three", 4)
numbers.putAll(other_numbers)
これにより、次のようなマッピングがnumbers
られnumbers
。
"One" -> 1
"Two" -> 2
"Three" -> 4 //old value 3 was overwritten by new value 4
値を上書きするのではなく、値を結合する場合は、Java 8で追加されたMap.merge
を使用できます。これは、ユーザー提供のBiFunction
を使用して重複キーの値をマージします。 merge
は個々のキーと値で動作するため、ループまたはMap.forEach
を使用する必要があります。ここでは、重複するキーの文字列を連結します。
for (Map.Entry<String, Integer> e : other_numbers.entrySet())
numbers.merge(e.getKey(), e.getValue(), Integer::sum);
//or instead of the above loop
other_numbers.forEach((k, v) -> numbers.merge(k, v, Integer::sum));
制約を強制する場合は、キーが重複しないようにするには、 AssertionError
をスローするマージ関数を使用します。
mapA.forEach((k, v) ->
mapB.merge(k, v, (v1, v2) ->
{throw new AssertionError("duplicate values for key: "+k);}));
Map <X、Y>とMap <Y、Z>を作成してMap <X、Z>を取得します。
2つのマッピングを作成する場合は、次のように行うことができます
Map<String, Integer> map1 = new HashMap<String, Integer>();
map1.put("key1", 1);
map1.put("key2", 2);
map1.put("key3", 3);
Map<Integer, Double> map2 = new HashMap<Integer, Double>();
map2.put(1, 1.0);
map2.put(2, 2.0);
map2.put(3, 3.0);
Map<String, Double> map3 = new new HashMap<String, Double>();
map1.forEach((key,value)->map3.put(key,map2.get(value)));
これにより、以下のマッピング
"key1" -> 1.0
"key2" -> 2.0
"key3" -> 3.0
キーが存在するかどうかを確認する
Map<String, String> num = new HashMap<>();
num.put("one", "first");
if (num.containsKey("one")) {
System.out.println(num.get("one")); // => first
}
マップにはNULL値を含めることができます
マップの場合、「キーを含む」と「値を持つ」を混同しないように注意しなければなりません。たとえば、 HashMap
はnullが含まれている可能性があります。これは、次のことが完全に正常な動作であることを意味します。
Map<String, String> map = new HashMap<>();
map.put("one", null);
if (map.containsKey("one")) {
System.out.println("This prints !"); // This line is reached
}
if (map.get("one") != null) {
System.out.println("This is never reached !"); // This line is never reached
}
より正式には、 map.contains(key) <=> map.get(key)!=null
という保証はありませんmap.contains(key) <=> map.get(key)!=null
効率的な地図エントリの反復
このセクションでは、 Map<Integer, Integer>
のエントリを繰り返し処理し、 Integer
値の合計を生成する10個の固有の実装例のコードとベンチマークを提供します。すべての例はアルゴリズムの複雑さがΘ(n)
ですが、実際の環境でどの実装がより効率的であるかについての洞察を提供するのに役立ちます。
Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, Integer> pair = it.next();
sum += pair.getKey() + pair.getValue();
}
for (Map.Entry<Integer, Integer> pair : map.entrySet()) {
sum += pair.getKey() + pair.getValue();
}
-
Map.forEach
(Java 8+)を使用した実装
map.forEach((k, v) -> sum[0] += k + v);
- の
for
Map.keySet
を使った実装
for (Integer key : map.keySet()) {
sum += key + map.get(key);
}
-
Iterator
を使用したMap.keySet
を使用した実装
Iterator<Integer> it = map.keySet().iterator();
while (it.hasNext()) {
Integer key = it.next();
sum += key + map.get(key);
}
for (Iterator<Map.Entry<Integer, Integer>> entries =
map.entrySet().iterator(); entries.hasNext(); ) {
Map.Entry<Integer, Integer> entry = entries.next();
sum += entry.getKey() + entry.getValue();
}
-
Stream.forEach
(Java 8以上)を使用した実装
map.entrySet().stream().forEach(e -> sum += e.getKey() + e.getValue());
-
Stream.forEach
とStream.parallel
(Java 8+)を使用した実装
map.entrySet()
.stream()
.parallel()
.forEach(e -> sum += e.getKey() + e.getValue());
- Apacheコレクションからの
IterableMap
を使用した実装
MapIterator<Integer, Integer> mit = iterableMap.mapIterator();
while (mit.hasNext()) {
sum += mit.next() + it.getValue();
}
- Eclipseコレクションから
MutableMap
を使用した実装
mutableMap.forEachKeyValue((key, value) -> {
sum += key + value;
});
パフォーマンステスト ( Githubで利用可能なコード )
テスト環境:Windows 8.1 64ビット、Intel i7-4790 3.60GHz、16GB
10回の試行(100要素)の平均性能最高:308±21ns / op
Benchmark Score Error Units test3_UsingForEachAndJava8 308 ± 21 ns/op test10_UsingEclipseMutableMap 309 ± 9 ns/op test1_UsingWhileAndMapEntry 380 ± 14 ns/op test6_UsingForAndIterator 387 ± 16 ns/op test2_UsingForEachAndMapEntry 391 ± 23 ns/op test7_UsingJava8StreamAPI 510 ± 14 ns/op test9_UsingApacheIterableMap 524 ± 8 ns/op test4_UsingKeySetAndForEach 816 ± 26 ns/op test5_UsingKeySetAndIterator 863 ± 25 ns/op test8_UsingJava8StreamAPIParallel 5552 ± 185 ns/op
10回の試行(10000要素)の平均性能最高:37.606±0.790μs/ op
Benchmark Score Error Units test10_UsingEclipseMutableMap 37606 ± 790 ns/op test3_UsingForEachAndJava8 50368 ± 887 ns/op test6_UsingForAndIterator 50332 ± 507 ns/op test2_UsingForEachAndMapEntry 51406 ± 1032 ns/op test1_UsingWhileAndMapEntry 52538 ± 2431 ns/op test7_UsingJava8StreamAPI 54464 ± 712 ns/op test4_UsingKeySetAndForEach 79016 ± 25345 ns/op test5_UsingKeySetAndIterator 91105 ± 10220 ns/op test8_UsingJava8StreamAPIParallel 112511 ± 365 ns/op test9_UsingApacheIterableMap 125714 ± 1935 ns/op
10回の試行(100000要素)の平均性能最高:1184.767±332.968μs/ op
Benchmark Score Error Units test1_UsingWhileAndMapEntry 1184.767 ± 332.968 μs/op test10_UsingEclipseMutableMap 1191.735 ± 304.273 μs/op test2_UsingForEachAndMapEntry 1205.815 ± 366.043 μs/op test6_UsingForAndIterator 1206.873 ± 367.272 μs/op test8_UsingJava8StreamAPIParallel 1485.895 ± 233.143 μs/op test5_UsingKeySetAndIterator 1540.281 ± 357.497 μs/op test4_UsingKeySetAndForEach 1593.342 ± 294.417 μs/op test3_UsingForEachAndJava8 1666.296 ± 126.443 μs/op test7_UsingJava8StreamAPI 1706.676 ± 436.867 μs/op test9_UsingApacheIterableMap 3289.866 ± 1445.564 μs/op
マップサイズに対する性能変動の比較
x: Size of Map
f(x): Benchmark Score (μs/op)
100 600 1100 1600 2100
---------------------------------------------------
10 | 0.333 1.631 2.752 5.937 8.024
3 | 0.309 1.971 4.147 8.147 10.473
6 | 0.372 2.190 4.470 8.322 10.531
1 | 0.405 2.237 4.616 8.645 10.707
Tests 2 | 0.376 2.267 4.809 8.403 10.910
f(x) 7 | 0.473 2.448 5.668 9.790 12.125
9 | 0.565 2.830 5.952 13.22 16.965
4 | 0.808 5.012 8.813 13.939 17.407
5 | 0.81 5.104 8.533 14.064 17.422
8 | 5.173 12.499 17.351 24.671 30.403
カスタムオブジェクトをキーとして使用する
独自のオブジェクトをキーとして使用する前に、オブジェクトのhashCode()およびequals()メソッドをオーバーライドする必要があります。
単純な場合、あなたは次のようなものを持っています:
class MyKey {
private String name;
MyKey(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof MyKey) {
return this.name.equals(((MyKey)obj).name);
}
return false;
}
@Override
public int hashCode() {
return this.name.hashCode();
}
}
hashCode
は、キーが属するハッシュバケットを決定し、 equals
はそのハッシュバケット内のオブジェクトを決定します。
これらのメソッドがなければ、オブジェクトの参照は上記の比較に使用され、毎回同じオブジェクト参照を使用しなければ機能しません。
HashMapの使用法
HashMapは、Key-Valueペアにデータを格納するデータ構造を提供するMapインタフェースの実装です。
1. HashMapの宣言
Map<KeyType, ValueType> myMap = new HashMap<KeyType, ValueType>();
KeyTypeとValueTypeは、String、Integer、Float、Employee、StudentなどのカスタムクラスのようなJavaの有効な型でなければなりません。
例: Map<String,Integer> myMap = new HashMap<String,Integer>();
2.値をHashMapに入れます。
HashMapに値を入れるには、KeyとValueをパラメータとして渡してHashMapオブジェクトにput
メソッドを呼び出さなければなりません。
myMap.put("key1", 1);
myMap.put("key2", 2);
Mapにすでに存在するKeyを使用してputメソッドを呼び出すと、メソッドはその値をオーバーライドして古い値を返します。
3. HashMapから値を取得する。
HashMapから値をget
するには、Keyをパラメータとして渡すことによってget
メソッドを呼び出す必要があります。
myMap.get("key1"); //return 1 (class Integer)
HashMapに存在しないキーを渡すと、このメソッドはnull
を返しnull
4.キーがマップ内にあるかどうかを確認します。
myMap.containsKey(varKey);
5.値が地図にあるかどうかを確認します。
myMap.containsValue(varValue);
上記のメソッドは、key、valueがMapに存在するかどうかにかかわらず真または偽のboolean
値を返します。
マップの作成と初期化
前書き
Maps
はキーと値のペアが格納されます。各キーには関連する値があります。特定のキーが与えられた場合、マップは関連付けられた値を非常に迅速にルックアップできます。
アソシエート配列とも呼ばれるMaps
は、キーと値の形式でデータを格納するオブジェクトです。 Javaでは、マップはコレクションインターフェイスの拡張ではないMapインターフェイスを使用して表されます。
方法1: -
/*J2SE < 5.0*/ Map map = new HashMap(); map.put("name", "A"); map.put("address", "Malviya-Nagar"); map.put("city", "Jaipur"); System.out.println(map);
方法2: -
/*J2SE 5.0+ style (use of generics):*/ Map<String, Object> map = new HashMap<>(); map.put("name", "A"); map.put("address", "Malviya-Nagar"); map.put("city", "Jaipur"); System.out.println(map);
方法3: -
Map<String, Object> map = new HashMap<String, Object>(){{ put("name", "A"); put("address", "Malviya-Nagar"); put("city", "Jaipur"); }}; System.out.println(map);
方法4: -
Map<String, Object> map = new TreeMap<String, Object>(); map.put("name", "A"); map.put("address", "Malviya-Nagar"); map.put("city", "Jaipur"); System.out.println(map);
方法5: -
//Java 8 final Map<String, String> map = Arrays.stream(new String[][] { { "name", "A" }, { "address", "Malviya-Nagar" }, { "city", "jaipur" }, }).collect(Collectors.toMap(m -> m[0], m -> m[1])); System.out.println(map);
方法6: -
//This way for initial a map in outside the function final static Map<String, String> map; static { map = new HashMap<String, String>(); map.put("a", "b"); map.put("c", "d"); }
Way 7: - 不変の単一キー値マップの作成。
//Immutable single key-value map Map<String, String> singletonMap = Collections.singletonMap("key", "value");
この地図を変更することは不可能であることに注意してください。
マップを変更しようとすると、UnsupportedOperationExceptionがスローされます。
//Immutable single key-value pair Map<String, String> singletonMap = Collections.singletonMap("key", "value"); singletonMap.put("newKey", "newValue"); //will throw UnsupportedOperationException singletonMap.putAll(new HashMap<>()); //will throw UnsupportedOperationException singletonMap.remove("key"); //will throw UnsupportedOperationException singletonMap.replace("key", "value", "newValue"); //will throw UnsupportedOperationException //and etc