Java 将列表转换为映射并筛选空键
使用Java8流,我想将列表转换为一个映射,如解决方案中所述。但是,我希望筛选以删除具有特定键的条目(例如,如果键为null),而无需将值转换为键两次 例如,我可以在收集之前进行过滤,例如Java 将列表转换为映射并筛选空键,java,java-8,java-stream,collectors,Java,Java 8,Java Stream,Collectors,使用Java8流,我想将列表转换为一个映射,如解决方案中所述。但是,我希望筛选以删除具有特定键的条目(例如,如果键为null),而无需将值转换为键两次 例如,我可以在收集之前进行过滤,例如 Map<String, Choice> result = choices.stream().filter((choice) -> choice.getName() != null).collect(Collectors.toMap(Choice::getName,
Map<String, Choice> result =
choices.stream().filter((choice) -> choice.getName() != null).collect(Collectors.toMap(Choice::getName,
Function.<Choice>identity());
映射结果=
choices.stream().filter((choice)->choice.getName()!=null).collect(Collectors.toMap(choice::getName,
Function.identity());
在我的例子中,获取密钥的逻辑比简单地获取字段属性更复杂,我希望避免先在过滤器中执行逻辑,然后在Collectors.toMap的keyMapper函数中执行逻辑
如何使用自定义键映射器函数将列表转换为映射,并根据新键过滤某些值?如果只想计算一次键,可以使用流方法映射将流转换为元组流,根据键过滤元组,最后从元组创建映射:
Map<String, Choice> result = choices.stream()
.map(c -> new AbstractMap.SimpleEntry<String, Choice>(c.getName(), c))
.filter(e -> e.getKey() != null)
.collect(toMap(e -> e.getKey(), e -> e.getValue()));
Map result=choices.stream()
.map(c->newAbstractMap.SimpleEntry(c.getName(),c))
.filter(e->e.getKey()!=null)
.collect(toMap(e->e.getKey(),e->e.getValue());
这里有一个定制的收集器,可以满足您的需求:
public class FilteredKeyCollector<T, K, V> implements Collector<T, Map<K, V>, Map<K, V>> {
private final Function<? super T,? extends K> keyMapper;
private final Function<? super T,? extends V> valueMapper;
private final Predicate<K> keyFilter;
private final EnumSet<Collector.Characteristics> characteristics;
private FilteredKeyCollector(Function<? super T,? extends K> keyMapper, Function<? super T,? extends V> valueMapper, Predicate<K> keyFilter) {
this.keyMapper = keyMapper;
this.valueMapper = valueMapper;
this.keyFilter = keyFilter;
this.characteristics = EnumSet.of(Collector.Characteristics.IDENTITY_FINISH);
}
@Override
public Supplier<Map<K, V>> supplier() {
return HashMap<K, V>::new;
}
@Override
public BiConsumer<Map<K, V>, T> accumulator() {
return (map, t) -> {
K key = keyMapper.apply(t);
if (keyFilter.test(key)) {
map.put(key, valueMapper.apply(t));
}
};
}
@Override
public BinaryOperator<Map<K, V>> combiner() {
return (map1, map2) -> {
map1.putAll(map2);
return map1;
};
}
@Override
public Function<Map<K, V>, Map<K, V>> finisher() {
return m -> m;
}
@Override
public Set<Collector.Characteristics> characteristics() {
return characteristics;
}
}
公共类FilteredKeyCollector实现收集器{
private final Function如果您接受分两步执行,您可以先收集地图,然后删除不需要的密钥:
Map result=choices.stream()
.collect(Collectors.toMap(c->c.getName(),c->c);
result.keySet().removeIf(k->k==null);
Ischoice.getName()
实际上这是一个昂贵的操作?如果不是,那么任何喜欢的人最终可能都会比这个简单的解决方案慢。在目前的情况下,这并不特别昂贵,但如果有其他选择,例如编写自定义收集器或使用不同的收集器,我希望避免不得不做两次stream方法比collect方法(如map)更适合首先将每个项目转换为map.Entry。不过,如果可能的话,我希望不必创建自定义的“中间”对对象。在我看来,似乎没有任何简单的选项不涉及中间对,而且创建中间对的成本比只创建中间对要高调用一个简单的getter.Map.Entry不是抽象的吗?@CarrKnight:你是对的,我用AbstractMap.SimpleEntry
替换了Map.Entry
。我理解最终的收集器。toMap()
这在这个星座中是必要的,但我忍不住想,一定有可能重写并简化这个表达式,以便将其分解。这可能是因为我的直觉告诉我这里有开销。但也许没有?
Map<String, Choice> result = choices.stream()
.collect(new FilteredKeyCollector<>(
Choice::getName, // key mapper
c -> c, // value mapper
k -> k != null)); // key filter