Як видалити повторні елементи з ArrayList?


Відповіді:


991

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

Set<String> set = new HashSet<>(yourList);
yourList.clear();
yourList.addAll(set);

Звичайно, це руйнує впорядкованість елементів у ArrayList.


260
Дивіться також LinkedHashSet, якщо ви хочете зберегти замовлення.
залп

3
@Chetan знаходить усі дублікати з ArrayList в O (n), важливо мати правильно визначений метод рівних для об’єктів, які у вас є в списку (немає проблеми для чисел): public Set<Object> findDuplicates(List<Object> list) { Set<Object> items = new HashSet<Object>(); Set<Object> duplicates = new HashSet<Object>(); for (Object item : list) { if (items.contains(item)) { duplicates.add(item); } else { items.add(item); } } return duplicates; }
Ondrej Bozek

4
Доброю практикою було б визначення змінних за допомогою типів інтерфейсу Listта Set(замість типів реалізації ArrayListта HashSetяк у вашому прикладі).
Джонік

33
Ви можете очистити це за допомогою, new HashSet(al)замість того, щоб ініціалізувати його до порожнього та виклику addAll.
ashes999

1
чи можу я додати правила встановлення дубліката для мене? Наприклад: коли у мене Objectє кілька значень, якщо два з них повторюються, я вважаю їх дублікатами (інші значення можуть бути різними) і використовувати Set?
jean d'arme

290

Незважаючи на те, перетворюючи ArrayListдо HashSetефективно видаляє дублікати, якщо вам потрібно , щоб зберегти порядок вставки, я волів би запропонувати вам використовувати цей варіант

// list is some List of Strings
Set<String> s = new LinkedHashSet<>(list);

Потім, якщо вам потрібно повернути Listпосилання, ви можете знову використовувати конструктор перетворення.


10
Чи надає LinkedHashSet якісь гарантії щодо того, який із кількох дублікатів зберігається у списку? Наприклад, якщо позиції 1, 3 та 5 є дублікатами у вихідному списку, чи можна вважати, що цей процес видалить 3 та 5? А може, видалити 1 і 3? Дякую.
Метт Брайансон

16
@Matt: так, це гарантує це. У документах кажуть: «Це пов'язаний список визначає порядок ітерації, який є порядком , в якому елементи були вставлені в набір (вставка порядок) Зверніть увагу , що порядок вставки не впливає , якщо елемент повторно вставлено в набір.» .
абахгат

Дуже цікаво. У мене тут інша ситуація. Я не намагаюся сортувати String, але інший об’єкт, який називається AwardYearSource. Цей клас має атрибут int під назвою year. Тому я хочу видалити дублікати на основі року. тобто якщо в 2010 році згадувалося не раз, я хочу видалити цей об’єкт AwardYearSource. Як я можу це зробити?
WowBow

@WowBow Наприклад, ви можете визначити об'єкт Wrapper, який містить AwardYearSource. І визначити цей об'єкт Wrapper дорівнює методу, заснованому на полі року YearYearSources. Тоді ви можете використовувати Set із цими об'єктами Wrapper.
Ондрей Божек

@WowBow або впроваджуємо Comparable / Comparator
shrini1000

134

На Java 8:

List<String> deduped = list.stream().distinct().collect(Collectors.toList());

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


1
Як я можу зробити це для відмінних від випадків випадків?
StackFlowed

@StackFlowed Якщо вам не потрібно , щоб зберегти порядок в списку ви можете addAllв new TreeSet<String>(String.CASE_INSENSITIVE_ORDER). Перший доданий елемент залишиться у наборі, тому якщо ваш список містить "Собака" та "Собака" (у такому порядку), він TreeSetбуде містити "Собаку". Якщо замовлення має бути збережене, то перед рядком у відповіді list.replaceAll(String::toUpperCase);.
Павло

1
Я отримую цю помилку: несумісні типи: Список <Object> не може бути перетворений у Список <String>
Самір

Це взагалі просте рішення, але як видалити дублікати з Arraylist of int []?
Програміст Nooby

56

Припустимо, у нас є такий список String:

List<String> strList = new ArrayList<>(5);
// insert up to five items to list.        

Тоді ми можемо видалити повторювані елементи кількома способами.

До Java 8

List<String> deDupStringList = new ArrayList<>(new HashSet<>(strList));

Примітка: Якщо ми хочемо підтримувати порядок вставки, тоді нам потрібно використовувати LinkedHashSetзамістьHashSet

Використання Guava

