从 ArrayList 中删除重复值

Posted

技术标签:

【中文标题】从 ArrayList 中删除重复值【英文标题】:Removing Duplicate Values from ArrayList 【发布时间】:2014-03-25 23:25:33 【问题描述】:

我有一个字符串数组列表,并在其中添加了一些重复值。我只想删除那个重复值那么如何删除它。

这里的例子我有一个想法。

List<String> list = new ArrayList<String>();
        list.add("Krishna");
        list.add("Krishna");
        list.add("Kishan");
        list.add("Krishn");
        list.add("Aryan");
        list.add("Harm");

        System.out.println("List"+list);

        for (int i = 1; i < list.size(); i++) 
            String a1 = list.get(i);
            String a2 = list.get(i-1);
            if (a1.equals(a2)) 
                list.remove(a1);
            
        

        System.out.println("List after short"+list);

但是有没有足够的方法删除重复的表单列表。不使用 For 循环? 而且我可以通过使用 HashSet 或其他方式来做到这一点,但只使用数组列表。 想听听你的建议。提前感谢您的回答。

【问题讨论】:

是否有理由不使用 for 循环或哈希集。 好吧,您可以重写 for 循环以使用 while 循环或递归,但我怀疑这不是您的想法。一些上下文(解释为什么你想以你想做的方式做某事)通常有助于解释 - 就目前而言,我可以对你想要什么做出一些猜测,但这就是他们的全部 -猜测。而且您的代码只会删除彼此相邻的重复项 - 这是您想要的吗? 【参考方案1】:

您可以从列表中创建LinkedHashSetLinkedHashSet 将只包含每个元素一次,并且与List 的顺序相同。然后从这个LinkedHashSet 创建一个新的List。如此有效,它是一条线:

list = new ArrayList<String>(new LinkedHashSet<String>(list))

任何涉及List#containsList#remove 的方法都可能将渐近运行时间从O(n)(如上例)减少到O(n^2)。


EDIT 对于评论中提到的要求:如果您想删除重复的元素,但考虑字符串为 equal 忽略大小写,那么您可以执行类似的操作这个:

Set<String> toRetain = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
toRetain.addAll(list);
Set<String> set = new LinkedHashSet<String>(list);
set.retainAll(new LinkedHashSet<String>(toRetain));
list = new ArrayList<String>(set);

它将有 O(n*logn) 的运行时间,这仍然比许多其他选项要好。请注意,这看起来比实际情况要复杂一些:我假设列表中元素的 order 可能不会更改。如果列表中元素的顺序无关紧要,您可以简单地做

Set<String> set = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
set.addAll(list);
list = new ArrayList<String>(set);

【讨论】:

+1 表示正确答案。但仍有一个问题,如果我想忽略大小写敏感,那该怎么办? @Krishna 如果您想忽略字符串的大小写,那么您应该在问题中已经提到这一点。但是,我也插入了涵盖此案例的 EDIT。 Great Dude,其绝对正确的答案让人眼前一亮。 :) 对象有可能吗?我的意思是我有 ArrayList 这种类型的数组列表,我想从中删除重复值 @Vaishali 然后,ModelGroup 类必须正确实现hashCodeequals 方法。网上有很多关于这方面的资源。【参考方案2】:

如果您只想使用 arraylist,那么我担心没有更好的方法可以带来巨大的性能优势。但是通过只使用arraylist,我会在添加到列表之前进行检查,如下所示

void addToList(String s)
  if(!yourList.contains(s))
       yourList.add(s);

在这种情况下使用 Set 是合适的。

【讨论】:

是的,可以将其转换为列表但仍然不能令人满意的答案。甚至 +1 表示正确 我认为这里的关键问题是是否应将删除视为一次性“清理”操作,或者确保每个元素仅包含一次是否可行。例如,使用您的方法插入 n 元素的运行时间为 O(n^2) ...【参考方案3】:

您可以使用 Google Guava 实用程序,如下所示

 list = ImmutableSet.copyOf(list).asList(); 

这可能是从列表中消除重复项的最有效方法,有趣的是,它还保留迭代顺序。

更新

但是,如果您不想涉及 Guava,则可以删除重复项,如下所示。

ArrayList<String> list = new ArrayList<String>();
    list.add("Krishna");
    list.add("Krishna");
    list.add("Kishan");
    list.add("Krishn");
    list.add("Aryan");
    list.add("Harm");

System.out.println("List"+list);
HashSet hs = new HashSet();
hs.addAll(list);
list.clear();
list.addAll(hs);

但是,当然,这会破坏 ArrayList 中元素的迭代顺序。

希希尔

【讨论】:

您的 ans 实际上是正确的,但添加 jar 文件仅用于一种用途,无论如何听起来都不是更好,为 ans +1。 @Krishna:Java 的 Guava 库非常有用且功能强大。它不是关于添加一个新的 JAR 来完成一项小任务,而是关于使用最少的代码可以如何智能和最有效地完成一项任务。 这个 *** 问题列出了 Guava 的一些好处。 ***.com/questions/3759440/… 正如你所说,它会破坏迭代顺序。 这个的可变版本怎么样?如果我想在删除重复项后将数据添加到列表中怎么办? ImmutableSet 抛出异常【参考方案4】:

