Параллельная хеш-карта удаляет сложные значения - PullRequest
1 голос
/ 24 октября 2019

У меня есть HashMap:

private final ConcurrentHashMap<String, List<Client>> clients;

И класс:

public static class Client {
    private String name; // it is also the key of the map
    private String url;
}

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

@Override
public CompletableFuture<Void> removeClient(Client client) {
    return CompletableFuture.runAsync(() ->
            clients.entrySet().removeIf(v ->
                    v.getValue().removeIf(
                            it -> client.url.equals(it.url))
            )
    );
}

Но, конечно, это не работает. Когда я получил метод сгенерировал исключение java.lang.UnsupportedOperationException, я решил проблему следующим образом:

@Override
public CompletableFuture<Void> removeClient(Client client) {
    return CompletableFuture.runAsync(() -> {
                List<Client> currentClients = new ArrayList<>(clients.get(client.getName()));
                currentClients.remove(client);
                if (currentClients.isEmpty()) {
                    clients.remove(client.getName());
                } else {
                    clients.put(client.getName(), currentClients);
                }
            }
    );
}

Но он не является поточно-ориентированным. Как я могу достичь этого здесь? Может быть, есть более элегантные способы ее решения?

1 Ответ

1 голос
/ 24 октября 2019

Я думаю, что вы могли бы использовать ConcurrentHashMap::computeIfPresent в этом случае, предполагая, что одинаковые List экземпляры не помещаются для одних и тех же ключей:

CompletableFuture.runAsync(() -> {
    clients.computeIfPresent(client.getName(), (name, clients1) -> {
        List<Client> currentClients = new ArrayList<>(clients1);
        currentClients.remove(client);
        return currentClients.isEmpty() ? null : currentClients;
    });
});

Поскольку computeIfPresent являетсявыполняется атомарно, и мы используем копию списка внутри remappingFunction - она ​​должна работать.

Как мы можем прочитать в документации:

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

...