List<String> deDupStringList2 = Lists.newArrayList(Sets.newHashSet(strList));

Використання Java 8

List<String> deDupStringList3 = strList.stream().distinct().collect(Collectors.toList());

Примітка. Якщо ми хочемо зібрати результат у певній реалізації списку, наприклад, LinkedListтоді ми можемо змінити наведений вище приклад:

List<String> deDupStringList3 = strList.stream().distinct()
                 .collect(Collectors.toCollection(LinkedList::new));

Ми також можемо використовувати parallelStreamі у наведеному вище коді, але це може не дати очікуваних переваг для виконання. Перевірте це питання для отримання додаткової інформації.


Так, коли я друкував свої попередні коментарі, я мав враження, що parallel streamsзавжди даватиме кращі результати. Але це міф. Пізніше я дізнався, що існують певні сценарії, де слід використовувати паралельні потоки. У цьому сценарії паралельні потоки не дадуть кращих показників. і так, паралельні потоки можуть не дати бажаних результатів у деяких випадках. List<String> deDupStringList3 = stringList.stream().map(String::toLowerCase).distinct().collect(Collectors.toList());має бути підходящим рішенням у цьому випадку
Діабло

53

Якщо ви не хочете копій, використовуйте набір замість List. Для перетворення a Listв a Setви можете скористатися наступним кодом:

// list is some List of Strings
Set<String> s = new HashSet<String>(list);

Якщо це дійсно необхідно, ви можете використовувати ту саму конструкцію, щоб перетворити Setспину в List.


Аналогічно в нижній частині теми я дав відповідь, де я використовую Set for Custom Object. У випадку, якщо у когось є спеціальний об’єкт на зразок "Контакт" або "Студент", можна використовувати ту відповідь, яка для мене добре працює.
Мухаммед Аділь

Проблема виникає, коли вам потрібно спеціально отримати доступ до елемента. Наприклад, прив'язуючи об'єкт до перегляду елемента списку в Android, вам присвоюється його індекс. Тому Setтут не можна використовувати
TheRealChx101

Як я можу привласнити це, коли в списку є об’єктний список
jvargas

28

Ви також можете це зробити і зберегти порядок:

// delete duplicates (if any) from 'myArrayList'
myArrayList = new ArrayList<String>(new LinkedHashSet<String>(myArrayList));

Я думаю, що це найкращий спосіб видалення дублікатів у ArrayList. Однозначно рекомендується. Дякую @Nenad за відповідь.
ByWaleed

25

Потоки Java 8 забезпечують дуже простий спосіб видалення повторюваних елементів зі списку. Використовуючи виразний метод. Якщо у нас є список міст і ми хочемо видалити дублікати зі цього списку, це можна зробити в одному рядку -

 List<String> cityList = new ArrayList<>();
 cityList.add("Delhi");
 cityList.add("Mumbai");
 cityList.add("Bangalore");
 cityList.add("Chennai");
 cityList.add("Kolkata");
 cityList.add("Mumbai");

 cityList = cityList.stream().distinct().collect(Collectors.toList());

Як видалити повторювані елементи з масиву


25

Ось спосіб, який не впливає на впорядкування вашого списку:

ArrayList l1 = new ArrayList();
ArrayList l2 = new ArrayList();

Iterator iterator = l1.iterator();

while (iterator.hasNext()) {
    YourClass o = (YourClass) iterator.next();
    if(!l2.contains(o)) l2.add(o);
}

l1 - це оригінальний список, а l2 - це список без повторних елементів (переконайтеся, що у YourClass є метод рівних відповідно до того, що ви хочете виступати за рівність)


У цій відповіді відсутні дві речі: 1) Він не використовує дженерики, але необроблені типи ( ArrayList<T>слід використовувати замість них ArrayList) 2) Створення явного ітератора можна уникнути, використовуючи a for (T current : l1) { ... }. Навіть якщо ви хотіли використовувати Iteratorявно, iteradorнаписано неправильно.
RAnders00

4
І ця реалізація працює в квадратичному часі, порівняно з реалізацією пов'язаного хеш-набору, що працює в лінійний час. (тобто для списку з 10 елементами це займає 10 разів більше, в списку 10 000 елементів - 10 000 разів більше. Реалізація JDK 6 для ArrayList.contains , імпульс JDK8 - те саме.)
Патрік М

21

Можна видалити дублікати з масиву архітектури, не використовуючи HashSet або іншого архівіста .

