使用附加值或新值扩展 ImmutableMap
Extending an ImmutableMap with additional or new values
很像 ImmutableList
可以这样扩展:
ImmutableList<Long> originalList = ImmutableList.of(1, 2, 3);
ImmutableList<Long> extendedList = Iterables.concat(originalList, ImmutableList.of(4, 5));
如果我有一个现有地图,我该如何扩展它(或创建一个具有替换值的新副本)?
ImmutableMap<String, Long> oldPrices = ImmutableMap.of("banana", 4, "apple", 7);
ImmutableMap<String, Long> newPrices = … // Increase apple prices, leave others.
// => { "banana": 4, "apple": 9 }
(我们不要寻求一个有效的解决方案,显然 that doesn't exist by design。这个问题而是寻求最惯用的解决方案。)
您可以显式创建构建器:
ImmutableMap<String, Long> oldPrices = ImmutableMap.of("banana", 4, "apple", 7);
ImmutableMap<String, Long> newPrices =
new ImmutableMap.Builder()
.putAll(oldPrices)
.put("orange", 9)
.build();
编辑:
如评论中所述,这将不允许覆盖现有值。这可以通过遍历不同 Map
(例如 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();
只需将 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);
好吧,我已经用流完成了这个,但它并不完美:
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 & 关联的收集器可以放入(这是我实际使用的)
不是一个非常高效的代码,但下面的代码可以工作
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;
}
继续使用 Guava,您可以创建一个在构建新地图时跳过重复项的实用方法。这是在 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();
}
很像 ImmutableList
可以这样扩展:
ImmutableList<Long> originalList = ImmutableList.of(1, 2, 3);
ImmutableList<Long> extendedList = Iterables.concat(originalList, ImmutableList.of(4, 5));
如果我有一个现有地图,我该如何扩展它(或创建一个具有替换值的新副本)?
ImmutableMap<String, Long> oldPrices = ImmutableMap.of("banana", 4, "apple", 7);
ImmutableMap<String, Long> newPrices = … // Increase apple prices, leave others.
// => { "banana": 4, "apple": 9 }
(我们不要寻求一个有效的解决方案,显然 that doesn't exist by design。这个问题而是寻求最惯用的解决方案。)
您可以显式创建构建器:
ImmutableMap<String, Long> oldPrices = ImmutableMap.of("banana", 4, "apple", 7);
ImmutableMap<String, Long> newPrices =
new ImmutableMap.Builder()
.putAll(oldPrices)
.put("orange", 9)
.build();
编辑:
如评论中所述,这将不允许覆盖现有值。这可以通过遍历不同 Map
(例如 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();
只需将 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);
好吧,我已经用流完成了这个,但它并不完美:
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 & 关联的收集器可以放入(这是我实际使用的)
不是一个非常高效的代码,但下面的代码可以工作
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;
}
继续使用 Guava,您可以创建一个在构建新地图时跳过重复项的实用方法。这是在 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();
}