java 复制带有附加值或新值的ImmutableMap

ddhy6vgd  于 2023-04-28  发布在  Java
关注(0)|答案(5)|浏览(159)

就像ImmutableList可以这样扩展:

ImmutableList<Long> originalList = ImmutableList.of(1, 2, 3);
ImmutableList<Long> extendedList = Iterables.concat(originalList, ImmutableList.of(4, 5));

如果我有一个现有的Map,我该如何扩展它(或者用替换的值创建一个新的副本)?

ImmutableMap<String, Long> oldPrices = ImmutableMap.of("banana", 4, "apple", 7);
ImmutableMap<String, Long> newPrices = … // Increase apple prices, leave others.
                                         //  => { "banana": 4, "apple": 9 }

(Let我们并不寻求一个有效的解决方案,因为显然that doesn't exist by design。这个问题寻求的是最惯用的解决办法。)

yhived7q

yhived7q1#

您可以显式地创建一个构建器:

ImmutableMap<String, Long> oldPrices = ImmutableMap.of("banana", 4, "apple", 7);
ImmutableMap<String, Long> newPrices =
    new ImmutableMap.Builder()
    .putAll(oldPrices)
    .put("orange", 9)
    .build();

编辑:
正如注解中所指出的,这将不允许覆盖现有值。这可以通过遍历不同Map(e.例如,HashMap)。它一点也不优雅,但它应该起作用:

ImmutableMap<String, Long> oldPrices = ImmutableMap.of("banana", 4, "apple", 7);
ImmutableMap<String, Long> newPrices =
    new ImmutableMap.Builder()
    .putAll(new HashMap<>() {{
        putAll(oldPrices);
        put("orange", 9); // new value
        put("apple", 12); // override an old value
     }})
    .build();
dhxwm5r4

dhxwm5r42#

只需将ImmutableMap复制到新的HashMap中,添加项,然后转换为新的ImmutableMap

ImmutableMap<String, Long> oldPrices = ImmutableMap.of("banana", 4, "apple", 7);
Map<String, Long> copy = new HashMap<>(oldPrices);
copy.put("orange", 9); // add a new entry
copy.put("apple", 12); // replace the value of an existing entry

ImmutableMap<String, Long> newPrices = ImmutableMap.copyOf(copy);
ahy6op9u

ahy6op9u3#

继续使用Guava,您可以创建一个实用程序方法,在构建新Map时跳过重复项。这是在Maps.filterKeys()的帮助下完成的。
这就是效用函数:

private <T, U> ImmutableMap<T, U> extendMap(ImmutableMap<T, U> original, ImmutableMap<T, U> changes) {
    return ImmutableMap.<T, U>builder()
            .putAll(changes)
            .putAll(Maps.filterKeys(original, key -> !changes.containsKey(key)))
            .build();
}

下面是一个使用数据的单元测试(基于AssertJ)。

@Test
public void extendMap() {
    ImmutableMap<String, Integer> oldPrices = ImmutableMap.of("banana", 4, "apple", 7);
    ImmutableMap<String, Integer> changes = ImmutableMap.of("orange", 9, "apple", 12);
    ImmutableMap<String, Integer> newPrices = extendMap(oldPrices, changes);
    assertThat(newPrices).contains(
            entry("banana", 4),
            entry("apple", 12),
            entry("orange", 9));

}

更新:这里有一个更优雅的基于Maps.difference()的实用函数的替代方案。

private <T, U> ImmutableMap<T, U> extendMap(ImmutableMap<T, U> original, ImmutableMap<T, U> changes) {
    return ImmutableMap.<T, U>builder()
            .putAll(Maps.difference(original, changes).entriesOnlyOnLeft())
            .putAll(changes)
            .build();
}
l5tcr1uw

l5tcr1uw4#

我已经用streams做过了,但它并不完美:

public static <K,V> Map<K,V> update(final Map<K,V> map, final Map.Entry<K,V> replace)
{
    return Stream.concat(
        Stream.of(replace),
        map.entrySet().stream()
            .filter(kv -> ! replace.getKey().equals(kv.getKey()))
    .collect(Collectors.toMap(SimpleImmutableEntry::getKey, SimpleImmutableEntry::getValue));
}

并且这仅插入或更新单个条目。请注意,可以放入ImmutableMap &关联的收集器(这是我实际使用的)

rsl1atfo

rsl1atfo5#

不是一个可怕的性能代码,但下面的工作

private <K, V> ImmutableMap.Builder<K, V> update(final ImmutableMap.Builder<K, V> builder, final List<ImmutablePair<K, V>> replace) {
    Set<K> keys = replace.stream().map(entry -> entry.getKey()).collect(toSet());
    Map<K, V> map = new HashMap<>();
    builder.build().forEach((key, val) -> {
        if (!keys.contains(key)) {
            map.put(key, val);
        }
    });
    ImmutableMap.Builder<K, V> newBuilder = ImmutableMap.builder();
    newBuilder.putAll(map);
    replace.stream().forEach(kvEntry -> newBuilder.put(kvEntry.getKey(), kvEntry.getValue()));
    return newBuilder;
}

相关问题