删除列表中的所有非唯一成员

时间:2015-01-17 04:24:35

标签: java loops exception exception-handling concurrentmodification

我想创建一个方法来过滤掉列表中所有非唯一成员,例如带有输入的列表

  

3 5 3 8 8 2

会变成

  

5 2

我有想法尝试以下内容:

private static List<Integer> getUniques(List<Integer> list) {
        for (Integer n : list) {
            list.remove(n);
            if (!list.contains(n)) {
                list.add(n);
            } else {
                while (list.contains(n)) {
                    list.remove(n);
                }
            }
        }
        return list;
    }

但是这会引发一个Concurrent Modification异常。我做了一个工作调整:

private static List<Integer> getUniques(List<Integer> list) {
        List<Integer> result = new ArrayList<>();
        Set<Integer> distinctSet = new HashSet<>();

        distinctSet.addAll(list);
        result.addAll(list);

        for (Integer n : distinctSet) {
            result.remove(n);
            if (!result.contains(n)) {
                result.add(n);
            } else {
                while (result.contains(n)) {
                    result.remove(n);
                }
            }
        }
        return result;
    }

这实现了我想要的,但似乎有点复杂/低效。有没有办法让我以我想到的第一种方式做到这一点?或者另一种更有效的方法?或者我已经基本上使用了最好的方法?

3 个答案:

答案 0 :(得分:4)

更好的方法是使用HashMap标记要保留的元素,然后根据该标记添加。此方法为O(N),优于您的解决方案的O(N^2)(删除可能为O(N),具体取决于传入的List实现)。如果这很重要,它当然也会保留原始列表中元素的顺序。

private static List<Integer> getUniques(List<Integer> list) {
    HashMap<Integer, Boolean> flagMap = new HashMap<>();

    //Total Loop: O(N)
    for(Integer i : list){
        if(flagMap.containsKey(i)) flagMap.put(i, false); //O(1)
        else flagMap.put(i, true); //O(1)
    }

    ArrayList<Integer> result = new ArrayList<Integer>();

    //Total Loop: O(N)
    for(Integer i : list){
        if(flagMap.get(i)) result.add(i); //O(1)
    }
    return result;
}

答案 1 :(得分:1)

我建议你从一个方法开始计算一个项目在List中显示的次数。像

这样的东西
private static <T> int count(List<T> al, T val) {
    int r = 0;
    for (T t : al) {
        if (t.equals(val)) {
            r++;
        }
    }
    return r;
}

然后创建一个新的List返回,并在将count添加到List之前检查private static List<Integer> getUniques(List<Integer> list) { List<Integer> al = new ArrayList<>(); for (Integer n : list) { if (count(list, n) == 1) { al.add(n); } } return al; } 是否为

{{1}}

答案 2 :(得分:1)

使用Java 8,您可以使用流API执行Mshnik正在执行的操作。但是,由于这仍然是一个新的API,您可能需要在工作中谨慎使用它。可读性(由你和你的同行)应该胜过简洁。如果使用Java 8 / stream API不是一个选项,那么Mshnik的解决方案就是我的选择。

List<Integer> uniques = 
    list.stream().
         collect(Collectors.groupingBy(i -> i, Collectors.reducing(0, (a, b) -> a + b))). //Adding all occurances of that number {3->6, 5->5, 8->16, 2->2}
         entrySet().stream(). //The above map's entries as a set
         filter(e -> e.getValue() == e.getKey()). //Retains {5->5, 2->2}
         map(e -> e.getKey()). //Retains {5, 2}
         collect(Collectors.<Integer>toList()); //Collects

请注意,虽然每一行可能看起来是列表上的另一个迭代,但这实际上是一个2遍解决方案(第一遍是我们收集地图时,第二遍是我们收集清单时)。

复杂性:预期 O(N) - 如果groupingBy将使用HashMap。