Java: Виявити дублікати в ArrayList?


104

Як я міг би розібратися (повернути true / false), чи містить ArrayList більше одного і того ж елемента на Java?

Велике спасибі, Террі

Редагувати Забув зазначити, що я шукаю не для порівняння "Блоків" один з одним, а їх цілих значень. Кожен "блок" має int, і саме це робить їх різними. Я знаходжу int певного Блоку, викликаючи метод під назвою "getNum" (наприклад, table1 [0] [2] .getNum ();


Якщо "Блок" порівнюється з int, ви, мабуть, повинні мати хеш-код повернути той самий int і мати рівний показник порівняння цих int.
— Пол Томблін

використовуйте Set замість List
— dmarquina

Відповіді:


192

Найпростіше: скиньте всю колекцію в Set (використовуючи конструктор Set (Collection) або Set.addAll), а потім подивіться, чи має набір такий же розмір, як ArrayList.

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

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

Оновлення: Якщо я правильно розумію ваше запитання, у вас є 2d масив Block, як в

Блокова таблиця [] [];

і ви хочете виявити, чи є в якомусь ряду з них дублікати?

У цьому випадку я міг би зробити наступне, припускаючи, що Блок правильно реалізує "рівний" та "хеш-код":

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повертає булеву помилкову, якщо доданий елемент вже є в наборі, тому ви можете навіть коротке замикання та вимкнути будь-яке додавання, яке повертається, falseякщо все, що ви хочете знати, чи є дублікати.


13
Не забудьте також застосувати hashCode / equals.
— jon077

1
Або навіть трохи простіше: оберніть його під час створення набору, наприклад нового HashSet (список), замість того, щоб використовувати addAll.
— Fabian Steeg

2
@ jon077: Це залежить від вашого визначення "дубліката".
— Майкл Майерс

Чи був би процес виявлення елементів у двовимірному масиві однаковий? Наприклад, перевірка від масиву [0] [0] до масиву [0] [6] (a 'рядок') ..? Велике спасибі, Террі

Кожен об'єкт у масиві містить ціле значення. За "дублікатом" об'єкт мав би те саме ціле значення.

60

Удосконалений код, використовуючи повернене значення Set#addзамість порівняння розміру списку та набору.

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());:? З огляду на параметр «Список», я думаю, що це більш ефективно, якщо для списку звичайно не містити дублікатів.
— Пол Джексон

1
@PaulJackson Розміри на основі повного списку, ймовірно, будуть корисними. Однак якщо загальним випадком є ​​те, що він повинен знайти копію на ранньому етапі, простір витрачено даремно. Також навіть HashSetвстановлення розміру до списку призведе до зміни розміру при виконанні всього списку через базовий коефіцієнт завантаження структури хеша.
— Джей Андерсон

1
Якщо у вас не виникають актуальні проблеми із часом виконання або простором, я б не зміцнював ваш код таким чином. Найчастіше уникати передчасної оптимізації.
— акун

15

Якщо ви хочете взагалі не мати дублікатів, вам слід просто вирізати середній процес виявлення дублікатів і використовувати набір .


1
Переконайтеся, що впровадити hashCode / equals :)
— jon077

@ jon077: Не обов’язково, як я щойно сказав.
— Майкл Майерс

1
Однак використання набору не виявляє дублікатів. Це їм просто заважає. Якщо, звичайно, ви не перевірите результат методу додавання, як зазначав @akuhn вище.
— 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

9

Якщо ваші елементи якимось чином порівнянні (той факт, що замовлення має будь-яке реальне значення, байдуже - воно просто повинно відповідати вашому визначенню рівності), найшвидше рішення про видалення дублікатів буде сортувати список (0 (n log ( n))) потім зробити один прохід і шукати повторювані елементи (тобто рівні елементи, що слідують один за одним) (це O (n)).

Загальна складність буде O (n log (n)), що приблизно відповідає тому, що ви отримаєте з Set (n разів довше (n)), але зі значно меншою постійною. Це пояснюється тим, що константа сортування / вирахування є результатом витрат на порівняння елементів, тоді як вартість з набору, швидше за все, є результатом обчислення хешу, плюс одне (можливо, кілька) порівнянь хешу. Якщо ви використовуєте хеш-реалізацію Set, тобто тому, що на основі Дерева ви отримаєте O (n log² (n)), що ще гірше.

Як я розумію, однак, вам не потрібно видаляти дублікати, а просто перевіряти їх існування. Таким чином, вам слід ввести в коді алгоритм сортування або об'єднання в масиві на своєму масиві, який просто закриває повернення true (тобто "є дуб"), якщо ваш порівняльник повертає 0, інакше завершує сортування, і проходить тестування відсортованого масиву для повторів. . У сортуванні або купі сортування, дійсно, коли сортування завершено, ви будете порівнювати кожну дублюючу пару, якщо обидва елементи вже не були у своїх остаточних положеннях (що малоймовірно). Таким чином, алгоритм налаштованого сортування повинен принести величезне поліпшення продуктивності (мені доведеться це довести, але я думаю, що алгоритм налаштованого повинен бути в O (log (n)) за рівномірно випадковими даними)


У цьому випадку n - 6, тому я б не витрачав багато часу на деталі реалізації, але я буду тримати ваше уявлення про особливий сорт купи, якщо мені коли-небудь потрібно щось робити.
— Пол Томблін

Я не розумію третього абзацу. Як ви пишете, Mergesort і гипсорт - це O (nlog (n)), а не O (log (n)); навіть якщо ви виходите, як тільки ви виявите дублікат, це все ще не змінює вашу часову складність ...
— ChaimKut

8

Мені потрібно було зробити аналогічну операцію для Stream, але не вдалося знайти хорошого прикладу. Ось що я придумав.

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

Це має перевагу короткого замикання, коли дублікати знаходять рано, а не обробляти весь потік, і це не набагато складніше, ніж просто поставити все в Setі перевірити розмір. Таким чином, приблизно це буде:

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

7

З Java 8+ ви можете використовувати Stream API:

boolean areAllDistinct(List<Block> blocksList) {
    return blocksList.stream().map(Block::getNum).distinct().count() == blockList.size();
}

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);

Просто надрукуйте масив результатів і подивіться результат без дублікатів :)


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);
    }
}

І, мабуть, також подумайте про обрізання значень або використання малих літер ... залежно від вашого випадку


Найпростіша і найкраща відповідь, якщо ви хочете дублікати, для виконання ви можете запустити унікальний підказку з розміром аргументів.
— Крістоф Руссі

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. Через необ’єктивні запитання, задані в інтерв'ю для видалення дублікатів зі списку, не використовуючи жодної іншої колекції, наведеним вище прикладом є відповідь. У реальному світі, однак, якщо мені доведеться цього досягти, я буду проставляти елементи зі списку до набору, просто!


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);

Додайте пояснення з відповіддю, як ця відповідь допоможе ОП у вирішенні поточного питання
— ρяσѕρєя K

0

Ця відповідь написана в Котліні, але її легко перекласти на Java.

Якщо розмір вашого арабського представника знаходиться в межах фіксованого невеликого діапазону, то це чудове рішення.

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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.