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

查看:118
本文介绍了如何使用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);
}

但它会给你一个(已创建的)未选中警告。您的方法无法承诺为任意对象数组返回正确键入的 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>条目(K k,V v)可以与

< K,V>组合地图< K,V> ofEntries(Map.Entry<?extends K,?extends V> ... entries)创建一个可变长度的地图(但varargs仍然限制为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));
}

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

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减少10个映射,如果你有更多,你必须使用 ofEntries(条目(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 ,而不是在 ,因为你没有写 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天全站免登陆