如何创建其get()调用返回可选包装值的自定义地图?

时间:2019-05-22 07:11:13

标签: java hashmap

我需要一个可以包含空值并可以返回Optional包装值的映射。

  • 我知道HashMap可以有空值,但不会使调用者明显知道该值可以为空。
  • 最简单的选择是使用Map<K, Optional<V>>,但对于数据类型使用Optional是不理想的。 (有关仅将Optional用于返回的Use of Optional in a map的相关SO帖子)
  • 我无法很好地扩展HashMap类,因为它的entrySet()方法仍然面临着相同的空值问题,因此我编写了包装HashMap并包含{{1}的类}和返回get()包装值的自定义Entry.getValue()。 (仍在弄清楚如何为此类编写Optional版本)
  • 现在的问题是,使用Collectors.toMap不能轻松地“替换”该类(不能编程为接口),并且在我的业务逻辑中流传着一个定制程度不多的映射,对此我有点不满意。
Map

期望:public class CustomMap<K, V> { private final Map<K, V> map; public CustomMap() { map = new HashMap<>(); } public Optional<V> get(@NonNull final K k) { // Lombok.NonNull return Optional.ofNullable(map.get(k)); } public void put(@NonNull final K k, @Nullable final V v) { map.put(k, v); } public Set<Entry<K, V>> entrySet() { return map.entrySet().stream() .map(e -> new Entry(e.getKey(), e.getValue())) .collect(toImmutableSet()); } public Set<K> keySet() { return map.keySet(); } public static class Entry<K, V> { private final K k; private final V v; public Entry(K k, V v) { this.k = k; this.v = v; } public K getKey() { return k; } public Optional<V> getValue() { return Optional.ofNullable(v); } } } 应该返回customMap.get(K)包装的对象,并且Optional不需要接受customMap.put(K, V)作为输入。 Optional<V>应该能够充当CustomMap

应该有一种更简洁,可扩展的方法来实现这一目标,我觉得我缺少明显的东西。有什么建议吗?

1 个答案:

答案 0 :(得分:1)

如果我对您的理解正确,那么您想使Optionals变得显而易见,并且您不愿意推出一些与Map实现不同的自定义map实现;这与您认为Optional数据类型不理想的说法相冲突。

假设您只是不想在地图中存储Optional实例,则可能的解决方案将是Map实现,该实现将(内部)Map<K,V>封装为空值作为Map<K, Optional<V>>,例如

import java.util.AbstractMap;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

public class OptionalMap<K, V> implements Map<K, Optional<V>> {
    private final Map<K, V> map;

    public OptionalMap(Map<K, V> map) {
        this.map = map;
    }

    @Override
    public void clear() {
        map.clear();
    }

    @Override
    public boolean containsKey(Object key) {
        return map.containsKey(key);
    }

    @Override
    public boolean containsValue(Object value) {
        if (!(value instanceof Optional)) {
            return false;
        }

        return map.containsValue(((Optional<?>)value).get());
    }

    @Override
    public Set<Entry<K, Optional<V>>> entrySet() {
        return
                map.entrySet().stream()
                .map(e -> new AbstractMap.SimpleEntry<K, Optional<V>>(e.getKey(), Optional.ofNullable(e.getValue())))
                .collect(Collectors.collectingAndThen(Collectors.toSet(), Collections::unmodifiableSet));
    }

    @Override
    public Optional<V> get(Object key) {
        return Optional.ofNullable(map.get(key));
    }

    @Override
    public boolean isEmpty() {
        return map.isEmpty();
    }

    @Override
    public Set<K> keySet() {
        return map.keySet();
    }

    @Override
    public Optional<V> put(K key, Optional<V> value) {
        final Optional<V> previous = Optional.ofNullable(map.get(key));
        map.put(key, value.orElse(null));
        return previous;
    }

    @Override
    public void putAll(Map<? extends K, ? extends Optional<V>> other) {
        Map<K, V> unwrappedMap =
            other.entrySet().stream()
            .<Entry<K, V>>map(entry -> new AbstractMap.SimpleEntry<K, V>(entry.getKey(), entry.getValue().orElse(null)))
            .collect(Collectors.toMap(Entry::getKey, Entry::getValue));

        map.putAll(unwrappedMap);
    }

    @Override
    public Optional<V> remove(Object key) {
        return Optional.ofNullable(map.remove(key));
    }

    @Override
    public int size() {
        return map.size();
    }

    @Override
    public Collection<Optional<V>> values() {
        return
                map.values().stream()
                .map(e -> Optional.ofNullable(e))
                .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
    }
}