Карта java, в которой известны ключи, однако значения следует вычислять позже, поскольку они дороги. - PullRequest
1 голос
/ 14 февраля 2020

Существует ли реализация карты java там, где известны ключи, однако значения должны вычисляться только при первом доступе, так как вычисление значений стоит дорого.

Ниже показано, как я хотел бы, чтобы работа.

someMap.keySet(); // Returns all keys but no values are computed.
someMap.get(key); // Returns the value for key computing it if needed.

Причина этого в том, что у меня есть кое-что, что содержит кучу данных, и этот объект возвращает данные как Map<String, String>, что вычислительно тяжело для вычисления, потому что вычисление значений дорого, однако ключи дешевы для вычисления.

Карта должна сохранять свой тип, поэтому я не могу вернуть Map<String, Supplier<String>>. Возвращенное значение Map может быть возвращено только для чтения.

Саму карту можно создать, передав ей Set<String>, определяющий ключи, и Function<String, String>, для которого данный ключ возвращает его значение.

Ответы [ 2 ]

0 голосов
/ 14 февраля 2020

Вы могли бы сделать что-то вроде этого. Карта имеет ключ и класс Fn c, который содержит функцию и аргумент функции.

import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

public class MapDemo {

    public static Map<String, Object> mymap = new HashMap<>();
    public static void main(String[] args) {
        MapDemo thisClass = new MapDemo();

    // populate the functions   
        mymap.put("v1", new Fnc<String>(String::toUpperCase));

        mymap.put("10Fact", new Fnc<Integer>((Integer a) -> {
            int f = 1;
            int k = a;
            while (k-- > 1) {
                f *= k;
            }
            return f + "";
        }));


        mymap.put("expensive", 
                new Fnc<Integer>(thisClass::expensiveFunction));

        // access them - first time does the computation
        System.out.println(getValue("expensive", 1000));
        System.out.println(getValue("10Fact", 10));
        System.out.println(getValue("v1", "hello"));

        // second time does not.
        System.out.println(getValue("expensive"));
        System.out.println(getValue("10Fact"));
        System.out.println(getValue("v1"));

    }



    public String expensiveFunction(int q) {
        return q * 100 + ""; // example
    }

    static class Fnc<T> {
        Function<T, String> fnc;
        public Fnc(Function<T,String> fnc) {
            this.fnc = fnc;
        }

    }


    public <T> void addFunction(String key,
            Function<String, T> fnc) {
        mymap.put(key, fnc);
    }

    public static String getValue(String key) {
        Object ret = mymap.get(key);
        if (ret instanceof Fnc) {
            return null;
        }
        return (String)mymap.get(key);
    }

    public static <T> String getValue(String key, T arg) {
        Object ret = mymap.get(key);

        if (ret instanceof Fnc) {
            System.out.println("Calculating...");
            ret = ((Fnc)ret).fnc.apply(arg);
            mymap.put(key, ret);
        }
        return (String) ret;

    }
}

При первом вызове функции вызывается, а значение вычисляется, сохраняется и возвращается , Последующие вызовы возвращают сохраненное значение.

Обратите внимание, что значение заменяет вычислительную функцию.

0 голосов
/ 14 февраля 2020

Одним из решений может быть карта, которая принимает Set ключей и Function, для которой задан ключ, можно вычислить значение.

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;

/**
 * Create a Map where we already know the keys but computing the values is expensive and so is delayed as 
 * much as possible. 
 *
 */
@AllArgsConstructor
public class MapWithValuesProvidedByFunction implements Map<String, String> {

    /**
     * All keys that are defined.
     */
    private Set<String> keys;

    /**
     * A function which maps a key to its value.
     */
    private Function<String, String> mappingFunction;

    /**
     * Holds all keys and values we have already computed.
     */
    private final Map<String, String> computedValues = new HashMap<>();

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

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

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

    @Override
    public boolean containsValue(Object value) {
        if(computedValues.size() == keys.size()) return computedValues.containsValue(value);
        for(String k : keys) {
            String v = get(k);
            if(v == value) return true;
            if(v != null && v.equals(value)) return true;
        }
        return false;
    }

    @Override
    public String get(Object key) {
        if(keys.contains(key)) {
            return computedValues.computeIfAbsent(key.toString(), mappingFunction);
        }
        return null;
    }

    @Override
    public String put(String key, String value) {
        throw new UnsupportedOperationException("Not modifiable");
    }

    @Override
    public String remove(Object key) {
        throw new UnsupportedOperationException("Not modifiable");
    }

    @Override
    public void putAll(Map<? extends String, ? extends String> m) {
        throw new UnsupportedOperationException("Not modifiable");
    }

    @Override
    public void clear() {
        throw new UnsupportedOperationException("Not modifiable");
    }

    @Override
    public Set<String> keySet() {
        return Collections.unmodifiableSet(keys);
    }

    @Override
    public Collection<String> values() {
        return keys.stream().map(this::get).collect(Collectors.toList());
    }

    @Override
    public Set<java.util.Map.Entry<String, String>> entrySet() {
        Set<Entry<String, String>> set = new HashSet<>();
        for(String s : keys) {
            set.add(new MyEntry(s, this::get));
        }
        return set;
    }

    @AllArgsConstructor
    @EqualsAndHashCode
    public class MyEntry implements Entry<String, String> {
        private String key;
        private Function<String, String> valueSupplier;

        @Override
        public String getKey() {
            return key;
        }

        @Override
        public String getValue() {
            return valueSupplier.apply(key);
        }

        @Override
        public String setValue(String value) {
            throw new UnsupportedOperationException("Not modifiable");
        }
    }

}

Примером такого использования может быть :

Map<String, String> map = new MapWithValuesProvidedByFunction(
    Set.of("Seelenvirtuose", "Mark Rotteveel", "Shiny"), // The known keys
    k -> "Some people close questions when they can't answer it."); // The function to make the values

Изменение этого значения на общее c должно быть достаточно легким.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...