広告

Java : ConcurrentMap (並列処理用マップ) - API使用例

ConcurrentMap (Java SE 20 & JDK 20) の使用例まとめです。
だいたいのメソッドを網羅済みです。
API仕様のおともにどうぞ。


概要

スレッドの安全性と原子性の保証を提供するMapです。

クラス構成

ConcurrentMap は、並列処理用 Map のベースとなるインタフェースです。
主な実装には ConcurrentHashMapConcurrentSkipListMap があります。

void test(Map<String, Integer> map) throws InterruptedException {
    map.put("a", 10);
    map.put("b", 20);
    map.put("c", 30);
    map.put("d", 40);
    map.put("e", 50);

    try (final var executor = Executors.newSingleThreadScheduledExecutor()) {

        executor.schedule(() -> {
            System.out.println("-- put entry! --");
            map.put("X", 999);
        }, 5, TimeUnit.SECONDS);

        try {
            for (final var entry : map.entrySet()) {
                System.out.println("entry = " + entry);
                TimeUnit.SECONDS.sleep(2);
            }
        } catch (ConcurrentModificationException e) {
            System.out.println("ConcurrentModificationException!");
        }
    }

    System.out.println("-- end --");
    System.out.println("map = " + map);
}
test(new HashMap<>());

// 結果
// ↓
//entry = a=10
//entry = b=20
//entry = c=30
//-- put entry! --
//ConcurrentModificationException!
//-- end --
//map = {a=10, b=20, c=30, d=40, e=50, X=999}

final ConcurrentMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
test(concurrentMap);

// 結果
// ↓
//entry = a=10
//entry = b=20
//entry = c=30
//-- put entry! --
//entry = d=40
//entry = e=50
//entry = X=999
//-- end --
//map = {a=10, b=20, c=30, d=40, e=50, X=999}

メソッド

default V compute (K key, BiFunction<? super K,? super V,? extends V> remappingFunction)

指定されたキーと現在マップされている値に対するマッピングの計算を試みます(現在のマッピングが存在しない場合はnull)。

final ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);
System.out.println(map); // {a=1, b=2, c=3}

final var func = new BiFunction<String, Integer, Integer>() {
    @Override
    public Integer apply(String key, Integer value) {
        if (value == null) {
            return -100;
        } else {
            return value * 10;
        }
    }
};

final var ret1 = map.compute("a", func);
System.out.println(ret1); // 10
System.out.println(map); // {a=10, b=2, c=3}

final var ret2 = map.compute("b", func);
System.out.println(ret2); // 20
System.out.println(map); // {a=10, b=20, c=3}

final var ret3 = map.compute("c", func);
System.out.println(ret3); // 30
System.out.println(map); // {a=10, b=20, c=30}

final var ret4 = map.compute("X", func);
System.out.println(ret4); // -100
System.out.println(map); // {a=10, b=20, c=30, X=-100}
final ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);
System.out.println(map); // {a=1, b=2, c=3}

final var ret = map.compute("b", (key, value) -> null);
System.out.println(ret); // null
System.out.println(map); // {a=1, c=3}

default V computeIfAbsent (K key, Function<? super K,? extends V> mappingFunction)

指定されたキーがまだ値に関連付けられていない(またはnullにマップされている)場合、指定されたマッピング関数を使用してその値の計算を試行し、nullでない場合はそれをこのマップに入力します。

final ConcurrentMap<String, String> map = new ConcurrentHashMap<>();
map.put("a", "aaa");
map.put("b", "bbb");
System.out.println(map); // {a=aaa, b=bbb}

final var func = new Function<String, String>() {
    @Override
    public String apply(String key) {
        if ("c".equals(key)) {
            return "ccc";
        } else {
            return null;
        }
    }
};

final var ret1 = map.computeIfAbsent("a", func);
System.out.println(ret1); // aaa
System.out.println(map); // {a=aaa, b=bbb}

final var ret2 = map.computeIfAbsent("b", func);
System.out.println(ret2); // bbb
System.out.println(map); // {a=aaa, b=bbb}

final var ret3 = map.computeIfAbsent("c", func);
System.out.println(ret3); // ccc
System.out.println(map); // {a=aaa, b=bbb, c=ccc}

