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.