【问题标题】:How to select duplicate values from a list in java?如何从java中的列表中选择重复值?
【发布时间】:2013-06-25 22:30:14
【问题描述】:

例如,我的列表包含 {4, 6, 6, 7, 7, 8},我想要最终结果 = {6, 6, 7, 7}

一种方法是遍历列表并消除唯一值(本例中为 4、8)。

除了遍历 list 之外,还有其他有效的方法吗?我问这个问题是因为我正在工作的列表非常大? 我的代码是

List<Long> duplicate = new ArrayList();
for (int i = 0; i < list.size(); i++) {
     Long item = (Long) list.get(i);
     if (!duplicate.contains(item)) {
          duplicate.add(item);
         }
     }

【问题讨论】:

  • 如果要查找所有重复项,您必须遍历整个列表,至少一次。如果您必须比较列表中的每个值以使其更有效,则没有“更有效”的方法可以使用列表执行此操作。解决方案在于创建列表。
  • 您至少需要一个循环。如果您想要一个更有效的代码(尽管并非在所有情况下都保证),您可以尝试先订购列表,然后检查“邻居”是否不同(如果是,您有一个唯一的项目,只需将其从列表中删除)
  • 如果您不想循环,您可以随时打印出列表并计算重复项。
  • 您知道您的代码没有按照您的问题要求执行吗?

标签: java list duplicates unique


【解决方案1】:

到目前为止,有一些不错的答案,但另一种选择只是为了好玩。循环遍历列表,尝试将每个数字放入 Set 例如HashSet。如果 add 方法返回 false,则表明该数字是重复的,应该进入重复列表。

编辑:应该这样做

Set<Number> unique = new HashSet<>();
List<Number> duplicates = new ArrayList<>();
for( Number n : inputList ) {
    if( !unique.add( n ) ) {
        duplicates.add( n );
    }
}

【讨论】:

  • 由于输入数据“大”,您应该预先分配集合的大小。
【解决方案2】:

除了循环遍历列表还有其他有效的方法吗?

你可以雇佣一个魔法精灵,让它为你做这件事。如果不循环访问它,您将如何做到这一点?如果您不遍历列表,您甚至无法查看元素。这就像你想把一大堆数字加在一起而不看这些数字。对元素求和比搜索重复元素或搜索唯一元素要容易得多。一般来说,97% 的代码所做的事情是循环遍历列表和数据,然后处理和更新它。