Спробуйте цей код ..

    ArrayList<String> lst = new ArrayList<String>();
    lst.add("ABC");
    lst.add("ABC");
    lst.add("ABCD");
    lst.add("ABCD");
    lst.add("ABCE");

    System.out.println("Duplicates List "+lst);

    Object[] st = lst.toArray();
      for (Object s : st) {
        if (lst.indexOf(s) != lst.lastIndexOf(s)) {
            lst.remove(lst.lastIndexOf(s));
         }
      }

    System.out.println("Distinct List "+lst);

Вихід є

Duplicates List [ABC, ABC, ABCD, ABCD, ABCE]
Distinct List [ABC, ABCD, ABCE]

Це повільно, і ви можете отримати ConcurrentModificationException.
maaartinus

@maaartinus Ви пробували цей код ?. Це не спричинить жодних винятків. Також це досить швидко. Я спробував код перед публікацією.
CarlJohn

4
Ви маєте рацію, це не так, як ви повторюєте масив замість списку. Однак це повільно, як пекло. Спробуйте це з кількома мільйонами елементів. Порівняйте його з ImmutableSet.copyOf(lst).toList().
maaartinus

відповідає на питання, яке мені задали в інтерв'ю. Як видалити повторні значення з ArrayList без використання наборів. Thanx
Анікет Пол

Внутрішньо це indexOfповторює lstвикористання циклу for.
Патрік М


19

це може вирішити проблему:

private List<SomeClass> clearListFromDuplicateFirstName(List<SomeClass> list1) {

     Map<String, SomeClass> cleanMap = new LinkedHashMap<String, SomeClass>();
     for (int i = 0; i < list1.size(); i++) {
         cleanMap.put(list1.get(i).getFirstName(), list1.get(i));
     }
     List<SomeClass> list = new ArrayList<SomeClass>(cleanMap.values());
     return list;
}

1
Мені це рішення сподобалось більше.
Тушар Гогна

12

Напевно, трохи надмірності, але мені подобається така ізольована проблема. :)

Цей код використовує тимчасовий набір (для перевірки унікальності), але видаляє елементи безпосередньо всередині оригінального списку. Оскільки видалення елементів всередині ArrayList може викликати величезну кількість копіювання масиву, уникати (int) -методу уникнути.

public static <T> void removeDuplicates(ArrayList<T> list) {
    int size = list.size();
    int out = 0;
    {
        final Set<T> encountered = new HashSet<T>();
        for (int in = 0; in < size; in++) {
            final T t = list.get(in);
            final boolean first = encountered.add(t);
            if (first) {
                list.set(out++, t);
            }
        }
    }
    while (out < size) {
        list.remove(--size);
    }
}

Поки ми тут, ось версія для LinkedList (набагато приємніше!):

public static <T> void removeDuplicates(LinkedList<T> list) {
    final Set<T> encountered = new HashSet<T>();
    for (Iterator<T> iter = list.iterator(); iter.hasNext(); ) {
        final T t = iter.next();
        final boolean first = encountered.add(t);
        if (!first) {
            iter.remove();
        }
    }
}

Використовуйте інтерфейс маркера для представлення уніфікованого рішення для списку:

public static <T> void removeDuplicates(List<T> list) {
    if (list instanceof RandomAccess) {
        // use first version here
    } else {
        // use other version here
    }
}

EDIT: Я здогадуюсь, що дженерики не дуже додають тут ніякої цінності. :)


1
Навіщо використовувати ArrayList у параметрі? Чому б не просто Список? Це не вийде?
Шервін Асгарі

Список абсолютно працює як параметр для першого перерахованого методу. Однак метод оптимізований для використання зі списком випадкового доступу, таким як ArrayList, тому, якщо замість нього буде передано LinkedList, ви отримаєте низьку ефективність. Наприклад, встановлення n: -го елемента в LinkedList займає час O (n), тоді як встановлення n-го елемента в списку випадкового доступу (наприклад, ArrayList) займає час O (1). Знову ж таки, це, мабуть, надмірно ... Якщо вам потрібен такий тип спеціалізованого коду, він, сподіваємось, опиниться в ізольованій ситуації.
залп

