3

使用 java 8 流,我想将列表转换为映射,如Java 8 List<V> into Map<K, V>解决方案中所述。但是,我想过滤以删除具有某些键的条目(例如,如果键为空)而不将值转换为键两次。

例如,我可以在收集之前进行过滤,例如

Map<String, Choice> result =
    choices.stream().filter((choice) -> choice.getName() != null).collect(Collectors.toMap(Choice::getName,
                                 Function.<Choice>identity());

在我的情况下,获取密钥的逻辑比简单地获取字段属性更复杂,我想避免先在过滤器中执行逻辑,然后再在 Collectors.toMap 的 keyMapper 函数中执行逻辑

如何使用自定义 keyMapper 函数将列表转换为地图并根据新键过滤某些值?

4

3 回答 3

9

如果只想计算一次key,可以使用stream方法map将流转换为元组流,根据key过滤元组,最后从元组创建map:

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()));
于 2014-05-09T20:44:02.520 回答
2

这是您想要的自定义收集器:

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;
    }
}

并使用它:

Map<String, Choice> result = choices.stream()
    .collect(new FilteredKeyCollector<>(
                Choice::getName,    // key mapper
                c -> c,             // value mapper
                k -> k != null));   // key filter
于 2014-05-10T06:24:59.493 回答
1

如果您接受分两步进行操作,您可以先收集地图,然后删除不需要的密钥:

Map<String, Choice> result = choices.stream()
    .collect(Collectors.toMap(c -> c.getName(), c -> c);
result.keySet().removeIf(k -> k == null);
于 2014-05-10T11:18:07.907 回答