Java:在ArrayList中检测重复项?


104

我如何去检测(返回true / false)ArrayList是否包含Java中多个相同元素?

非常感谢,特里

编辑 忘记提及我不是要相互比较“块”,而是要比较它们的整数值。每个“块”都有一个整数,这就是它们与众不同的原因。我通过调用名为“ getNum”的方法(例如,table1 [0] [2] .getNum();)来找到特定Block的int。


如果将“ Block”与一个int进行比较,则可能应该让hashCode返回相同的int并具有等于这些int的相等值。
Paul Tomblin,2009年

使用设置代替列表
dmarquina

Answers:


192

最简单:将整个集合转储到Set中(使用Set(Collection)构造函数或Set.addAll),然后查看Set是否具有与ArrayList相同的大小。

List<Integer> list = ...;
Set<Integer> set = new HashSet<Integer>(list);

if(set.size() < list.size()){
    /* There are duplicates */
}

更新:如果我正确地理解了您的问题,那么您有一个二维的Block数组,如下所示

块表[] [];

并且您想检测其中是否有重复的行?

在那种情况下,假设Block正确实现了“ equals”和“ hashCode”,我可以执行以下操作:

for (Block[] row : table) {
   Set set = new HashSet<Block>(); 
   for (Block cell : row) {
      set.add(cell);
   }
   if (set.size() < 6) { //has duplicate
   }
}

我不确定语法是否100%,因此将其编写为可能更安全

for (int i = 0; i < 6; i++) {
   Set set = new HashSet<Block>(); 
   for (int j = 0; j < 6; j++)
    set.add(table[i][j]);
 ...

Set.add如果要添加的项已经在集合中,则返回boolean false,因此false如果您想知道是否存在重复项,您甚至可以短路并打包返回的任何添加项。


13
确保也实现hashCode / equals。
jon077

1
甚至更简单:在创建集合时将其包装,例如新建HashSet(list),而不是使用addAll。
Fabian Steeg

2
@ jon077:这取决于您对“重复”的定义。
迈克尔·迈尔斯

检测2D数组中元素的过程是否相同?例如,从array [0] [0]到array [0] [6](“行”)进行检查。非常感谢Terry

数组中的每个对象都有一个整数值。通过“重复”,对象将具有相同的整数值。

60

改进的代码,使用返回值of Set#add而不是比较list和set的大小。

public static <T> boolean hasDuplicate(Iterable<T> all) {
    Set<T> set = new HashSet<T>();
    // Set#add returns false if the set does not change, which
    // indicates that a duplicate element has been added.
    for (T each: all) if (!set.add(each)) return true;
    return false;
}

7
难道是更有效地告诉HashSet的多少空间分配:Set<T> set = new HashSet<T>(list.size());?给定一个List参数,我认为如果列表不包含重复项是很有效的。
保罗·杰克逊

1
基于完整列表的@PaulJackson大小调整可能会有所帮助。但是,如果通常的情况是要尽早找到副本,则浪费了空间。同样HashSet,由于哈希结构的基础加载因素,甚至将s的大小调整为列表大小也将导致在遍历整个列表时调整大小。
杰·安德森

1
除非您遇到运行时或空间方面的实际问题,否则我不会那样微调您的代码。最好避免过早的优化。
阿库恩

15

如果您希望完全避免重复,那么您应该删除检测重复的中间过程,并使用Set


1
确保实现hashCode / equals :)
jon077

@ jon077:不一定,正如我刚才所说。
迈克尔·迈尔斯

1
但是,使用Set不会检测到重复项。它只是防止它们。当然,除非您按照上面@akuhn的说明检查add方法的结果。
mcallahan

13

改进的代码可返回重复元素

  • 可以在集合中找到重复项
  • 返回重复项
  • 可以从集合中获得唯一元素

public static <T> List getDuplicate(Collection<T> list) {

    final List<T> duplicatedObjects = new ArrayList<T>();
    Set<T> set = new HashSet<T>() {
    @Override
    public boolean add(T e) {
        if (contains(e)) {
            duplicatedObjects.add(e);
        }
        return super.add(e);
    }
    };
   for (T t : list) {
        set.add(t);
    }
    return duplicatedObjects;
}


