【问题标题】:Remove duplicates (both values) - duplicate values from an ArrayList删除重复项(两个值) - ArrayList 中的重复值
【发布时间】:2016-01-12 14:31:59
【问题描述】:

我有一个带有以下字符串的ArrayList

 List<String> e = new ArrayList<String>();
 e.add("123");
 e.add("122");
 e.add("125");
 e.add("123");

我想检查列表中的重复项并将它们从列表中删除。在这种情况下,我的列表将只有两个值,在此示例中它将是值 122 和 125,两个 123 将消失。

最好的方法是什么?我正在考虑使用Set,但这只会删除其中一个重复项。

【问题讨论】:

  • 您可以使用 Map (表示字符串在列表中的次数),然后过滤只有值为 1 的条目,并收集相应的键进入一个新列表。
  • @3Kings 他想删除如果值有重复然后删除重复的值....所以在上面的例子中都是 123
  • set 的 add() 方法如果值没有重复并且插入成功,则返回 true。如果您插入的新值是骗子,您可以使用它来获得指示。然后你可以找到并删除欺骗
  • 你可以使用多哈希图
  • A Set 不会删除项目,它会阻止添加重复项目。

标签: java arraylist


【解决方案1】:

在 Java 8 中你可以这样做:

e.removeIf(s -> Collections.frequency(e, s) > 1);

如果是 !Java 8,您可以创建 HashMap&lt;String, Integer&gt;。如果字符串已经出现在地图中,则将其 key 加一,否则,将其添加到地图中。

例如:

put("123", 1);

现在让我们假设您再次拥有“123”,您应该获取密钥的计数并将其加一:

put("123", get("aaa") + 1);

现在您可以轻松地在地图上进行迭代并创建一个新的数组列表,其中的键的值为

参考资料:

【讨论】:

  • Java 8 版本确实有效,但这只是因为它是一个 ArrayList,并且 removeIf 被覆盖以在最后批量删除所有内容。例如,它不适用于LinkedList
  • List::removeIf 是一个干净的解决方案,但它的复杂性是 O(n²) 因为迭代了 ListCollection::frequency 对吗?
  • 查找重复的复杂度是O(n²),应用removeIf只会增加一个常数时间。所以整体复杂度确实是 O(n²)。
  • 如果您创建一个Map&lt;String, Long&gt; 来计算出现次数,然后遍历EntrySet 以获得独特的元素,那么您就有了O(2*n) -&gt; O(n) 复杂性还是我错了?
  • @Taemyr 正确,但是在我知道的任何 hashmap 实现中 m 在 O(n) 中(我什至可以说这是唯一合理的选择),因为容量已调整为使其比当前大小大一些。
【解决方案2】:

你也可以在 Java 8 中使用filter

e.stream().filter(s -> Collections.frequency(e, s) == 1).collect(Collectors.toList())

