Как создать пользовательскую карту, чей вызов get () возвращает необязательное упакованное значение? - PullRequest
1 голос
/ 22 мая 2019

Мне нужна карта, которая может содержать нулевые значения и может возвращать необязательное упакованное значение.

  • Я знаю, HashMap может иметь нулевые значения, но для вызывающей стороны не будет очевидно, что это значение можетбыть нулевым.
  • Самой простой альтернативой является использование Map<K, Optional<V>>, но использование Optional для типа данных не является идеальным.(Соответствующий пост SO об использовании Optional только для возврата: Использование Optional на карте )
  • Я не смог очень хорошо расширить класс HashMap с его методом entrySet() до сих порстолкнувшись с той же проблемой нулевого значения, поэтому я написал свой класс, который включает HashMap и имеет get() и пользовательский Entry.getValue(), который возвращает Optional упакованное значение.(до сих пор выясняется, как написать версию Collectors.toMap для этого класса)
  • Проблема теперь в том, что класс не так просто "заменить" на Map (не программирование для интерфейса) и пользовательский-нет-much-extensible-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 должен быть в состоянии действовать как Map.

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

1 Ответ

1 голос
/ 22 мая 2019

Если я вас правильно понимаю, вы хотите сделать использование Optionals очевидным и неохотно внедрять какую-либо реализацию пользовательской карты, которая отличается от реализации Map;это противоречит вашему утверждению о том, что необязательный тип данных не идеален.

Предполагая, что вы просто не хотите хранить необязательные экземпляры на карте, возможным решением будет реализация 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));
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...