Java 8 流函数

您可以使用上面的distinct 函数来获取列表的不同元素,

stringList.stream().distinct();

从文档中,

返回由该流的不同元素组成的流(根据 Object.equals(Object))。


另外一种方法,如果你不想使用equals方法,可以使用collect这样的函数,

stringList.stream()  
    .collect(Collectors.toCollection(() -> 
        new TreeSet<String>((p1, p2) -> p1.compareTo(p2)) 
));  

从文档中,

使用收集器对此流的元素执行可变归约操作。

希望对您有所帮助。

【讨论】:

如果我们使用 Java 8 就可以了吗? 在元素流上使用函数式操作可以做更多事情。你可以阅读this 和this 或者你可以谷歌..【参考方案5】:

从列表中删除重复项的简单函数

private void removeDuplicates(List<?> list)

    int count = list.size();

    for (int i = 0; i < count; i++) 
    
        for (int j = i + 1; j < count; j++) 
        
            if (list.get(i).equals(list.get(j)))
            
                list.remove(j--);
                count--;
            
        
    

示例: 输入:[1, 2, 2, 3, 1, 3, 3, 2, 3, 1, 2, 3, 3, 4, 4, 4, 1] 输出:[1,2,3,4]

【讨论】:

【参考方案6】:
List<String> list = new ArrayList<String>();
        list.add("Krishna");
        list.add("Krishna");
        list.add("Kishan");
        list.add("Krishn");
        list.add("Aryan");
        list.add("Harm");

HashSet<String> hs=new HashSet<>(list);

System.out.println("=========With Duplicate Element========");
System.out.println(list);
System.out.println("=========Removed Duplicate Element========");
System.out.println(hs);

【讨论】:

它是对的,但只需将 Krishna 更改为 krishna,然后它就会给出两个答案。但我想忽略区分大小写。现在试试吧。【参考方案7】:

我不认为list = new ArrayList&lt;String&gt;(new LinkedHashSet&lt;String&gt;(list)) 不是最好的方法,因为我们使用的是LinkedHashset(我们可以直接使用LinkedHashset 而不是ArrayList),

解决方案:

import java.util.ArrayList;
public class Arrays extends ArrayList

@Override
public boolean add(Object e) 
    if(!contains(e))
        return super.add(e);
    else
        return false;
    


public static void main(String[] args) 
    Arrays element=new Arrays();
    element.add(1);
    element.add(2);
    element.add(2);
    element.add(3);

    System.out.println(element);


输出: [1, 2, 3]

在这里我扩展了 ArrayList ,因为我通过覆盖 add 方法来使用它并进行一些更改。

【讨论】:

我已经有了 ans ManojKumar 任何方式供您尝试 +1 :)【参考方案8】:
     public List<Contact> removeDuplicates(List<Contact> list) 
    // Set set1 = new LinkedHashSet(list);
    Set set = new TreeSet(new Comparator() 
        @Override
        public int compare(Object o1, Object o2) 
                 if(((Contact)o1).getId().equalsIgnoreCase(((Contact)2).getId()) ) 
                return 0;
            
            return 1;
        
    );
    set.addAll(list);
    final List newList = new ArrayList(set);
    return newList;

【讨论】:

【参考方案9】:

这将是最好的方法

    List<String> list = new ArrayList<String>();
    list.add("Krishna");
    list.add("Krishna");
    list.add("Kishan");
    list.add("Krishn");
    list.add("Aryan");
    list.add("Harm");

    Set<String> set=new HashSet<>(list);

【讨论】:

他只是说他只想使用 ArrayList。 “你可以使用 HashSet 或其他方式,但只使用数组列表。” 此外,此解决方案不会保留插入顺序;你需要一个LinkedHashSet【参考方案10】:

最好使用HastSet

1-a) HashSet 包含一组对象,但它允许您轻松快速地确定对象是否已经在集合中。它通过在内部管理一个数组并使用从对象的哈希码计算的索引来存储对象来实现这一点。看这里

1-b) HashSet 是一个包含唯一元素的无序集合。它具有标准的集合操作 Add、Remove、Contains,但由于它使用基于散列的实现,这些操作是 O(1)。 (与 List 相比,Contains 和 Remove 是 O(n)。)HashSet 还提供标准集合操作,例如并集、交集和对称差分。看这里

2) 集合有不同的实现。有些通过散列元素使插入和查找操作超快。但是,这意味着添加元素的顺序会丢失。其他实现以较慢的运行时间为代价保留了添加的顺序。

C# 中的 HashSet 类采用第一种方法,因此不保留元素的顺序。它比常规列表快得多。一些基本的基准测试表明 HashSet 在处理主要类型(int、double、bool 等)时要快得多。使用类对象时速度要快得多。所以这一点是 HashSet 很快。