【讨论】:

    【解决方案3】:

    您可以使用HashMap&lt;String, Integer&gt;

    您遍历列表,如果哈希映射不包含该字符串,则将其与值 1 一起添加。

    另一方面,如果您已经拥有该字符串,则只需递增计数器。因此,您的字符串的映射如下所示:

    {"123", 2}
    {"122", 1}
    {"125", 1}
    

    然后您将创建一个新列表,其中每个键的值为 1。

    【讨论】:

      【解决方案4】:

      这是一个非 Java 8 解决方案,使用映射来计算出现次数:

      Map <String,Integer> map = new HashMap<String, Integer>();
      for (String s : list){
          if (map.get(s) == null){
            map.put(s, 1);
          } 
          else {
            map.put(s, map.get(s) + 1);
          }
      }
      
      List<String> newList = new ArrayList<String>();
      
      // Remove from list if there are multiples of them.
      for (Map.Entry<String, String> entry : map.entrySet())
      {
        if(entry.getValue() > 1){
          newList.add(entry.getKey());
        }
      }
      
      list.removeAll(newList);
      

      【讨论】:

      • newList 添加 2 或更多的所有条目。这是一个临时列表。 list 是原始列表,所以为了“返回列表”,我通过删除所有 count = 1 的条目来修改这个列表。
      • 问题是删除count >= 2的。
      【解决方案5】:

      ArrayList 中的解决方案

      public static void main(String args[]) throws Exception {
            List<String> e = new ArrayList<String>();
            List<String> duplicate = new ArrayList<String>();
            e.add("123");
            e.add("122");
            e.add("125");
            e.add("123");
      
            for(String str : e){
                if(e.indexOf(str) != e.lastIndexOf(str)){
                    duplicate.add(str);
                }
            }
      
            for(String str : duplicate){
                e.remove(str);              
            }
      
            for(String str : e){
                System.out.println(str);
            }
        }
      

      【讨论】:

        【解决方案6】:

        使用流的最简单解决方案具有O(n^2) 时间复杂度。如果您在拥有数百万条目的List 上尝试它们,您将等待非常非常长的时间。 O(n) 的解决方案是:

        list = list.stream()
                   .collect(Collectors.groupingBy(Function.identity(), LinkedHashMap::new, Collectors.counting()))
                   .entrySet()
                   .stream()
                   .filter(e -> e.getValue() == 1)
                   .map(Map.Entry::getKey)
                   .collect(Collectors.toList());
        

        在这里,我使用了LinkedHashMap 来维护订单。请注意,静态导入可以简化 collect 部分。

        这太复杂了,我认为使用for 循环是解决这个问题的最佳选择。

        Map<String, Integer> map = new LinkedHashMap<>();
        for (String s : list)
            map.merge(s, 1, Integer::sum);
        list = new ArrayList<>();
        for (Map.Entry<String, Integer> e : map.entrySet())
            if (e.getValue() == 1)
                list.add(e.getKey());
        

        【讨论】:

        • 流复杂度也是O(2*n)O(n)
        • @Flown 它说O(n)
        • 你说的是O(n^2)
        • @Flown 我不是。它说使用流的最简单的解决方案是O(n^2)。我的解决方案不是最简单的。
        • 你也可以使用.collect(groupingBy(identity(), counting()))
        【解决方案7】:
        List<String> e = new ArrayList<String>();
        e.add("123");
        e.add("122");
        e.add("125");
        e.add("123");
        e.add("125");
        e.add("124");
        List<String> sortedList = new ArrayList<String>();
        for (String current : e){
            if(!sortedList.contains(current)){
                sortedList.add(current);
            }
            else{
                sortedList.remove(current);
            }
        }
        e.clear();
        e.addAll(sortedList);
        

        【讨论】:

          【解决方案8】:

          我是 Google Guava API 的粉丝。使用 Collections2 实用程序和通用 Predicate 实现,可以创建一个实用程序方法来覆盖多种数据类型。

          这假设有问题的对象有一个有意义的 .equals 实施

          @Test
              public void testTrimDupList() {
                  Collection<String> dups = Lists.newArrayList("123", "122", "125", "123");
                  dups = removeAll("123", dups);
                  Assert.assertFalse(dups.contains("123"));
          
                  Collection<Integer> dups2 = Lists.newArrayList(123, 122, 125,123);
                  dups2 = removeAll(123, dups2);
                  Assert.assertFalse(dups2.contains(123));
              }
          
              private <T> Collection<T> removeAll(final T element, Collection<T> collection) {
                  return Collections2.filter(collection, new Predicate<T>(){
                      @Override
                      public boolean apply(T arg0) {
                          return !element.equals(arg0);
                      }});
              }
          

          多考虑一下

          本页中的大多数其他示例都使用 java.util.List API 作为基础集合。我不确定这是否是有意的,但如果返回的元素必须是一个列表,则可以使用另一种中间方法,如下所示。多态性ftw!

          @Test
              public void testTrimDupListAsCollection() {
                  Collection<String> dups = Lists.newArrayList("123", "122", "125", "123");
                  //List used here only to get access to the .contains method for validating behavior.
                  dups = Lists.newArrayList(removeAll("123", dups)); 
                  Assert.assertFalse(dups.contains("123"));
          
                  Collection<Integer> dups2 = Lists.newArrayList(123, 122, 125,123);
                //List used here only to get access to the .contains method for validating behavior.
                  dups2 = Lists.newArrayList(removeAll(123, dups2));
                  Assert.assertFalse(dups2.contains(123));
              }
          
              @Test
              public void testTrimDupListAsList() {
                  List<String> dups = Lists.newArrayList("123", "122", "125", "123");
                  dups = removeAll("123", dups);
                  Assert.assertFalse(dups.contains("123"));
          
                  List<Integer> dups2 = Lists.newArrayList(123, 122, 125,123);
                  dups2 = removeAll(123, dups2);
                  Assert.assertFalse(dups2.contains(123));
              }
          
              private <T> List<T> removeAll(final T element, List<T> collection) {
                  return Lists.newArrayList(removeAll(element, (Collection<T>) collection));
          
              }
              private <T> Collection<T> removeAll(final T element, Collection<T> collection) {
                  return Collections2.filter(collection, new Predicate<T>(){
                      @Override
                      public boolean apply(T arg0) {
                          return !element.equals(arg0);
                      }});
              }
          

          【讨论】:

            【解决方案9】:

            类似这样的东西(使用Set):

            Set<Object> blackList = new Set<>()
            
            public void add(Object object) {
                if (blackList.exists(object)) {
                    return;
                }
                boolean notExists = set.add(object);
                if (!notExists) {
                   set.remove(object)
                   blackList.add(object);
                }
            }
            

            【讨论】:

            • 如果列表中有3乘以123怎么办?
            • 您将获得 1 个 123 的实例,它的好坏取决于您要完成和做什么。从技术上讲,当您一一插入值时,您将一一处理它们。因此,在第 2 次插入 123 之后,您删除了 123。您的收藏中没有 123,因此可以重新插入它。如果您想花哨,请使用黑名单
            • “你会得到 123 的 1 个实例,它的好坏取决于你想要完成和做的事情。” 但这不是 OP 想要做的.如果他的列表中有超过 1 次的字符串,他不希望它出现在最终列表中。
            • 你是对的,我的错!我已经更改了代码。它没有经过测试,但这个想法应该很明显
            【解决方案10】:

            如果您要进行设置,则可以通过两组来实现。保持其他集合中的重复值如下:

            List<String> duplicateList = new ArrayList<String>();
            
            duplicateList.add("123");
            duplicateList.add("122");
            duplicateList.add("125");
            duplicateList.add("123");
            duplicateList.add("127");
            duplicateList.add("127");
            
            System.out.println(duplicateList);
            
            Set<String> nonDuplicateList = new TreeSet<String>();
            Set<String> duplicateValues = new TreeSet<String>();
            
            if(nonDuplicateList.size()<duplicateList.size()){
                for(String s: duplicateList){
                    if(!nonDuplicateList.add(s)){
                        duplicateValues.add(s);
                    }
                }
            
                duplicateList.removeAll(duplicateValues);
            
                System.out.println(duplicateList);
                System.out.println(duplicateValues);
            }
            

            输出:原始列表:[123, 122, 125, 123, 127, 127]。删除后
            重复:[122, 125] 重复的值:[123, 127]


            注意:此解决方案可能未优化。您可能会找到更好的
            解决方案比这个。

            【讨论】:

              【解决方案11】:

              使用 Guava 库,使用 multiset 和流:

              e = HashMultiset.create(e).entrySet().stream()
                  .filter(me -> me.getCount() > 1)
                  .map(me -> me.getElement())
                  .collect(toList());
              

              这对于大型列表(O(n) 具有相当大的常数因子)来说非常漂亮且相当快。但它不会保留顺序(如果需要,可以使用LinkedHashMultiset)并且它会创建一个新的列表实例。

              这也很容易概括,例如,删除所有的三元组。

              一般来说,多集数据结构对于保存在一个工具箱中非常有用。

              【讨论】:

                猜你喜欢
                • 2014-03-25
                • 2018-05-29
                • 1970-01-01
                • 2019-02-23
                • 1970-01-01
                • 2010-12-25
                • 2015-12-12
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多