一个javaMap,其中键是已知的,但是值应该稍后计算,因为它们很昂贵

v7pvogib  于 2021-07-12  发布在  Java
关注(0)|答案(2)|浏览(230)

是否存在一个javaMap实现,其中键是已知的,但是值应该只在第一次访问时计算,因为计算值是昂贵的。
下面演示了我希望它如何工作。

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必须保持其类型,所以我不能返回 Map<String, Supplier<String>> . 他回来了 Map 可能返回为只读。
可以通过传入两个 Set<String> 定义键和 Function<String, String> 给定一个键返回它的值。

cunj1qz1

cunj1qz11#

一个解决办法是有一张Map 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

将此更改为通用应该很容易。
我怀疑有一个更好的解决方案存在,但是这对其他人来说可能已经足够好了。

vsdwdz23

vsdwdz232#

你可以这样做。Map有一个键和一个fnc类,其中包含一个函数和函数的参数。

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;

    }
}

第一次通过时,调用函数并计算、存储和返回值。后续调用返回存储值。
请注意,该值将替换计算函数。

相关问题