也就是说,你必须循环。现在您可能想要选择最有效的方式。想到了一些方法:

  • 对所有数字进行排序,然后仅循环一次以查找重复项(因为它们将彼此相邻)。不过请记住,排序算法也会循环遍历数据。
  • 对于列表中的每个元素,检查是否存在具有相同值的另一个元素。 (这就是你的做法。这意味着你在彼此内部有两个循环。(contains 当然循环遍历列表。)

【讨论】:

    【解决方案3】:
    List<Number> inputList = Arrays.asList(4, 6, 6, 7, 7, 8);
    List<Number> result = new ArrayList<Number>();
    for(Number num : inputList) {
       if(Collections.frequency(inputList, num) > 1) {
           result.add(num);
       }
    }
    

    我不确定效率,但我发现代码易于阅读(应该首选。

    编辑:将 Lists.newArrayList() 更改为 new ArrayList&lt;Number&gt;();

    【讨论】:

    • 我猜你在这里使用了一些第 3 方库... (Lists.newArrayList())?但是你可以使用new ArrayList&lt;&gt;();
    • @Junaid 我知道。我指的是Lists.newArrayList()
    • 这实际上是使用 Guava 以及那些过滤器和谓词的好任务。
    • 我评论后才意识到。打扰一下。 ;)
    • 当然,可读性非常重要,但它具有二次复杂性,这太糟糕了。作为 Guava 用户,您可以享受 Multiset like I did。如果没有 Guava,HashMap 可能会多出一行。
    【解决方案4】:

    我喜欢回答Java 8, Streams to find the duplicate elements。解决方案仅返回唯一的重复项。

     Integer[] numbers = new Integer[] { 1, 2, 1, 3, 4, 4 };
     Set<Integer> allItems = new HashSet<>();
     Set<Integer> duplicates = Arrays.stream(numbers)
        .filter(n -> !allItems.add(n)) //Set.add() returns false if the item was already in the set.
        .collect(Collectors.toSet());
     System.out.println(duplicates); // [1, 4]
    

    【讨论】:

    • 这没有回答问题,他想要所有重复的数字。在您的示例中将是:[1,1,4,4]
    【解决方案5】:

    有一个

    Map<Integer, Integer> numberToOccurance = new HashMap<Integer, Integer>();
    

    维护count和number,最后迭代keyset并获取超过一个count的值

    【讨论】:

    • 如果你想对数字进行排序,也可以使用 TreeMap。
    • 为什么我们需要排序开销,Hash 更快!
    • 好吧,如果 OP 希望对数字进行排序。在示例中,数字已排序。
    • @Jigar,好吧,如果我使用 Tree 或 Hash Map,我仍然需要迭代。我正在处理的实际数据是一个大型加密文本项目列表。
    • @puce:如果由于某种原因应该对结果进行排序(尽管没有人提到过),那么对结果列表进行排序而不是使用 TreeMap 来计算出现次数会更快稍后有潜在的好处,即结果已经排序。
    【解决方案6】:

    您的List 理想情况下应该是Set,它首先不允许重复。作为循环的替代方法,您可以转换并切换到 Set 或在中间使用它来消除重复,如下所示:

    List<Long> dupesList = Arrays.asList(4L, 6L, 6L, 7L, 7L, 8L);
    
    Set<Long> noDupesSet = new HashSet<Long>(dupesList);
    System.out.println(noDupesSet); // prints: [4, 6, 7, 8]
    
    // To convert back to List
    Long[] noDupesArr = noDupesSet.toArray(new Long[noDupesSet.size()]);
    List<Long> noDupesList = Arrays.asList(noDupesArr);
    System.out.println(noDupesList); // prints: [4, 6, 7, 8]
    

    【讨论】:

    • 这没有回答问题,他想要所有重复的数字。在您的示例中将是:[6,6,7,7]
    【解决方案7】:
    import java.util.ArrayList;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Set;
    
    public class FindDuplicate {
    
        public static void main(String[] args) {
    
            // Load all your ArrayList
            List<String> list = new ArrayList<String>();
            list.add("Jhon");
            list.add("Jency");
            list.add("Mike");
            list.add("Dmitri");
            list.add("Mike");
    
            // Set will not allow duplicates
            Set<String> checkDuplicates = new HashSet<String>();
    
            System.out.println("Actual list " + list);
            for (int i = 0; i < list.size(); i++) {
                String items = list.get(i);
                if (!checkDuplicates.add(items)) {
                    // retain the item from set interface
                    System.out.println("Duplicate in that list " + items);
                }
            }
    
        }
    }
    

    【讨论】:

      【解决方案8】:

      使用 Guava 和 Java 8,它既简单又快速:

      Multiset<Integer> multiset = HashMultiset.create(list);
      return list.stream()
          .filter(i -> multiset.count(i) > 1)
          .collect(Collectors.toList());
      

      第一行使用一种哈希映射计算计数。剩下的就很明显了。

      这样的东西可以模拟多重集:

      HashMap<Integer, Integer> multiset = new HashMap<>();
      list.stream().forEach(i -> 
          multiset.compute(i, (ignored, old) -> old==null ? 1 : old+1)));
      

      【讨论】:

        【解决方案9】:

        lambda 再次拯救了这一天:

        List<Long> duplicates = duplicate.stream()
          .collect( Collectors.collectingAndThen( Collectors.groupingBy( Function.identity() ),
            map -> {
              map.values().removeIf( v -> v.size() < 2 );  // eliminate unique values (4, 8 in this case)
              return( map.values().stream().flatMap( List::stream ).collect( Collectors.toList() ) );
            } ) );  // [6, 6, 7, 7]
        


        上述解决方案的速度优化版本:

        List<Long> duplicates = duplicate.stream().collect( Collectors.collectingAndThen(
            Collectors.groupingBy( Function.identity(), Collectors.counting() ),
            map -> {
              map.values().removeIf( v -> v < 2 );  // eliminate unique values (4, 8 in this case)
              return( map.entrySet().stream().collect( Collector.of( ArrayList<Long>::new, (list, e) -> {
                for( long n = 0; n < e.getValue(); n++ )
                  list.add( e.getKey() );
              }, (l1, l2) -> null ) ) );
            } ) );  // [6, 6, 7, 7]
        

        duplicate 的长值不会被保存但会被计算——当然是最快和最节省空间的变体

        【讨论】:

          【解决方案10】:

          以下内容适用于Eclipse Collections

          IntBag bag = IntLists.mutable.with(4, 6, 6, 7, 7, 8).toBag().selectDuplicates();
          

          如果您想要装箱的值而不是原始值,以下方法将起作用:

          Bag<Integer> bag = Lists.mutable.with(4, 6, 6, 7, 7, 8).toBag().selectDuplicates();
          

          注意:我是 Eclipse Collections 的提交者。

          【讨论】:

            【解决方案11】:

            试试这个:

            受此答案启发:https://stackoverflow.com/a/41262509/11256849

            for (String s : yourList){
                 if (indexOfNth(yourList, s, 2) != -1){
                     Log.d(TAG, s);
                  }
               }
            

            使用这种方法:

            public static <T> int indexOfNth(ArrayList list, T find, int nthOccurrence) {
                    if (list == null || list.isEmpty()) return -1;
                    int hitCount = 0;
                    for (int index = 0; index < list.size(); index++) {
                        if (list.get(index).equals(find)) {
                            hitCount++;
                        }
                        if (hitCount == nthOccurrence) return index;
                    }
                    return -1;
                }
            

            【讨论】:

              【解决方案12】:

              鉴于您可以通过仅循环一次列表来完成此操作,因此我不会过多担心性能。如果您寻找更高性能的解决方案,那么您最终可能会使代码过于复杂,并且可读性和可维护性会受到影响。归根结底,如果您想检查整个列表是否有重复项,则必须访问每个元素。

              我建议编写显而易见的解决方案,看看它的表现如何。您可能会惊讶于 Java 对列表的迭代速度有多快,即使它特别大。

              【讨论】:

                【解决方案13】:

                这是我的解决方案版本:

                import java.util.ArrayList;
                
                public class Main {
                
                public static void main(String[] args) {
                
                    ArrayList<Integer> randomNumbers = new ArrayList<Integer>();
                    ArrayList<Integer> expandingPlace = new ArrayList<Integer>();
                    ArrayList<Integer> sequenceOfDuplicates = new ArrayList<Integer>();
                
                    for (int i = 0; i < 100; i++) {
                        randomNumbers.add((int) (Math.random() * 10));
                        expandingPlace.add(randomNumbers.get(i));
                    }
                
                    System.out.println(randomNumbers); // Original list.
                
                    for (int i = 0; i < randomNumbers.size(); i++) {
                        if (expandingPlace.get(i) == expandingPlace.get(i + 1)) {
                            expandingPlace.add(0);
                            sequenceOfDuplicates.add(expandingPlace.get(i)); 
                            sequenceOfDuplicates.add(expandingPlace.get(i + 1));
                        }
                    }
                
                    System.out.println(sequenceOfDuplicates); // What was in duplicate there.
                
                }
                
                }
                

                它将从 0 到 9 的数字添加到列表中,并将“重复”中的内容(数字后跟相同的数字)添加到另一个列表中。你可以用你的大列表代替我的 randomNumbers ArrayList。

                【讨论】:

                • -1:此代码假定随机数已排序,但随机生成它们时并非如此。当没有重复时,此代码也会崩溃。此代码还将报告一大堆重复项。要了解我对这些评论的意思,请在以下列表中测试此代码:3, 1, 3(未找到重复项!)和1, 2, 3(崩溃!)和1, 1, 1(将在重复列表中报告四次1 !)。
                • 你是对的。随机数只是我发现用随机信息填充列表的一种方式(考虑到我不知道他的列表会怎样)。
                猜你喜欢
                • 2020-07-27
                • 2019-07-09
                • 2021-03-19
                • 1970-01-01
                • 2014-11-26
                • 2022-01-16
                • 1970-01-01
                • 2016-02-27
                • 1970-01-01
                相关资源
                最近更新 更多