Как удалить повторяющиеся элементы из ArrayList?


Ответы:


991

Если вам не нужны дубликаты в a Collection, вам следует подумать, почему вы используете a, Collectionкоторый разрешает дубликаты. Самый простой способ удалить повторяющиеся элементы - это добавить содержимое в Set(что не допустит дублирования), а затем добавить Setобратно в ArrayList:

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

Конечно, это разрушает порядок элементов в ArrayList.


261
Смотрите также LinkedHashSet, если вы хотите сохранить заказ.
залп

3
@Cetan находит все дубликаты из ArrayList в O (n), важно правильно определить метод equals для объектов, которые есть в списке (без проблем для чисел): 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; }
Ондрей Бозек

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

33
Вы можете очистить это, используя new HashSet(al)вместо инициализации его пустым и вызывая addAll.
ashes999

1
Могу ли я добавить правила для установки того, что дублирует меня? Например: когда my Objectимеет несколько значений, если два из них повторяются, я считаю их дубликатами (другие значения могут отличаться) и использую Set?
Жан д'Арм

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: да, это гарантирует. В документах говорят: «Это связанный список определяет порядок итерации, который является порядком , в котором элементы были вставлены в набор (вставка порядок) Обратите внимание , что порядок вставки не влияет , если элемент повторно вставлено в набор.» .
Абахгат

Очень интересно. У меня здесь другая ситуация. Я не пытаюсь отсортировать строку, но другой объект называется AwardYearSource. Этот класс имеет атрибут int с именем year. Поэтому я хочу удалить дубликаты в зависимости от года. т.е. если 2010 год упоминается более одного раза, я хочу удалить этот объект AwardYearSource. Как я могу это сделать?
WowBow

@WowBow Например, вы можете определить объект Wrapper, который содержит AwardYearSource. И определите этот метод равных объектов Wrapper на основе поля AwardYearSources year. Затем вы можете использовать Set с этими объектами Wrapper.
Ондрей Бозек

@WowBow или реализуй Comparable / Comparator
shrini1000

134

В Java 8:

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

Обратите внимание, что для правильной работы фильтрации следует соблюдать контракт hashCode-equals для членов списка.


1
Как я могу сделать это для разных без учета регистра?
StackFlowed

@StackFlowed Если вам не нужно сохранять порядок списка, addAllк которому вы можете new TreeSet<String>(String.CASE_INSENSITIVE_ORDER). Первый добавленный элемент останется в наборе, поэтому, если ваш список содержит «Dog» и «Dog» (в этом порядке), он TreeSetбудет содержать «Dog». Если порядок должен быть сохранен, то перед строкой в ​​ответе поставить list.replaceAll(String::toUpperCase);.
Пол

1
Я получаю эту ошибку: несовместимые типы: List <Object> не может быть преобразован в List <String>
Samir

В общем, это простое решение, но как удалить дубликаты из Arraylist из int []?
Новичок Программист

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

Использование гуавы

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());должно быть подходящее решение в этом случае
Diablo

53

Если вы не хотите дубликатов, используйте Set вместо a List. Для преобразования 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 есть метод equals в соответствии с тем, что вы хотите отстаивать для равенства)


В этом ответе отсутствуют две вещи: 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 impl такая же.)
Патрик М.

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 Вы пробовали этот код? Это не будет производить никаких исключений. Также это довольно быстро. Я попробовал код перед публикацией.
КарлДжон

4
Вы правы, это не так, как вы перебираете массив вместо списка. Тем не менее, это медленно, как ад. Попробуйте это с несколькими миллионами элементов. Сравните это с ImmutableSet.copyOf(lst).toList().
Maaartinus

отвечает на вопрос, который мне задавали в интервью. Как удалить повторяющиеся значения из ArrayList без использования Sets. Спасибо
Аникет Пол,

Внутренне, 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();
        }
    }
}

Используйте интерфейс маркера, чтобы представить унифицированное решение для List:

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

РЕДАКТИРОВАТЬ: Я думаю, что дженерики действительно не добавляет никакой ценности здесь ... Ну, хорошо. :)


1
Зачем использовать ArrayList в параметре? Почему не просто список? Это не сработает?
Шервин Аскари

Список будет работать как параметр для первого метода в списке. Однако этот метод оптимизирован для использования со списком произвольного доступа, таким как ArrayList, поэтому, если вместо него будет передан LinkedList, вы получите низкую производительность. Например, установка элемента n: th в LinkedList занимает время O (n), тогда как установка элемента n: th в списке произвольного доступа (например, 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. Так что этот ответ полностью хорош. Прежде чем дать отрицательный отзыв, вы также должны добавить тестовый пример, чтобы каждый мог понять результат. Спасибо Манаш
Манаш Ранджан Дакуа

5

Если вы желаете использовать стороннюю библиотеку, вы можете использовать метод distinct()в Eclipse Collections (ранее GS Collections).

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

Преимущество использования distinct()вместо преобразования в набор, а затем обратно в список состоит в том, что distinct()сохраняется порядок исходного списка, сохраняя первое вхождение каждого элемента. Это реализовано с использованием как Set, так и List.

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 Collections.


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

Если вы используете модель типа List <T> / ArrayList <T>. Надеюсь, это поможет вам.

Вот мой код без использования какой-либо другой структуры данных, такой как set или hashmap

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
Зачем вам публиковать квадратичное решение вопроса, в котором уже есть 2-летние линейные и логарифмические решения, которые также проще?
Абарнерт
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.