final var ret4 = map.computeIfAbsent("d", func);
System.out.println(ret4); // null
System.out.println(map); // {a=aaa, b=bbb, c=ccc}
// valueにListを持たせる例です。
final ConcurrentMap<String, List<String>> map = new ConcurrentHashMap<>();
System.out.println(map); // {}

// 最初のキー追加のときにArrayListを作成するようにします。
map.computeIfAbsent("a", (key) -> new ArrayList<>()).add("a-1");
System.out.println(map); // {a=[a-1]}

map.computeIfAbsent("a", (key) -> new ArrayList<>()).add("a-2");
System.out.println(map); // {a=[a-1, a-2]}

map.computeIfAbsent("b", (key) -> new ArrayList<>()).add("b-1");
System.out.println(map); // {a=[a-1, a-2], b=[b-1]}

default V computeIfPresent (K key, BiFunction<? super K,? super V,? extends V> remappingFunction)

指定されたキーの値が存在していてnull以外の場合、キーと現在マップされている値から新しいマッピングの計算を試みます。

final ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);
System.out.println(map); // {a=1, b=2, c=3}

final var func = new BiFunction<String, Integer, Integer>() {
    @Override
    public Integer apply(String key, Integer value) {
        return value * 10;
    }
};

final var ret1 = map.computeIfPresent("a", func);
System.out.println(ret1); // 10
System.out.println(map); // {a=10, b=2, c=3}

final var ret2 = map.computeIfPresent("b", func);
System.out.println(ret2); // 20
System.out.println(map); // {a=10, b=20, c=3}

final var ret3 = map.computeIfPresent("c", func);
System.out.println(ret3); // 30
System.out.println(map); // {a=10, b=20, c=30}

final var ret4 = map.computeIfPresent("X", func);
System.out.println(ret4); // null
System.out.println(map); // {a=10, b=20, c=30}
final ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);
System.out.println(map); // {a=1, b=2, c=3}

final var ret = map.computeIfPresent("b", (k, v) -> null);
System.out.println(ret); // null
System.out.println(map); // {a=1, c=3}

default void forEach (BiConsumer<? super K,? super V> action)

このマップのすべてのエントリの処理が完了するかアクションから例外がスローされるまで、各エントリに対して指定されたアクションを実行します。

final ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);
System.out.println(map); // {a=1, b=2, c=3}

System.out.println("-- forEach --");
map.forEach((key, value) -> {
    System.out.println("key = %s : value = %d".formatted(key, value));
});

// 結果
// ↓
//-- forEach --
//key = a : value = 1
//key = b : value = 2
//key = c : value = 3

default V getOrDefault (Object key, V defaultValue)

指定されたキーがマップされている値を返します。このマップにそのキーのマッピングが含まれていない場合はdefaultValueを返します。

final ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);
System.out.println(map); // {a=1, b=2, c=3}

System.out.println(map.getOrDefault("a", 10)); // 1
System.out.println(map.getOrDefault("b", 20)); // 2
System.out.println(map.getOrDefault("c", 30)); // 3
System.out.println(map.getOrDefault("X", 40)); // 40
System.out.println(map.getOrDefault("", 50)); // 50

default V merge (K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction)

指定されたキーがまだ値と関連付けられていないかnullと関連付けられている場合、指定されたnull以外の値に関連付けます。

final ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);
System.out.println(map); // {a=1, b=2, c=3}

final var func = new BiFunction<Integer, Integer, Integer>() {
    @Override
    public Integer apply(Integer oldValue, Integer newValue) {
        return oldValue + newValue;
    }
};

final var ret1 = map.merge("a", 1000, func);
System.out.println(ret1); // 1001
System.out.println(map); // {a=1001, b=2, c=3}

final var ret2 = map.merge("b", 1000, func);
System.out.println(ret2); // 1002
System.out.println(map); // {a=1001, b=1002, c=3}

final var ret3 = map.merge("c", 1000, func);
System.out.println(ret3); // 1003
System.out.println(map); // {a=1001, b=1002, c=1003}

