【问题标题】:How to count single occurrences of elements in an ArrayList?如何计算 ArrayList 中元素的单次出现?
【发布时间】:2021-04-07 06:06:02
【问题描述】:

我正在尝试找出哪些数字在列表中出现过一次。例如,[1, 3, 2, 3] 的列表应该返回 [1, 2]。但是,我的代码只返回 [1],当我也尝试打印重复项时,它打印了 [3, 2]。

我不确定我哪里出错了,因为我正在跟踪重复项以确保我不会重复计算一个元素。

还有更有效的方法吗?例如,使用 HashMap?我知道我可以使用 Collections 类,但为了练习,我宁愿避免使用它!

任何帮助和建议将不胜感激。

这是我下面的内容:

import java.util.*;

class Main {
  public static void main(String[] args) {
    // From a list find which numbers appear once
    ArrayList<Integer> list = new ArrayList<Integer>();
    list.add(1);
    list.add(3);
    list.add(2);
    list.add(3);

    ArrayList<Integer> once = new ArrayList<Integer>();
    boolean isDuplicate = false;
    ArrayList<Integer> duplicate = new ArrayList<Integer>();

    for (int i=0; i<list.size(); i++) {
      if (!duplicate.contains(list.get(i))){ // if its not a duplicate, continue iterating through the list
        for (int j=i+1; j<list.size(); j++) {
          if (list.get(i).equals(list.get(j))) { // if its a duplicate
            isDuplicate = true;
          }
        }
        if (!isDuplicate) { // if its not a duplicate add to the "once" list
            once.add(list.get(i));
        } else { // if isDuplicate is true add to the duplicate list
            duplicate.add(list.get(i));
        }
          }
    }
    System.out.println("Numbers that appear once: " + once + "\nDuplicates: " + duplicate);
}

【问题讨论】:

  • 为什么不直接使用Map 来统计列表中每个数字出现的频率?
  • 您好,欢迎来到 StackOverflow!也许创建一个 HashSet 将您的列表作为构造函数参数传递给它?像这样:new HashSet&lt;Integer&gt;(list); 这使用 Sets 的唯一性属性为您完成工作。
  • 这篇文章是一个可能的解决方案How can I count occurrences with groupBy?

标签: java list arraylist duplicates


【解决方案1】:

您需要将isDuplicate 重置为false

import java.util.*;

class Main {
  public static void main(String[] args) {
    // From a list find which numbers appear once
    ArrayList<Integer> list = new ArrayList<Integer>();
    list.add(1);
    list.add(3);
    list.add(2);
    list.add(3);

    ArrayList<Integer> once = new ArrayList<Integer>();
    boolean isDuplicate = false;
    ArrayList<Integer> duplicate = new ArrayList<Integer>();

    for (int i=0; i<list.size(); i++) {
      if (!duplicate.contains(list.get(i))){ // if its not a duplicate, continue iterating through the list
        for (int j=i+1; j<list.size(); j++) {
          if (list.get(i).equals(list.get(j))) { // if its a duplicate
            isDuplicate = true;
          }
        }
        if (!isDuplicate) { // if its not a duplicate add to the "once" list
          once.add(list.get(i));
        } else { // if isDuplicate is true add to the duplicate list
          duplicate.add(list.get(i));
          isDuplicate = false;
        }
      }
    }
    System.out.println("Numbers that appear once: " + once + "\nDuplicates: " + duplicate);
  }
}

【讨论】:

    【解决方案2】:

    假设你有这个列表:

    ArrayList 列表 = 新的 ArrayList();

    ...

    如果它们只出现一次,您希望将它们保留在列表中。所以如果它是重复的,我们必须删除它。

    如果您的列表中只有一个数字,那么该数字的第一次和最后一次出现将是相同的。我们将使用它从列表中删除所有重复项:

    list.removeIf((number)->list.indexOf(number)!=list.lastIndexOf(number));

    【讨论】:

      【解决方案3】:

      特别是对于您的代码,您需要在每次迭代开始时将 isDuplicate 重置为 false,此外,您可以在将 isDuplicate 设置为 true 后添加 break 语句以避免其余部分的验证如果您已经知道这是重复的数字:

      for (int i=0; i<list.size(); i++) {
          isDuplicate = false; // reset the variable to false
          if (!duplicate.contains(list.get(i))){
              for (int j=i+1; j<list.size(); j++) {
                  if (list.get(i).equals(list.get(j))) {
                      isDuplicate = true;
                      break; // stops the execution of the current for
                  }
              }
              if (!isDuplicate) {
                  once.add(list.get(i));
              } else {
                  duplicate.add(list.get(i));
              }
          }
      }
      

      对于是否有最有效的方法来做到这一点的问题,有很多方法,但最简单的方法之一是不使用嵌套 for,您可以只询问该数字是否存在于一次列表中,如果是的,这意味着重复,然后您可以将其删除并将其添加到重复列表中,如下所示(在此解决方案中,duplicate 必须是 Set&lt;Integer&gt; 类型以避免重复,如果数字出现在原始列表):

      for(Integer number : list) {
          if (!once.contains(number) && !duplicate.contains(number)) {
              once.add(number);
          } else {
              once.remove(number);
              duplicate.add(number);
          }
      }
      

      【讨论】:

        【解决方案4】:

        答案来晚了,但是一般推荐使用Set来有效检测重复,因为当元素已经存在于集合中时,集合的add方法返回false,所以没有必要使用contains 方法。

        Set<Integer> once = new LinkedHashSet<>(); // use LinkedHashMap to keep insertion order
        Set<Integer> duplicates = new LinkedHashSet<>();
        for (Integer i : list) {
            if (!once.add(i)) { // duplicate detected
                duplicates.add(i);
            }
        }
        once.removeAll(duplicates); // remove all duplicates from `once` set
        System.out.println("once: " + once);
        System.out.println("duplicates: " + duplicates);
        

        输出:

        once: [1, 2]
        duplicates: [3]
        

        还可以使用 Java Stream API (Java 8+) 构建频率图,使用 groupingBysummingInt 等收集器来保持计数为整数或仅使用 toMap。然后可以通过频率值过滤地图条目:如果为1,则为单个,否则(频率> 1)为重复。

        Map<Integer, Integer> frequencyMap1 = list.stream()
                                                  .collect(Collectors.groupingBy(
                                                      x -> x, 
                                                      LinkedHashMap::new, 
                                                      Collectors.summingInt(x -> 1)
                                                  ));
        System.out.println("groupingBy: " + frequencyMap1);
        
        Map<Integer, Integer> frequencyMap = list.stream()
                                                 .collect(Collectors.toMap(
                                                     x -> x, x -> 1, Integer::sum, LinkedHashMap::new
                                                 ));
        System.out.println("toMap: " + frequencyMap);
        System.out.println("once: " + frequencyMap.entrySet().stream().filter(e -> e.getValue() == 1).map(Map.Entry::getKey).collect(Collectors.toList()));
        System.out.println("duplicates: " + frequencyMap.entrySet().stream().filter(e -> e.getValue() > 1).map(Map.Entry::getKey).collect(Collectors.toList()));
        

        输出:

        groupingBy: {1=1, 3=2, 2=1}
        toMap: {1=1, 3=2, 2=1}
        once: [1, 2]
        duplicates: [3]
        

        【讨论】:

          【解决方案5】:

          可以比较indexOflastIndexOf方法的返回值,filter出重复项:

          ArrayList<Integer> list = new ArrayList<>();
          list.add(1);
          list.add(3);
          list.add(2);
          list.add(3);
          
          ArrayList<Integer> distinct = list.stream()
                  .filter(e -> list.indexOf(e) == list.lastIndexOf(e))
                  .collect(Collectors.toCollection(ArrayList::new));
          
          System.out.println(distinct); // [1, 2]
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2018-10-19
            • 2015-08-13
            • 2010-10-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-06-09
            相关资源
            最近更新 更多