【问题标题】:Filter HashMap based on multiple 'optional' conditions根据多个“可选”条件过滤 HashMap
【发布时间】:2021-03-05 01:50:20
【问题描述】:

我有一个需要在函数中过滤的 HashMap:

public void filter(filterOption1, filterOption2, filterOption3, filterOption4) {
    //Filter map in here
    ...
} 

过滤器选项filterOption1,filterOption2,filterOption3,filterOption4 在运行时可能为空,而我希望避免的是:

public void filter(filterOption1, filterOption2, filterOption3, filterOption4) {
    if(filterOption1 != null && filterOption2 == null && filterOption3 == null && filterOption4 == null) {
        // Filter map values on filterOption1
    } else if(filterOption1 != null && filterOption2 != null && filterOption3 == null && filterOption4 == null) {
        // Filter map values on filterOption1 and filterOption2
    } else if ... // And so on
    
} 

有没有办法通过map.stream() 的巧妙过滤来避免链接 16 个 if 语句?

【问题讨论】:

    标签: java collections hashmap java-stream filtering


    【解决方案1】:

    这是一种解决方案。它说明您不需要一系列 if 语句。

    您可以通过将谓词集合传递给过滤器来进一步改进。

    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.function.Predicate;
    
    public class FilterOption {
        public static void main(String... args) {
            FilterOption f = new FilterOption();
    
            System.out.println(f.theMap);
            f.filter(null, null);
            System.out.println(f.theMap);
            f = new FilterOption();
            f.filter(null, s -> s.startsWith("f"));
            System.out.println(f.theMap);
        }
    
        public FilterOption() {
            theMap.put("a", "foo");
            theMap.put("b", "bar");
        }
    
        Map<String, String> theMap = new HashMap<>();
    
        // assume we are filtering the values of the map
        // and assume that we keep the key if no filter
        // is false, and that a missing filter is true
        public void filter(Predicate<String> filterOption1,
                           Predicate<String> filterOption2) {
            List<Map.Entry<String, String>> entries =
                    new ArrayList(theMap.entrySet());
            for (Map.Entry<String, String> entry : entries) {
                if (!keep(entry.getValue(), filterOption1)
                        || !keep(entry.getValue(), filterOption2)) {
                    theMap.remove(entry.getKey());
                }
            }
        }
    
        private boolean keep(String value, Predicate<String> filterOption) {
            return filterOption == null || filterOption.test(value);
        }
    }
    

    【讨论】:

    • 更好的是,采用单个谓词并允许调用者根据需要组合(使用and()or())。
    【解决方案2】:

    您可以创建一些接受Predicates 数组的方法,连接非空元素并生成Predicates 链。然后你可以将这个链传递给filter 方法:

    public static void main(String... args) {
        Map<Character, Character> map =
                Map.of('a', 'A', 'b', 'B', 'c', 'C', 'd', 'D');
    
        // predicate chain 'and'
        Map<Character, Character> filtered1 = map.entrySet().stream()
                .filter(predicateChain(Predicate::and,
                        e -> e.getKey() > 'a', null,
                        e -> e.getValue() < 'D', null))
                .collect(Collectors.toMap(
                        Map.Entry::getKey, Map.Entry::getValue));
    
        System.out.println(filtered1); // {b=B, c=C}
    
        // predicate chain 'or'
        Map<Character, Character> filtered2 = map.entrySet().stream()
                .filter(predicateChain(Predicate::or,
                        e -> e.getKey() < 'b', null,
                        e -> e.getValue() > 'C', null))
                .collect(Collectors.toMap(
                        Map.Entry::getKey, Map.Entry::getValue));
    
        System.out.println(filtered2); // {a=A, d=D}
    }
    
    @SafeVarargs
    public static <T> Predicate<T> predicateChain(
            BinaryOperator<Predicate<T>> accumulator,
            Predicate<T>... predicates) {
        return Stream.of(predicates)
                // non-null predicates
                .filter(Objects::nonNull)
                // concatenation of predicates
                .reduce(accumulator)
                .orElse(p -> true);
    }
    

    【讨论】:

      【解决方案3】:

      如果您可以控制它,另一种解决方案是不传递 null。

      如果您的方法期望获得某种过滤器,为什么要给它一个空值?这可以在调用方法中处理,如果它真的不想过滤任何东西,它应该传递一个标识函数而不是 null。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-04
        • 2018-08-24
        • 2020-08-29
        • 2021-01-27
        • 1970-01-01
        相关资源
        最近更新 更多