【问题标题】:How to create custom-map whose get() call returns Optional wrapped value?如何创建其 get() 调用返回可选包装值的自定义映射?
【发布时间】:2019-05-22 07:11:13
【问题描述】:

我需要一个可以保存空值并且可以返回可选包装值的映射。

  • 我知道HashMap 可以有空值,但它不会让调用者明白该值可以为空。
  • 最简单的替代方法是使用Map<K, Optional<V>>,但使用Optional 作为数据类型并不理想。 (有关使用 Optional 仅用于返回的相关 SO 帖子:Use of Optional in a map
  • 我无法很好地扩展 HashMap 类,因为它的 entrySet() 方法仍然面临同样的空值问题,所以我编写了包装 HashMap 并具有 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&lt;V&gt; 作为输入。 CustomMap 应该可以充当Map

应该有更清洁和可扩展的方式来实现这一点,我觉得我错过了一些明显的东西。有什么建议吗?

【问题讨论】:

  • 您可以在某些 MapUtils 类中将该行为定义为静态方法: public static Optional optionalGet(Map elements, K key) { return Optional. ofNullable(elements.get(key)); }
  • 它不会成为Map&lt;K,V&gt; 的直接替代品,因为该接口表示您必须从get 返回V,而不是Optional&lt;V&gt;。因此,所有get 调用者都可以使用的静态辅助方法是最好的方法。

标签: java hashmap


【解决方案1】:

如果我对您的理解正确,您希望使 Optionals 的使用显而易见,并且您不愿意推出一些偏离 Map 实现的自定义地图实现;这与您关于 Optional 数据类型不理想的说法相冲突。

假设您只是不想将 Optional 实例存储在地图中,一种可能的解决方案是使用 Map 实现,将带有空值的(内部)Map&lt;K,V&gt; 包装为 Map&lt;K, Optional&lt;V&gt;&gt;,例如

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

【讨论】:

  • “这与您关于 Optional 数据类型不理想的说法相冲突。” -> 澄清了问题中的Optional;我希望它作为返回数据类型。感谢您花时间实现整个Map 接口。我真的不希望你代表我实施它。
  • Map&lt;K, Optional&lt;V&gt;&gt; map = new OptionalMap&lt;K, V&gt;(); 自定义映射需要像这样声明,这会破坏多态性。在我的问题中增加了更清晰的期望。再次感谢您的回答。我认为我已经达到了 Java 的限制,即覆盖 get() 行为以返回 Optional 对象,这在 Java 中可能是不可能的。
  • 另外,OptionalMap.put(K, Optional&lt;V&gt;) 需要 Optional 包裹 V 这不是理想的 Optional 用法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-27
  • 1970-01-01
  • 2021-09-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多