【问题标题】:How do I capture only the duplicate elements in an ArrayList?如何仅捕获 ArrayList 中的重复元素?
【发布时间】:2021-04-04 19:17:10
【问题描述】:

我需要在我的ArrayList 中获取所有重复项。我不需要删除重复项,我需要将它们添加到另一个 ArrayList。这是一个例子:

ArrayList<String> var = new ArrayList<>();
var.add("a");
var.add("b");
var.add("b");
var.add("c");

所以,如您所见,有 2 个重复元素(b 和 b)。我需要将它们添加到另一个 ArrayList。

在这种情况下,生成的ArrayList 应该是[b,b]。我该怎么做?

【问题讨论】:

  • 对我来说,这个问题似乎略有不同。按照我的阅读方式,OP希望示例案例返回[b, b],而该问题只需要[b]。
  • @CharlieArmstrong 是的,你说得对,我需要返回 [b, b]

标签: java arraylist duplicates java-stream


【解决方案1】:

方法一:

这样就足够了:

    for(String s : var )
        if(Collections.frequency(var, s) > 1)
            duplicates.add(s);

还有流:

var.stream().filter(s -> frequency(var, s) > 1).collect(toList());

一个运行的例子:

public static void main(String[] args) {

    List<String> var = Arrays.asList("a", "b", "b", "c");
    List<String> dup = var.stream().filter(s -> Collections.frequency(var, s) > 1).collect(Collectors.toList());         
    System.out.println(dup);
}

输出:

[b, b]

思路如下,考虑一下列表,检查每个元素在列表中出现的频率,如果它们出现不止一次,则添加到重复列表中。

方法2:

一个不太干净但时间复杂度更高的解决方案是使用该字符串的 per 频率的 Map,然后基于该 Map 构建重复列表:

List<String> dup =  new ArrayList<>();
Map<String, Long> frequencies =
        var.stream()
           .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

     for (Map.Entry<String, Long> entry : frequencies.entrySet()){
         for(int i = 0; i < entry.getValue() && entry.getValue() > 1; i++)
             dup.add(entry.getKey());
}

方法 3:

具有线性时间复杂度O(N):

    Set<String> set = new HashSet <>();
    List<String> duplicates = new ArrayList<>();
    Set<String> is_duplicated = new HashSet <>();
    var.forEach(s -> {
        if(set.contains(s)) {
            is_duplicated.add(s);
            duplicates.add(s);
        }
        else
          set.add(s);
    });
    duplicates.addAll(is_duplicated);
    System.out.println(duplicates);

可以利用Set.add 方法语义,即:

If this set already contains the element, the call leaves the set
     unchanged and returns {@code false}.

将上面的代码缩短为:

    Set<String> set = new HashSet <>();
    List<String> duplicates = new ArrayList<>();
    Set<String> to_add = new HashSet<>();
    var.forEach(s -> {
        if(!set.add(s)) {
            to_add.add(s);
            duplicates.add(s);
        }
    });
    duplicates.addAll(to_add);
    System.out.println(duplicates);  

【讨论】:

    【解决方案2】:

    您可以使用HashMap 存储每次迭代中String 的出现次数。

    然后,对于多次出现的元素,将它们添加n次到新的List:

    List<String> var = new ArrayList<>();
    var.add("a");
    var.add("b");
    var.add("b");
    var.add("c");
            
    Map<String, Integer> map = new HashMap<>();
    for(String str : var) {
        if(map.containsKey(str))
            map.put(str, map.get(str)+1);
        else
            map.put(str, 1);
    }
            
    List<String> duplicates = new ArrayList<>();
    for (String str : var) {
        int count = map.get(str);
        if(count > 1) {
            duplicates.add(str);
        }
    }
            
    System.out.println(duplicates);
    

    输出:

    [b,b]
    

    【讨论】:

    • 呃,我要返回["b", "b"]
    • 我在这里看到的一个问题是地图不保持元素的顺序,对于 OP 来说可能不是问题..
    • @Bahij.Mik 好记,我修改它以维持秩序
    【解决方案3】:

    您可以遍历 ArrayList 并比较 indexOf 和 lastIndexOf 的值。如果它们相同,则列表中只有一个 Object 实例。否则就是重复的。

    【讨论】:

      【解决方案4】:

      这是我对这个问题的解决方案:

      ArrayList<String> duplicates = (ArrayList<String>) var.stream()
              .filter((e) ->
                      var.stream()
                              .filter(e::equals)
                              .count() > 1
              ).collect(Collectors.toList());
      

      它的作用是使用原始 ArrayList (var) 作为源创建一个流,并对其进行过滤。过滤器的谓词还为外部流中的每个元素创建一个相同的流,但这次它通过与外部流上的当前元素等效的所有元素进行过滤。因此,假设在您的示例中,您位于第一个“b”元素上,它会查看 ArrayList 并构造一个仅包含“b”的流。然后它计算流中元素的数量并断言剩下的元素超过 1 个。换句话说,它验证这个元素是重复的。如果该谓词失败,则从流中删除该元素,因此当您将流收集回 ArrayList 时,只剩下重复的元素。

      【讨论】:

        【解决方案5】:

        您可以创建一个将所有元​​素分组的映射,然后从该映射中删除大小为1 的组的条目,最后将列表集合转换为平面列表。另外,我不会使用 var 作为标识符,因为在较新版本的 Java 中它是 reserved type name。

        在代码中:

        Map<String, List<String>> map = yourInitialList.stream()
            .collect(Collectors.groupingBy(Function.identity()));
        
        map.values().removeIf(list -> list.size() == 1);
        
        List<String> result = map.values().stream()
            .flatMap(list -> list.stream())
            .collect(Collectors.toList());
        

        如果您还需要保留插入顺序,您可以使用Collectors.groupingBy 的重载调整地图的创建:

        Map<String, List<String>> map = yourInitialList.stream()
            .collect(Collectors.groupingBy(
                     Function.identity(),
                     LinkedHashMap::new,
                     Collectors.toList()));
        

        【讨论】:

          【解决方案6】:

          这个问题的正确答案是使用集合中的集合,通过实现适当的相等和散列方法。 Set 不允许重复。

          https://docs.oracle.com/javase/tutorial/collections/interfaces/set.html

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2019-07-10
            • 2017-11-06
            • 2011-08-03
            • 2014-09-22
            • 2022-01-17
            • 2021-10-29
            • 2017-11-17
            相关资源
            最近更新 更多