HashSet 的唯一问题是不能通过索引访问。要访问元素,您可以使用枚举器或使用内置函数将 HashSet 转换为 List 并对其进行迭代。看这里

【讨论】:

但是 HashSet 不保留顺序,OP 也不想使用 HashSet。 使用不可变集副本。它保留了http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/collect/ImmutableSet.html 的顺序 好吧,但那会加载更多,你这实际上是正确的,但是如果我将树集转换为树集,它会给我相同的顺序,甚至不允许重复。 :) 是的,Treeset 保证基本操作(添加、删除和包含)的 log(n) 时间成本保证集合的元素将被排序(升序、自然或您通过它指定的那个) constructor) 不提供任何用于迭代性能的调整参数 提供了一些方便的方法来处理有序集,如 first()、last()、headSet() 和 tailSet() 等【参考方案11】:

没有循环,!由于ArrayList 是按顺序而不是按键索引的,所以如果不遍历整个列表,您将无法找到目标元素。

一个好的编程习惯是选择合适的数据结构来适应你的场景。因此,如果 Set 最适合您的方案,那么讨论使用 List 实现它并尝试找到使用不正确数据结构的最快方法是没有意义的。

【讨论】:

可能你不知道。只需扔掉linkedHashset或treeset,你就会得到答案。我说不使用 for 循环我没有说不使用转换。 :) 但是你说只使用数组列表。此约束意味着转到其他数据结构不符合您的要求。 :) @克里希纳【参考方案12】:
public static void main(String[] args) 
    @SuppressWarnings("serial")
    List<Object> lst = new ArrayList<Object>() 
        @Override
        public boolean add(Object e) 
            if(!contains(e))
            return super.add(e);
            else
            return false;
        
    ;
    lst.add("ABC");
    lst.add("ABC");
    lst.add("ABCD");
    lst.add("ABCD");
    lst.add("ABCE");
    System.out.println(lst);


这是更好的方法

【讨论】:

【参考方案13】:

list = list.stream().distinct().collect(Collectors.toList()); 这可能是使用 Java8 Stream API 的解决方案之一。希望这可以帮助。

【讨论】:

【参考方案14】:
 public void removeDuplicates() 
    ArrayList<Object> al = new ArrayList<Object>();
    al.add("java");
    al.add('a');
    al.add('b');
    al.add('a');
    al.add("java");
    al.add(10.3);
    al.add('c');
    al.add(14);
    al.add("java");
    al.add(12);

    System.out.println("Before Remove Duplicate elements:" + al);
    for (int i = 0; i < al.size(); i++) 
        for (int j = i + 1; j < al.size(); j++) 
            if (al.get(i).equals(al.get(j))) 
                al.remove(j);
                j--;
            
        
    
    System.out.println("After Removing duplicate elements:" + al);

删除重复元素之前:

[java, a, b, a, java, 10.3, c, 14, java, 12]

删除重复元素后:

[java, a, b, 10.3, c, 14, 12]

【讨论】:

【参考方案15】:

使用 java 8:

public static <T> List<T> removeDuplicates(List<T> list) 
    return list.stream().collect(Collectors.toSet()).stream().collect(Collectors.toList());

【讨论】:

【参考方案16】:

如果您只需要使用 ArrayList 删除重复项,而不使用其他 Collection 类,那么:-

//list is the original arraylist containing the duplicates as well
List<String> uniqueList = new ArrayList<String>();
    for(int i=0;i<list.size();i++) 
        if(!uniqueList.contains(list.get(i)))
            uniqueList.add(list.get(i));
    

希望这会有所帮助!

【讨论】:

【参考方案17】:
private static void removeDuplicates(List<Integer> list)

    Collections.sort(list);
    int count = list.size();
    for (int i = 0; i < count; i++) 
    
        if(i+1<count && list.get(i)==list.get(i+1))
            list.remove(i);
            i--;
            count--;
        
    

【讨论】:

【参考方案18】:
public static List<String> removeDuplicateElements(List<String> array)
    List<String> temp = new ArrayList<String>();
    List<Integer> count = new ArrayList<Integer>();
    for (int i=0; i<array.size()-2; i++)
        for (int j=i+1;j<array.size()-1;j++)
            
                if (array.get(i).compareTo(array.get(j))==0) 
                    count.add(i);
                    int kk = i;
                
            
        
        for (int i = count.size()+1;i>0;i--) 
            array.remove(i);
        
        return array;
    

【讨论】:

以上是关于从 ArrayList 中删除重复值的主要内容,如果未能解决你的问题,请参考以下文章

如何从整数arraylist.ANDROID中删除值[重复]

使用 For 循环从 ArrayList 中删除数据

从 ArrayList 中删除项目时出错 [重复]

从单个方法返回 ArrayList 和 int 值。可能吗?

从 ArrayList 的 ArrayList 中删除重复项 [重复]

如何使用原始元素删除 ArrayList 中的重复值[重复]