如何使用lambda初始化映射



我想在一条语句中声明一个完全填充的map字段(可能包含几个嵌套语句),如下所示:

private static final Map<Integer,Boolean> map = 
    something-returning-an-unmodifiable-fully-populated-HashMap;

匿名初始化器不行,因为调用返回新填充的map的函数也不行:它们需要两个顶级语句:一个用于变量声明,另一个用于方法或初始化器。

双花括号({{}})习惯用法可以工作,但它创建了一个扩展HashMap<>的全新类,我不喜欢这样表示的开销。

Java 8的lambdas是否提供了一种更好的方法来实现这一点?

如果您想在单个语句中初始化Map,您可以使用Collectors.toMap

假设您想构建一个Map<Integer, Boolean>,将一个整数映射到调用某个函数f的结果:

private static final Map<Integer,Boolean> MAP = 
        Collections.unmodifiableMap(IntStream.range(0, 1000)
                                             .boxed()
                                             .collect(Collectors.toMap(i -> i, i -> f(i))));
private static final boolean f(int i) {
    return Math.random() * 100 > i;
}

如果你想用"静态"已知值初始化它,就像你回答中的例子一样,你可以像这样滥用流API:

private static final Map<Integer, Boolean> MAP = 
       Stream.of(new Object[] { 1, false }, new Object[] { 2, true })
             .collect(Collectors.toMap(s -> (int) s[0], s -> (boolean) s[1]));

注意,这是一个真正的滥用,我个人永远不会使用它:如果你想用已知的静态值构造一个Map,使用Streams没有任何好处,你最好使用静态初始化器。

Google Guava的Maps类为此提供了一些很好的实用程序方法。此外,还有ImmutableMap类及其静态方法。看一下:

    ImmutableMap.of(key1, value1, key2, value2, ...);
    Maps.uniqueIndex(values, keyExtractor);
    Maps.toMap(keys, valueMapper);

如果您真的想在单个语句中初始化映射,您可以编写自定义构建器并在项目中使用它。像这样:

public class MapBuilder<K, V> {
    private final Map<K, V> map;
    private MapBuilder(Map<K, V> map) {
        this.map = map;
    }
    public MapBuilder<K, V> put(K key, V value) {
        if(map == null)
            throw new IllegalStateException();
        map.put(key, value);
        return this;
    }
    public MapBuilder<K, V> put(Map<? extends K, ? extends V> other) {
        if(map == null)
            throw new IllegalStateException();
        map.putAll(other);
        return this;
    }
    public Map<K, V> build() {
        Map<K, V> m = map;
        map = null;
        return Collections.unmodifiableMap(m);
    }
    public static <K, V> MapBuilder<K, V> unordered() {
        return new MapBuilder<>(new HashMap<>());
    }
    public static <K, V> MapBuilder<K, V> ordered() {
        return new MapBuilder<>(new LinkedHashMap<>());
    }
    public static <K extends Comparable<K>, V> MapBuilder<K, V> sorted() {
        return new MapBuilder<>(new TreeMap<>());
    }
    public static <K, V> MapBuilder<K, V> sorted(Comparator<K> comparator) {
        return new MapBuilder<>(new TreeMap<>(comparator));
    }
}

使用例子:

Map<Integer, Boolean> map = MapBuilder.<Integer, Boolean>unordered()
    .put(1, true).put(2, false).build();

这也适用于Java-7。

作为旁注,我们可能会在Java-9中看到类似Map.of(1, true, 2, false)的东西。详情见JDK-8048330

下面是如何在Java 8中使用lambda在单个语句中实现字段初始化器。

private static final Map<Integer,Boolean> map =
        ((Supplier<Map<Integer,Boolean>>)() -> {
            Map<Integer,Boolean> mutableMap = new HashMap<>();
            mutableMap.put( 1, false );
            mutableMap.put( 2, true );
            return Collections.unmodifiableMap( mutableMap );
        }).get();

Java 9解决方案:

private static final Map<Integer,Boolean> map = Map.of( 1, false, 2, true );

,如果你有超过10个条目,Map.of()将不起作用,所以你需要这个:

private static final Map<Integer,Boolean> map = Map.ofEntries( 
        Map.entry( 1, false ), 
        Map.entry( 2, true ), 
        Map.entry( 3, false ), 
        Map.entry( 4, true ), 
        Map.entry( 5, false ), 
        Map.entry( 6, true ), 
        Map.entry( 7, false ), 
        Map.entry( 8, true ), 
        Map.entry( 9, false ), 
        Map.entry( 10, true ), 
        Map.entry( 11, false ) );

最新更新