如何使用 Java 8 Stream 将 Array 转换为 HashMap [英] How to convert Array to HashMap using Java 8 Stream

查看:44
本文介绍了如何使用 Java 8 Stream 将 Array 转换为 HashMap的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个函数来使用 Java 8 Stream 将数组转换为 Map.

I am writing a function to convert array to Map using Java 8 Stream.

这就是我想要的

public static <K, V> Map<K, V> toMap(Object... entries) {
    // Requirements:
    // entries must be K1, V1, K2, V2, .... ( even length )
    if (entries.length % 2 == 1) {
        throw new IllegalArgumentException("Invalid entries");
    }

    // TODO
    Arrays.stream(entries).????
}

有效用法

Map<String, Integer> map1 = toMap("k1", 1, "k2", 2);

Map<String, String> map2 = toMap("k1", "v1", "k2", "v2", "k3", "v3");

无效用法

Map<String, Integer> map1 = toMap("k1", 1, "k2", 2, "k3");

有什么帮助吗?

谢谢!

推荐答案

你可以用

public static <K, V> Map<K, V> toMap(Object... entries) {
    if(entries.length % 2 == 1)
        throw new IllegalArgumentException("Invalid entries");
    return (Map<K, V>)IntStream.range(0, entries.length/2).map(i -> i*2)
        .collect(HashMap::new, (m,i)->m.put(entries[i], entries[i+1]), Map::putAll);
}

但它会给你一个(有根据的)unchecked警告.您的方法无法承诺为任意对象数组返回正确类型的 Map<K, V> ,更糟糕的是,它不会因异常而失败,而是默默地返回不一致如果传入错误类型的对象,则映射.

but it will give you a (founded) unchecked warning. Your method can’t hold the promise to return a correctly typed Map<K, V> for an array of arbitrary objects and, even worse, it will not fail with an exception, but silently return an inconsistent map if you pass in objects of the wrong type.

一种更清洁、常用的解决方案是

A cleaner, commonly used, solution is

public static <K, V> Map<K, V> toMap(
                               Class<K> keyType, Class<V> valueType, Object... entries) {
    if(entries.length % 2 == 1)
        throw new IllegalArgumentException("Invalid entries");
    return IntStream.range(0, entries.length/2).map(i -> i*2)
        .collect(HashMap::new,
                 (m,i)->m.put(keyType.cast(entries[i]), valueType.cast(entries[i+1])),
                 Map::putAll);
}

这可以在没有警告的情况下编译,因为正确性将在运行时检查.调用代码需要修改:

This can be compiled without a warning, as the correctness will be checked at runtime. The calling code has to be adapted:

Map<String, Integer> map1 = toMap(String.class, Integer.class, "k1", 1, "k2", 2);
Map<String, String> map2 = toMap(
                           String.class, String.class, "k1", "v1", "k2", "v2", "k3", "v3");

除了需要将实际类型指定为类字面量之外,它的缺点是不支持通用键或值类型(因为它们不能表示为 Class)并且仍然无法编译- 时间安全,只有运行时检查.

Besides the need to specify the actual types as class literals, it has the disadvantage of not supporting generic key or value types (as they can’t be expressed as Class) and still having no compile-time safety, only a runtime check.

值得看看Java 9.在那里,您将能够:

It’s worth looking at Java 9. There, you will be able to do:

Map<String, Integer> map1 = Map.of("k1", 1, "k2", 2);
Map<String, String>  map2 = Map.of("k1", "v1", "k2", "v2", "k3", "v3");

这将创建一个未指定类型的不可变映射,而不是一个HashMap,但有趣的一点是API.

This will create an immutable map of an unspecified type, rather than a HashMap, but the interesting point is the API.

有一个方法<K,V>Map.Entry<K,V>entry(K k, V v) 可以结合
<代码><K,V>地图<K,V>ofEntries(Map.Entry... entries) 创建一个可变长度的映射(不过,可变参数仍然限制为 255 个参数).

There is a method <K,V> Map.Entry<K,V> entry(K k, V v) which can be combined with
<K,V> Map<K,V> ofEntries(Map.Entry<? extends K,? extends V>... entries) to create a map of a variable length (varargs are still limited to 255 parameters, though).

你可以实现类似的东西:

You can implement a similar thing:

public static <K,V> Map.Entry<K,V> entry(K k, V v) {
    return new AbstractMap.SimpleImmutableEntry<>(k, v);
}
public static <K,V> Map<K,V> ofEntries(Map.Entry<? extends K,? extends V>... entries) {
    return Arrays.stream(entries)
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}

of 的便捷方法是唯一实现的方法,这可以通过类型安全来完成:作为具有不同数量参数的重载方法,例如

The convenience method(s) of are implemented the only way, this can be done with type safety: as overloaded methods with different numbers of arguments, like

public static <K,V> Map<K,V> of() {
    return new HashMap<>();// or Collections.emptyMap() to create immutable maps
}
static <K,V> Map<K,V> of(K k1, V v1) {
    return ofEntries(entry(k1, v1));
}
static <K,V> Map<K,V> of(K k1, V v1, K k2, V v2) {
    return ofEntries(entry(k1, v1), entry(k2, v2));
}
static <K,V> Map<K,V> of(K k1, V v1, K k2, V v2, K k3, V v3) {
    return ofEntries(entry(k1, v1), entry(k2, v2), entry(k3, v3));
}
static <K,V> Map<K,V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) {
    return ofEntries(entry(k1, v1), entry(k2, v2), entry(k3, v3), entry(k4, v4));
}   
static <K,V> Map<K,V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) {
    return ofEntries(entry(k1, v1), entry(k2, v2), entry(k3, v3), entry(k4, v4));
}   

(Java 9 有十个映射,如果有更多,则必须使用 ofEntries(entry(k1, v1), ...) 变体.

(Java 9 makes the cut at ten mappings, if you have more, you have to use the ofEntries(entry(k1, v1), …) variant).

如果您遵循这种模式,您应该保留您的 toMap 名称或仅使用 map,而不是调用of",如你不是在写 Map 接口.

If you follow this pattern, you should keep your toMap name or use just map, rather than calling at "of", as you are not writing the Map interface.

这些重载可能看起来不是很优雅,但它们解决了所有问题.您可以像在您的问题中一样编写代码,而无需指定 Class 对象,但可以获得编译时类型安全性,甚至拒绝使用奇数个参数调用它的尝试.

These overloads might not look very elegant, but they solve all problems. You can write the code just as in your question, without specifying Class objects, but gain compile-time type safety and even rejection of attempts to call it with an odd number of arguments.

您必须在一定数量的参数上进行切割,但是,如前所述,即使 varargs 也不支持无限参数.而 ofEntries(entry(…), …) 形式对于较大的地图来说还不错.

You have to make a cut at a certain number of parameters, but, as already noted, even varargs do not support unlimited parameters. And the ofEntries(entry(…), …) form isn’t so bad for larger maps.

收集器 Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue) 返回一个未指定的地图类型,它甚至可能是不可变的(尽管它是一个 HashMap在当前版本中).如果您想保证返回 HashMap 实例,则必须使用 Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1,v2)->{throw new IllegalArgumentException("duplicate key");}, HashMap::new) 代替.

The collector Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue) returns an unspecified map type, which might even be immutable (though it’s a HashMapin the current version). If you want to have a guaranty that a HashMap instance is returned, you have to use Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1,v2)->{throw new IllegalArgumentException("duplicate key");}, HashMap::new) instead.

这篇关于如何使用 Java 8 Stream 将 Array 转换为 HashMap的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