识别列表中的重复项

fre*_*est 105 java collections

我有一个Integer类型的List,例如:

[1, 1, 2, 3, 3, 3]
Run Code Online (Sandbox Code Playgroud)

我想要一个方法来返回所有重复项,例如:

[1, 3]
Run Code Online (Sandbox Code Playgroud)

做这个的最好方式是什么?

lei*_*ifg 172

返回布尔值的方法add,Set无论值是否已存在(如果不存在,则返回true;如果已存在,则返回false,请参阅设置文档).

所以只需遍历所有值:

public Set<Integer> findDuplicates(List<Integer> listContainingDuplicates)
{ 
  final Set<Integer> setToReturn = new HashSet<>(); 
  final Set<Integer> set1 = new HashSet<>();

  for (Integer yourInt : listContainingDuplicates)
  {
   if (!set1.add(yourInt))
   {
    setToReturn.add(yourInt);
   }
  }
  return setToReturn;
}
Run Code Online (Sandbox Code Playgroud)

  • 对,就是这样.但是当elemnt仅在指定列表中出现一次时,也会添加该元素.查看问题中的示例:我的解决方案将返回[1,3],因为数字2插入set1但不插入setToReturn.您的解决方案将返回[1,2,3](这不是要求) (3认同)
  • 为什么你有setToReturn?你不能只使用 set1.add(yourInt) 并返回 set1 吗? (2认同)
  • 顺便说一句,对于“HashSet”,您还必须考虑负载因子,例如,当您指定初始容量“100”时,因为您想要添加该数量的元素,所以它会四舍五入到 2 的下一个幂(`128`),这意味着默认负载因子为`0.75f`,调整大小阈值将为`96`,因此在添加`100`元素之前将会调整大小。值得庆幸的是,调整大小不再那么昂贵了。使用最新的 JRE,调整大小不再是重新散列,元素只是根据相关位分布在两个可能的结果位置之间。 (2认同)

Joh*_*ler 46

我也需要一个解决方案.我使用了leifg的解决方案并使其成为通用的.

private <T> Set<T> findDuplicates(Collection<T> collection) {

    Set<T> duplicates = new LinkedHashSet<>();
    Set<T> uniques = new HashSet<>();

    for(T t : collection) {
        if(!uniques.add(t)) {
            duplicates.add(t);
        }
    }

    return duplicates;
}
Run Code Online (Sandbox Code Playgroud)

  • @AhmadRagab你是对的,除非你关心找到重复项的顺序(我认为我当时做了),否则不需要LinkedHashSet (4认同)
  • 我知道这是 3 年后的事了,但是为什么是 LinkedHashedSet,即为什么要关心顺序? (2认同)

Seb*_*ian 34

我采用了John Strickler的解决方案并重新构建它以使用JDK8中引入的流API:

private <T> Set<T> findDuplicates(Collection<T> collection) {
    Set<T> uniques = new HashSet<>();
    return collection.stream()
        .filter(e -> !uniques.add(e))
        .collect(Collectors.toSet());
}
Run Code Online (Sandbox Code Playgroud)

  • 这很难读,不是吗?您在流操作中正在产生副作用,这使得很难进行推理。但这只是我在思考功能样式。这很简洁,虽然可能是最短的方法;)。 (3认同)

sno*_*man 13

这是在Java 8中使用Streams的解决方案

// lets assume the original list is filled with {1,1,2,3,6,3,8,7}
List<String> original = new ArrayList<>();
List<String> result = new ArrayList<>();
Run Code Online (Sandbox Code Playgroud)

您只需查看列表中此对象的出现次数是否超过一次即可。然后调用.distinct()以在结果中仅包含唯一元素

result = original.stream()
    .filter(e -> Collections.frequency(original, e) > 1)
    .distinct()
    .collect(Collectors.toList());
// returns {1,3}
// returns only numbers which occur more than once

result = original.stream()
    .filter(e -> Collections.frequency(original, e) == 1)
    .collect(Collectors.toList());
// returns {2,6,8,7}
// returns numbers which occur only once

result = original.stream()
    .distinct()
    .collect(Collectors.toList());
// returns {1,2,3,6,8,7}
// returns the list without duplicates
Run Code Online (Sandbox Code Playgroud)

  • 这在可读性方面很好,但对性能来说确实很糟糕。“集合::频率”是 O(n)。它需要遍历整个集合才能找到某个项目的频率。我们为集合中的每个项目调用一次,这使得这些片段为“O(n^2)”。您会注意到任何包含多个元素的集合的差异。我永远不会在实际代码中使用它。 (6认同)

Ash*_*yan 12

int[] nums =  new int[] {1, 1, 2, 3, 3, 3};
Arrays.sort(nums);
for (int i = 0; i < nums.length-1; i++) {

    if (nums[i] == nums[i+1]) {
        System.out.println("duplicate item "+nums[i+1]+" at Location"+(i+1) );
    }

}
Run Code Online (Sandbox Code Playgroud)

显然你可以用它们做任何你想做的事情(即放入一个Set来获得一个重复值的唯一列表)而不是打印...这也有记录重复项目的位置的好处.


بلا*_*ودي 8

Java 8基本解决方案:

List duplicates =    
list.stream().collect(Collectors.groupingBy(Function.identity()))
    .entrySet()
    .stream()
    .filter(e -> e.getValue().size() > 1)
    .map(Map.Entry::getKey)
    .collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

  • 输入列表被转换为地图,(按相同值分组)。然后“删除”具有唯一值的映射值,然后使用键映射,然后将列表列表转换为列表 (2认同)
  • 使用计数怎么样?`stream() .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())) .entrySet().stream().filter(e -&gt; e.getValue() &gt; 1) .map(Map .Entry::getKey).collect(Collectors.toList())` (2认同)

Adr*_*ter 7

这也有效:

public static Set<Integer> findDuplicates(List<Integer> input) {
    List<Integer> copy = new ArrayList<Integer>(input);
    for (Integer value : new HashSet<Integer>(input)) {
        copy.remove(value);
    }
    return new HashSet<Integer>(copy);
}
Run Code Online (Sandbox Code Playgroud)


Phi*_*and 7

在Java 8上使用Guava

private Set<Integer> findDuplicates(List<Integer> input) {
    // Linked* preserves insertion order so the returned Sets iteration order is somewhat like the original list
    LinkedHashMultiset<Integer> duplicates = LinkedHashMultiset.create(input);

    // Remove all entries with a count of 1
    duplicates.entrySet().removeIf(entry -> entry.getCount() == 1);

    return duplicates.elementSet();
}
Run Code Online (Sandbox Code Playgroud)


Eng*_*uad 6

你可以使用这样的东西:

List<Integer> newList = new ArrayList<Integer>();
for(int i : yourOldList)
{
    yourOldList.remove(i);
    if(yourOldList.contains(i) && !newList.contains(i)) newList.add(i);
}
Run Code Online (Sandbox Code Playgroud)

  • 在这里使用List非常无效 (2认同)
  • 并且不要让我开始在这里使用`int`作为变量类型.这意味着对于每次迭代,Integer都会被取消装箱一次,并且int被装箱四次! (2认同)

小智 5

Lambas 可能是一个解决方案

Integer[] nums =  new Integer[] {1, 1, 2, 3, 3, 3};
List<Integer> list = Arrays.asList(nums);

List<Integer> dps = list.stream().distinct().filter(entry -> Collections.frequency(list, entry) > 1).collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)