如何使用Java 8 Stream将Array转换为HashMap
我正在编写一个函数,使用Java 8 Stream将数组转换为Map。
这就是我想要的
public static Map 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 map1 = toMap("k1", 1, "k2", 2); Map map2 = toMap("k1", "v1", "k2", "v2", "k3", "v3");
无效的用法
Map map1 = toMap("k1", 1, "k2", 2, "k3");
有帮助吗?
谢谢!
你可以用
public static Map toMap(Object... entries) { if(entries.length % 2 == 1) throw new IllegalArgumentException("Invalid entries"); return (Map)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
的承诺,更糟糕的是,它不会因exception而失败,但如果你传入一个对象,它会默默地返回一个不一致的地图。错误的类型。
一种更清洁,常用的解决方案是
public static Map toMap( Class keyType, Class 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); }
这可以在没有警告的情况下编译,因为将在运行时检查正确性。 调用代码必须进行调整:
Map map1 = toMap(String.class, Integer.class, "k1", 1, "k2", 2); Map map2 = toMap( String.class, String.class, "k1", "v1", "k2", "v2", "k3", "v3");
除了需要将实际类型指定为类文字之外,它的缺点是不支持通用键或值类型(因为它们不能表示为Class
)并且仍然没有编译时安全性,只有运行时检查。
值得一看的是Java 9 。 在那里,你将能够做到:
Map map1 = Map.of("k1", 1, "k2", 2); Map map2 = Map.of("k1", "v1", "k2", "v2", "k3", "v3");
这将创建一个未指定类型的不可变映射,而不是HashMap
,但有趣的是API。
有一种方法
可以与之结合使用
创建一个可变长度的映射(varargs仍然限制为255个参数)。
你可以实现类似的东西:
public static Map.Entry entry(K k, V v) { return new AbstractMap.SimpleImmutableEntry<>(k, v); } public static Map ofEntries(Map.Entry extends K,? extends V>... entries) { return Arrays.stream(entries) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); }
方便实现的方法是唯一的方法,这可以通过类型安全来完成:作为具有不同数量的参数的重载方法,如
public static Map of() { return new HashMap<>();// or Collections.emptyMap() to create immutable maps } static Map of(K k1, V v1) { return ofEntries(entry(k1, v1)); } static Map of(K k1, V v1, K k2, V v2) { return ofEntries(entry(k1, v1), entry(k2, v2)); } static Map 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 Map 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 Map 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), …)
变体)。
如果你遵循这种模式,你应该保留你的toMap
名称或只使用map
,而不是调用“ of
”,因为你没有编写Map
接口。
这些重载可能看起来不是很优雅,但它们可以解决所有问题。 您可以像在问题中一样编写代码,而无需指定Class
对象,但可以获得编译时类型安全性,甚至拒绝尝试使用奇数个参数调用它。
您必须在一定数量的参数上进行切割,但是,如前所述,甚至varargs也不支持无限制的参数。 并且ofEntries(entry(…), …)
forms对于较大的地图来说并不是那么糟糕。
收集器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)
而不是。
获取您想要的内容可能不适用于键类型与其值类型不同的地图。 这是因为Java的变量arity声明( Object... entries
部分)仅支持一种类型。
我想到了一些选择:
-
如果值不匹配,您可以动态执行检查并抛出非法参数exception。 但是你将失去编译器类型检查。
-
您可以定义一个
Pair
类,并使用静态导入进行一些操作以获得您想要的几乎所有内容:
例如:
class Pair { final K k; final V v; Pair( K ak, V av) { k=ak; v=av; } static Pair p(A a, B b) { return new Pair(a,b); } } public class JavaTest8 { Map toMap( Pair... pairs ) { return Arrays.stream(pairs).collect(Collectors.toMap(p->pk, p->pv)); } public static void main(String[] args) { // Usage Map sti = toMap( p("A",1), p("B",2) ); Map itb = toMap( p(1,true), p(42,false) ); } }
以下是JDK 8流的想法:
public static Map toMap(final Object... entries) { // Requirements: // entries must be K1, V1, K2, V2, .... ( even length ) if (entries.length % 2 == 1) { throw new IllegalArgumentException("Invalid entries"); } final Map map = new HashMap<>((int) (entries.length / 2 * 1.25 + 1)); IntStream.range(0, entries.length / 2).forEach(i -> map.put((K) entries[i * 2], (V) entries[i * 2 + 1])); return map; // OR: // return IntStream.range(0, entries.length / 2).boxed().reduce(new HashMap(), (m, i) -> { // m.put((K) entries[i * 2], (V) entries[i * 2 + 1]); // return m; // }, (a, b) -> { // a.putAll(b); // return b; // }); }
如果您不介意使用第三方库AbacusUtil ,可以将代码简化为:
public static Map toMap2(final Object... entries) { // Requirements: // entries must be K1, V1, K2, V2, .... ( even length ) if (entries.length % 2 == 1) { throw new IllegalArgumentException("Invalid entries"); } return Stream.of(entries).split0(2).toMap(e -> (K) e.get(0), e -> (V) e.get(1)); }
我认为最有效的方法是通过for循环,如果你不是特别追求使用Stream API
public static Map toMap3(final Object... entries) { // Requirements: // entries must be K1, V1, K2, V2, .... ( even length ) if (entries.length % 2 == 1) { throw new IllegalArgumentException("Invalid entries"); } final Map map = new HashMap<>((int) (entries.length / 2 * 1.25 + 1)); for (int i = 0, len = entries.length; i < len; i++) { map.put((K) entries[i], (V) entries[++i]); } return map; // OR just call the method in AbacusUtil. // return N.asMap(entries); }
你可以使用像地图文字这样的东西。
为实现此目的,您可以使用工厂方法:
// Creates a map from a list of entries @SafeVarargs public static Map mapOf(Map.Entry... entries) { LinkedHashMap map = new LinkedHashMap<>(); for (Map.Entry entry : entries) { map.put(entry.getKey(), entry.getValue()); } return map; } // Creates a map entry public static Map.Entry entry(K key, V value) { return new AbstractMap.SimpleEntry<>(key, value); }
最后,您可以执行以下操作:
public static void main(String[] args) { Map map = mapOf(entry("a", 1), entry("b", 2), entry("c", 3)); System.out.println(map); }
输出:
{a = 1,b = 2,c = 3}
我希望这能给你正确的方法。
- 如果密钥以与equals不一致的方式实现Comparable,那么Java 8的HashMap是否会出错?这是一个错误吗?
- 为什么这个HashMap.get返回null?
- 如何从arrayList对象中创建所有可能的幂集(或子集)?
- Java同步HashMap中的size(),put(),remove(),get()是否为primefaces?
- HashMap与LinkedHashMap在值迭代中的性能()
- 使用HashMap计算实例
- 只需一步即可在HashMap中声明和放置String数组
- Java中可排序的类似HashMap的数据结构?
- Android使用包含另一个hashmap的hashmap实现Parcelable对象