public static <T> boolean hasDuplicate(Collection<T> list) {
    if (getDuplicate(list).isEmpty())
        return false;
    return true;
}

太棒了 您有一些无效的代码,也许这不是最佳方法,但是您的方法完全不可行!(效果很好)
Jules Colle,2012年

9

如果您的元素在某种程度上是可比的(该顺序具有任何实际含义的事实并不重要-它只需要与您对相等性的定义保持一致),则最快的重复删除解决方案将对列表进行排序(0(n log( n)))然后进行单遍查找重复元素(即彼此跟随的相等元素)(这是O(n))。

整体复杂度将为O(n log(n)),与使用Set(n倍long(n))所得到的结果大致相同,但常数要小得多。这是因为排序/重复的常量是由比较元素的成本引起的,而集合中的成本最有可能是由哈希计算以及一个(可能是多个)哈希比较得出的。如果您使用的是基于散列的Set实现,那是因为基于Tree的树将为您提供O(nlog²(n)),甚至更糟。

但是据我了解,您不需要删除重复项,而只需测试它们的存在。因此,您应该在阵列上手动编码合并或堆排序算法,如果比较器返回0,则退出返回true(即“ dup”),否则就完成排序,并遍历排序的数组测试是否重复。实际上,在合并或堆排序中,当排序完成时,您将比较每个重复对,除非两个元素都已经处于其最终位置(这不太可能)。因此,经过调整的排序算法应该会产生巨大的性能改进(我必须证明这一点,但是我想经过调整的算法应该在均匀随机数据的O(log(n))中)


在这种情况下,n为6,因此我不会在实现细节上浪费很多时间,但是如果我需要执行类似的操作,我将保留您对特殊堆排序的想法。
Paul Tomblin,2009年

我不明白第三段。在您编写时,Mergesort和heapsort均为O(nlog(n)),而不是O(log(n))。即使您一旦识别出重复项就退出,这仍然不会改变您的时间复杂度……
ChaimKut 2012年

8

我需要对进行类似的操作Stream,但找不到一个很好的例子。这是我想出的。

public static <T> boolean areUnique(final Stream<T> stream) {
    final Set<T> seen = new HashSet<>();
    return stream.allMatch(seen::add);
}

这样做的优点是,尽早发现重复项时会发生短路,而不是必须处理整个流,并且这样做并不比将所有内容都放入a Set并检查大小而复杂得多。因此,这种情况大致为:

List<T> list = ...
boolean allDistinct = areUnique(list.stream());


2

简而言之:1)确保所有项目都是可比较的2)对数组进行排序2)对数组进行迭代并查找重复项


1

要了解列表中的重复项,请使用以下代码:它将为您提供包含重复项的集合。

 public Set<?> findDuplicatesInList(List<?> beanList) {
    System.out.println("findDuplicatesInList::"+beanList);
    Set<Object> duplicateRowSet=null;
    duplicateRowSet=new LinkedHashSet<Object>();
            for(int i=0;i<beanList.size();i++){
                Object superString=beanList.get(i);
                System.out.println("findDuplicatesInList::superString::"+superString);
                for(int j=0;j<beanList.size();j++){
                    if(i!=j){
                         Object subString=beanList.get(j);
                         System.out.println("findDuplicatesInList::subString::"+subString);
                         if(superString.equals(subString)){
                             duplicateRowSet.add(beanList.get(j));
                         }
                    }
                }
            }
            System.out.println("findDuplicatesInList::duplicationSet::"+duplicateRowSet);
        return duplicateRowSet;
  }

1

解决此问题的最佳方法是使用HashSet

ArrayList<String> listGroupCode = new ArrayList<>();
listGroupCode.add("A");
listGroupCode.add("A");
listGroupCode.add("B");
listGroupCode.add("C");
HashSet<String> set = new HashSet<>(listGroupCode);
ArrayList<String> result = new ArrayList<>(set);

只需打印 结果 arraylist并查看没有重复的结果即可:)


1