10
public static void main(String[] args){
    ArrayList<Object> al = new ArrayList<Object>();
    al.add("abc");
    al.add('a');
    al.add('b');
    al.add('a');
    al.add("abc");
    al.add(10.3);
    al.add('c');
    al.add(10);
    al.add("abc");
    al.add(10);
    System.out.println("Before Duplicate Remove:"+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:"+al);
}

Ця реалізація не повертає жодного елемента у списку через останній j--
neo7,

1
Ця робота по впровадженню дуже чудова. За цим завданням немає жодної проблеми, і для цього завдання я використовую лише один arraylist.so ця відповідь цілком хороша. Перед тим, як давати негативні відгуки, ви також додаєте тестовий зразок, щоб кожен міг зрозуміти результат. Дякую Манаш
Манаш Ранджан Дакуа

5

Якщо ви бажаєте використовувати сторонні бібліотеки, ви можете використовувати метод distinct()у колекціях Eclipse (раніше колекції GS).

ListIterable<Integer> integers = FastList.newListWith(1, 3, 1, 2, 2, 1);
Assert.assertEquals(
    FastList.newListWith(1, 3, 2),
    integers.distinct());

Перевага використання distinct()замість перетворення на набір, а потім повернення до списку полягає в тому, що distinct()зберігається порядок вихідного списку, зберігаючи перше виникнення кожного елемента. Він реалізується за допомогою набору і списку.

MutableSet<T> seenSoFar = UnifiedSet.newSet();
int size = list.size();
for (int i = 0; i < size; i++)
{
    T item = list.get(i);
    if (seenSoFar.add(item))
    {
        targetCollection.add(item);
    }
}
return targetCollection;

Якщо ви не можете перетворити свій оригінальний список у тип колекцій Eclipse, ви можете скористатися ListAdapter, щоб отримати той самий API.

MutableList<Integer> distinct = ListAdapter.adapt(integers).distinct();

Примітка. Я є членом колекції Eclipse.


3

Ці три рядки коду можуть видалити повторюваний елемент з ArrayList або будь-якої колекції.

List<Entity> entities = repository.findByUserId(userId);

Set<Entity> s = new LinkedHashSet<Entity>(entities);
entities.clear();
entities.addAll(s);

2

Коли ви заповнюєте ArrayList, використовуйте умову для кожного елемента. Наприклад:

    ArrayList< Integer > al = new ArrayList< Integer >(); 

    // fill 1 
    for ( int i = 0; i <= 5; i++ ) 
        if ( !al.contains( i ) ) 
            al.add( i ); 

    // fill 2 
    for (int i = 0; i <= 10; i++ ) 
        if ( !al.contains( i ) ) 
            al.add( i ); 

    for( Integer i: al )
    {
        System.out.print( i + " ");     
    }

Ми отримаємо масив {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}


2

Якщо ви хочете зберегти своє замовлення, то найкраще скористатися LinkedHashSet . Тому що, якщо ви хочете передати цей Список вставному запиту, повторивши його, порядок буде збережено.

Спробуйте це

LinkedHashSet link=new LinkedHashSet();
List listOfValues=new ArrayList();
listOfValues.add(link);

Ця конверсія буде дуже корисною, коли ви хочете повернути список, але не набір.


2

Код:

List<String> duplicatList = new ArrayList<String>();
duplicatList = Arrays.asList("AA","BB","CC","DD","DD","EE","AA","FF");
//above AA and DD are duplicate
Set<String> uniqueList = new HashSet<String>(duplicatList);
duplicatList = new ArrayList<String>(uniqueList); //let GC will doing free memory
System.out.println("Removed Duplicate : "+duplicatList);

Примітка. Однозначно, накладні гроші будуть.


2
ArrayList<String> city=new ArrayList<String>();
city.add("rajkot");
city.add("gondal");
city.add("rajkot");
city.add("gova");
city.add("baroda");
city.add("morbi");
city.add("gova");

HashSet<String> hashSet = new HashSet<String>();
hashSet.addAll(city);
city.clear();
city.addAll(hashSet);
Toast.makeText(getActivity(),"" + city.toString(),Toast.LENGTH_SHORT).show();

1

LinkedHashSet зробить трюк.

String[] arr2 = {"5","1","2","3","3","4","1","2"};
Set<String> set = new LinkedHashSet<String>(Arrays.asList(arr2));
for(String s1 : set)
    System.out.println(s1);

System.out.println( "------------------------" );
String[] arr3 = set.toArray(new String[0]);
for(int i = 0; i < arr3.length; i++)
     System.out.println(arr3[i].toString());

// вихід: 5,1,2,3,4


1
        List<String> result = new ArrayList<String>();
        Set<String> set = new LinkedHashSet<String>();
        String s = "ravi is a good!boy. But ravi is very nasty fellow.";
        StringTokenizer st = new StringTokenizer(s, " ,. ,!");
        while (st.hasMoreTokens()) {
            result.add(st.nextToken());
        }
         System.out.println(result);
         set.addAll(result);
        result.clear();
        result.addAll(set);
        System.out.println(result);

output:
[ravi, is, a, good, boy, But, ravi, is, very, nasty, fellow]
[ravi, is, a, good, boy, But, very, nasty, fellow]

1

Це використовується для вашого списку спеціальних об’єктів

   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) o2).getId()) /*&&
                    ((Contact)o1).getName().equalsIgnoreCase(((Contact)o2).getName())*/) {
                return 0;
            }
            return 1;
        }
    });
    set.addAll(list);

    final List newList = new ArrayList(set);
    return newList;
}

