删除列表的所有非唯一成员
Removing all non-unique members of a list
我想创建一个方法来过滤掉列表中的所有非唯一成员,这样一个带有输入的列表
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;
}
但这会引发并发修改异常。我做了一个工作调整:
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;
}
这实现了我想要的,但似乎有点 convoluted/inefficient。有没有办法让我按照我想到的第一种方式去做?或者一般来说另一种更有效的方法?还是我已经基本上使用了可用的最佳方法?
我建议您从计算某个项目在 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
到 return,并检查 count
是否为 1,然后再将其添加到 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;
}
更好的方法是使用 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;
}
使用 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 遍解决方案(第一遍是当我们收集 map 时,第二遍是当我们收集列表时)。
复杂度:预期 O(N) -- 因为 groupingBy 将使用 HashMap。
我想创建一个方法来过滤掉列表中的所有非唯一成员,这样一个带有输入的列表
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;
}
但这会引发并发修改异常。我做了一个工作调整:
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;
}
这实现了我想要的,但似乎有点 convoluted/inefficient。有没有办法让我按照我想到的第一种方式去做?或者一般来说另一种更有效的方法?还是我已经基本上使用了可用的最佳方法?
我建议您从计算某个项目在 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
到 return,并检查 count
是否为 1,然后再将其添加到 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;
}
更好的方法是使用 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;
}
使用 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 遍解决方案(第一遍是当我们收集 map 时,第二遍是当我们收集列表时)。
复杂度:预期 O(N) -- 因为 groupingBy 将使用 HashMap。