用Java中映射到单个值的多个键进行映射?

时间:2019-01-11 12:42:45

标签: java dictionary

我需要的是一个Map,它将多个键映射到一个值(在我的情况下是3个键映射到1个值),这样3个键一起唯一地标识一个值。这可以通过嵌套地图轻松完成,例如:

Map<String, Map<Integer, Map<Double, Object>>> map = new HashMap<>();
Object value = map.get("foo").get(3).get(1.23);

但是,我还希望仅使用某些键就可以获取值列表,例如:

List<Object> values1 = map.get("foo");       // All values with "foo" as its String-key
List<Object> values2 = map.get(3).get(1.23); // All values with 3 as its Integer-key AND 1.23 as its Double-key

Java中是否有一个实现此类数据结构的类,如果没有,我该如何自己做呢?

3 个答案:

答案 0 :(得分:0)

标准库中没有满足您需求的接口。我认为您已经有了正确的方法,下面是一个简单的类对其进行扩展:

import static java.util.Collections.emptyMap;
import static java.util.stream.Collectors.toList;

public class ThreeMap<K1, K2, K3, V> {

    Map<K1, Map<K2, Map<K3, V>>> map = new HashMap<>();

    public V get(K1 a, K2 b, K3 c) {
        return map.getOrDefault(a, emptyMap()).getOrDefault(b, emptyMap()).get(c);
    }

    public V put(K1 a, K2 b, K3 c, V v) {
        return map.computeIfAbsent(a, mapProvider()).computeIfAbsent(b, mapProvider()).put(c, v);
    }

    public Collection<V> getAllValues(K1 a) {
        return map.getOrDefault(a, emptyMap()).entrySet().stream().flatMap(e -> e.getValue().values().stream()).collect(toList());
    }

    public Collection<V> getAllValues(K1 a, K2 b) {
        return map.getOrDefault(a, emptyMap()).getOrDefault(b, emptyMap()).entrySet().stream().map(e -> e.getValue()).collect(toList());
    }

    private <K, A, B> Function<K, Map<A, B>> mapProvider() {
        // for convenient use in computeIfAbsent
        return k -> new HashMap<>();
    }
}

答案 1 :(得分:0)

您在此处描述的内容似乎是一个经典的DB表,其中包含4列,其中3列将是关键。因此,我将继续创建具有三种不同类型的自定义密钥。

public class CompositeKey {

    public String stringKey;

    public Integer integerKey;

    public Long longKey;

}

由您来建模不同子键的约束,例如如果所有这些都是必需的。如果您实现equals和hashCode,那么简单的Map<CompositeKey,Object>就可以解决简单的查询。

对于其他查询类型,您将必须实现自己的存储库并决定是否向该查询添加索引,也不能。例如,如果您希望获得具有特定Integer值且没有索引的所有值,则必须进行全面扫描(遍历地图中的所有条目)。如果要复制数据库索引的功能,则必须保留从键的子集到可能值列表的映射。在这种情况下,您将无法直接使用地图,而必须使用存储库实现来执行所有操作(putget等)。

答案 2 :(得分:0)

我将创建一个新类,并将这3个键作为属性,例如

class CompositeKey {
    private String key1;
    private Integer key2;
    private Double key3;


    public CompositeKey(String key1, Integer key2, Double key3) {
        this.key1 = key1;
        this.key2 = key2;
        this.key3 = key3;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((key1 == null) ? 0 : key1.hashCode());
        result = prime * result + ((key2 == null) ? 0 : key2.hashCode());
        result = prime * result + ((key3 == null) ? 0 : key3.hashCode());
        return result;
    }


    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        CompositeKey other = (CompositeKey) obj;
        if (key1 == null) {
            if (other.key1 != null)
                return false;
        } else if (!key1.equals(other.key1))
            return false;
        if (key2 == null) {
            if (other.key2 != null)
                return false;
        } else if (!key2.equals(other.key2))
            return false;
        if (key3 == null) {
            if (other.key3 != null)
                return false;
        } else if (!key3.equals(other.key3))
            return false;
        return true;
    }

    public String getKey1() {
        return key1;
    }

    public Integer getKey2() {
        return key2;
    }

    public Double getKey3() {
        return key3;
    }

}

然后我将为每个键创建3个索引图,如下所示

// 3 index maps
// List<CompositeKey> not just CompositeKey because one key say k1 can be in combination of 2 different set of k2 and k3.
Map<String, List<CompositeKey>> key1Index = new HashMap<>();
Map<Integer, List<CompositeKey>> key2Index = new HashMap<>();
Map<Double, List<CompositeKey>> key3Index = new HashMap<>();

然后是保存实际数据的数据图

Map<CompositeKey, Object> dataMap = new HashMap<>();

现在,假设您要对key1添加key2key3Object。创建CompositeKey对象作为键

CompositeKey compositeKey = new CompositeKey(key1, key2, key3);

// Change value accordingly below
dataMap.put(compositeKey, new Object());

此外,更新索引图,以便以后可以使用它们查找

key1Index.computeIfAbsent(key1, k -> new ArrayList<>()).add(compositeKey);
key2Index.computeIfAbsent(key2, k -> new ArrayList<>()).add(compositeKey);
key3Index.computeIfAbsent(key3, k -> new ArrayList<>()).add(compositeKey);

最后,您现在可以

// Search by single key, say key1
        List<CompositeKey> list = key1Index.get(key1);
        List<Object> result = getResult(list, dataMap);

        // Search by two keys, say key 1 and key 2
        List<CompositeKey> key1IndexResult = key1Index.get(key1);
        List<CompositeKey> key1Key2List = key1IndexResult.stream()
            .filter(ck -> ck.getKey2().equals(key2))
            .collect(Collectors.toList());

        List<Object> key1Key2Result = getResult(key1Key2List, dataMap);

        // Search by all 3 keys
        CompositeKey allKeys = new CompositeKey(key1, key2, key3);
        List<Object> allKeysResult = getResult(Collections.singletonList(allKeys), dataMap);

使用的实用方法:

private List<Object> getResult(List<CompositeKey> list, Map<CompositeKey, Object> dataMap) {
        return list.stream().map(dataMap::get)
        .filter(Objects::nonNull)
        .collect(Collectors.toList());
    }

工作代码在线链接:https://onlinegdb.com/rk2kszLMN