final var ret4 = map.merge("d", 1000, func);
System.out.println(ret4); // 1000
System.out.println(map); // {a=1001, b=1002, c=1003, d=1000}

final var ret5 = map.merge("b", 1000, (k, v) -> null);
System.out.println(ret5); // null
System.out.println(map); // {a=1001, c=1003, d=1000}

V putIfAbsent (K key, V value)

指定されたキーがまだ値と関連付けられていない場合は、指定された値に関連付けます。

final ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>();
System.out.println(map); // {}

System.out.println(map.putIfAbsent("a", 1)); // null
System.out.println(map); // {a=1}

System.out.println(map.putIfAbsent("b", 2)); // null
System.out.println(map); // {a=1, b=2}

System.out.println(map.putIfAbsent("c", 3)); // null
System.out.println(map); // {a=1, b=2, c=3}

// 上書きされません。
System.out.println(map.putIfAbsent("a", 999)); // 1
System.out.println(map); // {a=1, b=2, c=3}

boolean remove (Object key, Object value)

指定された値にキーが現在マッピングされている場合にのみ、そのキーのエントリを削除します。

final ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);
System.out.println(map); // {a=1, b=2, c=3}

System.out.println(map.remove("a", 1)); // true
System.out.println(map); // {b=2, c=3}

System.out.println(map.remove("b", 1)); // false
System.out.println(map); // {b=2, c=3}

System.out.println(map.remove("b", 2)); // true
System.out.println(map); // {c=3}

System.out.println(map.remove("C", 3)); // false
System.out.println(map); // {c=3}

System.out.println(map.remove("d", 4)); // false
System.out.println(map); // {c=3}

V replace (K key, V value)

キーが値に現在マッピングされている場合にのみ、そのキーのエントリを置換します。

final ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);
System.out.println(map); // {a=1, b=2, c=3}

System.out.println(map.replace("a", 10)); // 1
System.out.println(map); // {a=10, b=2, c=3}

System.out.println(map.replace("b", 20)); // 2
System.out.println(map); // {a=10, b=20, c=3}

System.out.println(map.replace("z", 99)); // null
System.out.println(map); // {a=10, b=20, c=3}

boolean replace (K key, V oldValue, V newValue)

指定された値にキーが現在マッピングされている場合にのみ、そのキーのエントリを置換します。

final ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);
System.out.println(map); // {a=1, b=2, c=3}

System.out.println(map.replace("a", 1, 10)); // true
System.out.println(map); // {a=10, b=2, c=3}

System.out.println(map.replace("b", 999, 20)); // false
System.out.println(map); // {a=10, b=2, c=3}

System.out.println(map.replace("b", 2, 20)); // true
System.out.println(map); // {a=10, b=20, c=3}

System.out.println(map.replace("z", 3, 999)); // false
System.out.println(map); // {a=10, b=20, c=3}

default void replaceAll (BiFunction<? super K,? super V,? extends V> function)

すべてのエントリが処理されるか、または関数が例外をスローするまで、各エントリの値を、そのエントリで指定された関数を呼び出した結果で置換します。

final var src = Map.of("a", 1, "b", 2, "c", 3);

{
    final ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>(src);
    System.out.println(map); // {a=1, b=2, c=3}

    map.replaceAll((k, v) -> v * 10);
    System.out.println(map); // {a=10, b=20, c=30}
}
{
    final ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>(src);
    System.out.println(map); // {a=1, b=2, c=3}

    map.replaceAll((k, v) -> {
        if (k.equals("a")) {
            return v * 1000;
        } else {
            return v * 10;
        }
    });
    System.out.println(map); // {a=1000, b=20, c=30}
}
{
    final ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>(src);
    System.out.println(map); // {a=1, b=2, c=3}

    map.replaceAll((k, v) -> {
        if (v == 2) {
            return v * 1000;
        } else {
            return v * 10;
        }
    });
    System.out.println(map); // {a=10, b=2000, c=30}
}

Mapで宣言されたメソッド

clear, containsKey, containsValue, entrySet, equals, get, hashCode, isEmpty, keySet, put, putAll, remove, size, values

Java API 使用例 : Map」をご参照ください。


関連記事

ページの先頭へ