在计算两个列表之间的重复值时如何短路?

时间:2018-04-21 14:22:43

标签: java list java-8 java-stream

我有2个列表,我需要以最快的方式计算/检查列表A中与列表B中的元素匹配的重复元素。

例如,如果列表A是["A", "B", "C"]而列表B是["X", "B", "B", "A", "C", "C", "C"],那么我的计数器应该是2,因为B中有2个重复的元素("B" & "C")。因为它是一个布尔方法,只要重复出现A中的A,它就应该返回true。

我正在避免级联循环甚至尝试使用流。虽然以下代码有效,但我对它的设计仍然不太确定。 这就是我现在正在做的事情:

class MyPojo {
    int value; String str;
    MyPojo(int value) { this.value = value; };
    /* getters & setters*/ 
}

public static boolean hasDuplicates() {
    List<Integer> forbiddenValues = Arrays.asList(1, 2, 3);
    List<MyPojo> pojoList = Arrays.asList(new MyPojo(0), new MyPojo(2), 
    new MyPojo(2), new MyPojo(3), new MyPojo(3), new MyPojo(4));

    for ( Integer value : forbiddenValues) {
        long count = pojoList.stream()
            .filter( pojoElement -> pojoElement.getValue() == value)
            .count();
        // returns true if in a single iteration count is greater than 1
        if ( count > 1) {
           return true;
        }
    }
    return false;
}

6 个答案:

答案 0 :(得分:2)

这对你有用。让我知道你有任何问题。如果需要,您也可以使用并行流。

使用Stream API

public static boolean hasDuplicates() {
        List<Integer> forbiddenValues = Arrays.asList(1, 2, 3);

        List<MyPojo> pojoList = Arrays.asList(new MyPojo(0), new MyPojo(2),
                new MyPojo(2), new MyPojo(3), new MyPojo(3), new MyPojo(4));


        long count = pojoList.stream()
                .filter(pojo -> forbiddenValues.contains(pojo.getValue()))
                .map(MyPojo::getValue)
                .collect(Collectors.groupingBy(value -> value))
                .values()
                .stream()
                .filter(values -> values.size() > 1)
                .count();

        return count > 1;
    }

无流

public static boolean hasDuplicates() {
        List<Integer> forbiddenValues = Arrays.asList(1, 2, 3);

        List<MyPojo> pojoList = Arrays.asList(new MyPojo(0), new MyPojo(2),
                new MyPojo(2), new MyPojo(3), new MyPojo(3), new MyPojo(4));


        Map<Integer, Integer> counts = new HashMap<>();

        for(int forbidden : forbiddenValues){
            counts.put(forbidden, 0);
        }

        for(MyPojo myPojo : pojoList){
            if(counts.containsKey(myPojo.getValue())){
                int count = counts.get(myPojo.getValue());

                if(count == 1){
                    return true;
                }

                counts.put(myPojo.getValue(), count + 1);
            }
        }

        return false;
    }

答案 1 :(得分:2)

使用HashSet检查元素是否存在,因为contains要快得多,并且按照@Aonimé建议你可以这样做。

public static boolean hasDuplicates() {
        List<Integer> forbiddenValues = Arrays.asList(1, 2, 3);
        Set<Integer> forbiddenValuesSet = new HashSet<>(forbiddenValues);
        List<MyPojo> pojoList = Arrays.asList(new MyPojo(0), new MyPojo(2),
                new MyPojo(2), new MyPojo(3), new MyPojo(3), new MyPojo(4));

        long count = pojoList.stream()
                     .filter(t -> forbiddenValuesSet.contains(t.value)).limit(2).count();
        return count > 1;
    }

答案 2 :(得分:2)

您可以使用

return pojoList.stream()
        .map(MyPojo::getValue)
        .filter(forbiddenValues::contains)
        .collect(Collectors.toMap(Function.identity(), value -> false, (a, b) -> true))
        .containsValue(true);
  • 首先,将MyPojo元素映射到值
  • 然后只允许forbiddenValues中包含的那些传递(为了提高效率,强烈建议在集合变大时使用Set
  • 使用值作为键收集到地图并最初映射到false,但使用 merge 函数,对于任何重复键的出现,它将评估为true < / LI>
  • 如果生成的地图包含任何true值,则我们有重复的密钥

这与其他已发布的答案类似,但会跳过许多不必要的操作。但是,它需要在我们查询副本之前构建整个映射,因此,循环可能仍然更快:

Set<Integer> seen = new HashSet<>();
for(MyPojo pojo: pojoList) {
    Integer value = pojo.getValue();
    if(forbiddenValues.contains(value) && !seen.add(value)) return true;
}
return false;

Set.add返回是否将值添加到集合中,即它是否已包含在集合中。因此,只要此方法返回false,我们就知道遇到了重复。

答案 3 :(得分:1)

我建议使用纯粹的命令式循环而不是流,因为后者往往会导致比你想象的更多的开销。

所以,在这种情况下,我会先考虑每个循环嵌套,然后再考虑Streams。

此外,如果您决定继续使用流方法,那么您可以做的一项改进是在limit(2)急切操作之前调用count以尽可能短路。

答案 4 :(得分:0)

public static boolean hasDuplicates() {
    List<Integer> forbiddenValues = Arrays.asList(1, 2, 3);
    List<MyPojo> pojoList = Arrays.asList(new MyPojo(0), new MyPojo(2), 
            new MyPojo(2), new MyPojo(3), new MyPojo(3),
            new MyPojo(4));

    Map<Integer, Long> map = 
         pojoList.stream().collect(Collectors.groupingBy(MyPojo::getValue, 
                    Collectors.counting()));
    boolean result = 
         forbiddenValues.stream().filter(map::containsKey).map(map::get)
         .anyMatch(count -> count > 1);

    return result;
}

答案 5 :(得分:-1)

这样可行。

    public static boolean hasDuplicates() {
        List<MyPojo> forbiddenValues = Arrays.asList(new MyPojo(1), new MyPojo(2),
            new MyPojo(3));

        List<MyPojo> pojoList = Arrays.asList(new MyPojo(0), new MyPojo(2),
        new MyPojo(2), new MyPojo(3), new MyPojo(3), new MyPojo(4));

        for(MyPojo i : forbiddenValues){
            if(pojoList.contains(i))
                return true;
        }
        return false;
    }
}

class MyPojo {
    int value; String str;
    MyPojo(int value) { this.value = value; };

    public int getValue(){
        return this.value;
    }

    @Override
    public boolean equals(Object o) {

        if (o == this)
            return true;

        if (!(o instanceof MyPojo))
            return false;

        MyPojo p = (MyPojo) o;

        return this.value==p.getValue();
    }
}