如果要使用重复值集:

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class FindDuplicateInArrayList {

    public static void main(String[] args) {

        Set<String> uniqueSet = new HashSet<String>();
        List<String> dupesList = new ArrayList<String>();
        for (String a : args) {
            if (uniqueSet.contains(a))
                dupesList.add(a);
            else
                uniqueSet.add(a);
        }
        System.out.println(uniqueSet.size() + " distinct words: " + uniqueSet);
        System.out.println(dupesList.size() + " dupesList words: " + dupesList);
    }
}

可能还会考虑修剪值或使用小写字母...视您的情况而定。


如果您想要重复,则是最简单,最好的答案,为了提高性能,您可以使用args大小初始化uniqueSet提示。
Christophe Roussy

0
    String tempVal = null;
    for (int i = 0; i < l.size(); i++) {
        tempVal = l.get(i); //take the ith object out of list
        while (l.contains(tempVal)) {
            l.remove(tempVal); //remove all matching entries
        }
        l.add(tempVal); //at last add one entry
    }

注意:尽管从列表的开头删除了项目,但这将对性能造成重大影响。为了解决这个问题,我们有两个选择。1)以相反的顺序迭代并删除元素。2)使用LinkedList而不是ArrayList。由于在访谈中提出的偏见性问题是在不使用任何其他集合的情况下从List中删除重复项,因此上述示例就是答案。但是,在现实世界中,如果必须实现这一点,我将把List到Set中的元素简单化!


0
/**
     * Method to detect presence of duplicates in a generic list. 
     * Depends on the equals method of the concrete type. make sure to override it as required.
     */
    public static <T> boolean hasDuplicates(List<T> list){
        int count = list.size();
        T t1,t2;

        for(int i=0;i<count;i++){
            t1 = list.get(i);
            for(int j=i+1;j<count;j++){
                t2 = list.get(j);
                if(t2.equals(t1)){
                    return true;
                }
            }
        }
        return false;
    }

已覆盖的具体类的示例equals()

public class Reminder{
    private long id;
    private int hour;
    private int minute;

    public Reminder(long id, int hour, int minute){
        this.id = id;
        this.hour = hour;
        this.minute = minute;
    }

    @Override
    public boolean equals(Object other){
        if(other == null) return false;
        if(this.getClass() != other.getClass()) return false;
        Reminder otherReminder = (Reminder) other;
        if(this.hour != otherReminder.hour) return false;
        if(this.minute != otherReminder.minute) return false;

        return true;
    }
}

0
    ArrayList<String> withDuplicates = new ArrayList<>();
    withDuplicates.add("1");
    withDuplicates.add("2");
    withDuplicates.add("1");
    withDuplicates.add("3");
    HashSet<String> set = new HashSet<>(withDuplicates);
    ArrayList<String> withoutDupicates = new ArrayList<>(set);

    ArrayList<String> duplicates = new ArrayList<String>();

    Iterator<String> dupIter = withDuplicates.iterator();
    while(dupIter.hasNext())
    {
    String dupWord = dupIter.next();
    if(withDuplicates.contains(dupWord))
    {
        duplicates.add(dupWord);
    }else{
        withoutDupicates.add(dupWord);
    }
    }
  System.out.println(duplicates);
  System.out.println(withoutDupicates);

与答案添加一些解释如何回答帮助OP在固定电流问题
ρяσѕρєяķ

0

这个答案在Kotlin中写得很清楚,但是可以很容易地翻译成Java。

如果arraylist的大小在固定的小范围内,那么这是一个很好的解决方案。

var duplicateDetected = false
    if(arrList.size > 1){
        for(i in 0 until arrList.size){
            for(j in 0 until arrList.size){
                if(i != j && arrList.get(i) == arrList.get(j)){
                    duplicateDetected = true
                }
            }
        }
    }

0
private boolean isDuplicate() {
    for (int i = 0; i < arrayList.size(); i++) {
        for (int j = i + 1; j < arrayList.size(); j++) {
            if (arrayList.get(i).getName().trim().equalsIgnoreCase(arrayList.get(j).getName().trim())) {
                return true;
            }
        }
    }

    return false;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.