1

ви можете використовувати вкладений цикл у наступному:

ArrayList<Class1> l1 = new ArrayList<Class1>();
ArrayList<Class1> l2 = new ArrayList<Class1>();

        Iterator iterator1 = l1.iterator();
        boolean repeated = false;

        while (iterator1.hasNext())
        {
            Class1 c1 = (Class1) iterator1.next();
            for (Class1 _c: l2) {
                if(_c.getId() == c1.getId())
                    repeated = true;
            }
            if(!repeated)
                l2.add(c1);
        }

1

Як було сказано раніше, вам слід використовувати клас, що реалізує інтерфейс Set замість List, щоб бути впевненим у єдиності елементів. Якщо вам доведеться дотримуватися порядку елементів, то інтерфейс SortedSet може бути використаний; клас TreeSet реалізує цей інтерфейс.


1

Якщо ви використовуєте модель типу Список <T> / ArrayList <T>. Сподіваюсь, це вам допоможе.

Ось мій код без використання будь-якої іншої структури даних, як набір або хешмап

for (int i = 0; i < Models.size(); i++){
for (int j = i + 1; j < Models.size(); j++) {       
 if (Models.get(i).getName().equals(Models.get(j).getName())) {    
 Models.remove(j);
   j--;
  }
 }
}

0
for(int a=0;a<myArray.size();a++){
        for(int b=a+1;b<myArray.size();b++){
            if(myArray.get(a).equalsIgnoreCase(myArray.get(b))){
                myArray.remove(b); 
                dups++;
                b--;
            }
        }
}

0
import java.util.*;
class RemoveDupFrmString
{
    public static void main(String[] args)
    {

        String s="appsc";

        Set<Character> unique = new LinkedHashSet<Character> ();

        for(char c : s.toCharArray()) {

            System.out.println(unique.add(c));
        }
        for(char dis:unique){
            System.out.println(dis);
        }


    }
}

0
public Set<Object> findDuplicates(List<Object> list) {
        Set<Object> items = new HashSet<Object>();
        Set<Object> duplicates = new HashSet<Object>();
        for (Object item : list) {
            if (items.contains(item)) {
                duplicates.add(item);
                } else { 
                    items.add(item);
                    } 
            } 
        return duplicates;
        }

0
    ArrayList<String> list = new ArrayList<String>();
    HashSet<String> unique = new LinkedHashSet<String>();
    HashSet<String> dup = new LinkedHashSet<String>();
    boolean b = false;
    list.add("Hello");
    list.add("Hello");
    list.add("how");
    list.add("are");
    list.add("u");
    list.add("u");

    for(Iterator iterator= list.iterator();iterator.hasNext();)
    {
        String value = (String)iterator.next();
        System.out.println(value);

        if(b==unique.add(value))
            dup.add(value);
        else
            unique.add(value);


    }
    System.out.println(unique);
    System.out.println(dup);

0

Якщо ви хочете видалити дублікати з ArrayList, значить знайти нижче логіку,

public static Object[] removeDuplicate(Object[] inputArray)
{
    long startTime = System.nanoTime();
    int totalSize = inputArray.length;
    Object[] resultArray = new Object[totalSize];
    int newSize = 0;
    for(int i=0; i<totalSize; i++)
    {
        Object value = inputArray[i];
        if(value == null)
        {
            continue;
        }

        for(int j=i+1; j<totalSize; j++)
        {
            if(value.equals(inputArray[j]))
            {
                inputArray[j] = null;
            }
        }
        resultArray[newSize++] = value;
    }

    long endTime = System.nanoTime()-startTime;
    System.out.println("Total Time-B:"+endTime);
    return resultArray;
}

1
Чому ви розміщуєте квадратичне рішення питання, яке вже має дворічні лінійні та лінійно-лінійні рішення, які також є простішими?
abarnert
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.