Jak usunąć powtarzające się elementy z ArrayList?


Odpowiedzi:


991

Jeśli nie chcesz duplikatów w Collection, powinieneś zastanowić się, dlaczego używasz takiej, Collectionktóra zezwala na duplikaty. Najprostszym sposobem na usunięcie powtarzających się elementów jest dodanie zawartości do Set(co nie pozwoli na duplikaty), a następnie dodanie Settyłu do ArrayList:

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

Oczywiście, to niszczy uporządkowanie elementów w ArrayList.


260
Zobacz także LinkedHashSet, jeśli chcesz zachować zamówienie.
salwa

3
@Chetan znajduje wszystkie duplikaty z ArrayList w O (n), ważne jest, aby poprawnie zdefiniować metodę equals na obiektach, które masz na liście (bez problemu dla liczb): 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
Dobrą praktyką byłoby definiowanie zmiennych przy użyciu typów interfejsów Listi Set(zamiast typów implementacji ArrayListi HashSetjak w twoim przykładzie).
Jonik

33
Możesz to wyczyścić, używając new HashSet(al)zamiast inicjować w celu opróżnienia i wywołania addAll.
ashes999

1
czy mogę dodać zasady ustawiania duplikatu? Na przykład: jeśli mój Objectma kilka wartości, jeśli dwie z nich się powtarzają, uważam je za duplikaty (inne wartości mogą być różne) i używam Set?
jean d'arme

290

Że pomimo iż przeliczenie ArrayListDo HashSetskutecznie usuwa duplikaty, jeśli trzeba zachować kolejność wstawiania, wolałbym proponuję użyć tego wariantu

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

Następnie, jeśli musisz odzyskać List referencję, możesz ponownie użyć konstruktora konwersji.


10
Czy LinkedHashSet daje jakiekolwiek gwarancje, które z kilku duplikatów są przechowywane na liście? Na przykład, jeśli pozycje 1, 3 i 5 są duplikatami na oryginalnej liście, czy możemy założyć, że ten proces usunie 3 i 5? A może usunąć 1 i 3? Dzięki.
Matt Briançon

16
@Matt: tak, to gwarantuje. W Dokumentach stwierdzić: „połączonej listy określa kolejność iteracji, która jest kolejność w której elementy zostały umieszczone w zestawie (wstawiania rzędu) Należy zauważyć, że kolejność wstawiania nie ma wpływu, jeśli element jest ponownie umieszczona w zestawie.”.
abahgat

Bardzo interesujące. Mam tutaj inną sytuację. Nie próbuję sortować ciągów, ale inny obiekt o nazwie AwardYearSource. Ta klasa ma atrybut int o nazwie rok. Chcę więc usunąć duplikaty na podstawie roku. tzn. jeśli w 2010 roku wspomniano więcej niż raz, chcę usunąć ten obiekt AwardYearSource. Jak mogę to zrobić?
WowBow

@WowBow Na przykład możesz zdefiniować obiekt Wrapper, który przechowuje AwardYearSource. I zdefiniuj ten obiekt opakowania równa się metodzie opartej na polu AwardYearSources roku. Następnie możesz użyć Ustaw z tymi obiektami Opakowania.
Ondrej Bozek

@ WowBow lub zaimplementuj porównywalny / komparator
shrini1000 11.01.2013

134

W Javie 8:

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

Należy pamiętać, że aby elementy filtrujące działały poprawnie, należy przestrzegać umowy hashCode-equals dla członków listy.


1
Jak to zrobić, aby rozróżniać małe i wielkie litery?
StackFlowed

@StackFlowed Jeśli nie trzeba zachować kolejność listy możesz addAlldo new TreeSet<String>(String.CASE_INSENSITIVE_ORDER). Pierwszy dodany element pozostanie w zestawie, więc jeśli twoja lista zawiera „Pies” i „Pies” (w tej kolejności), TreeSetbędzie zawierać „Pies”. Jeśli trzeba zachować porządek, to przed wierszem w odpowiedzi umieścić list.replaceAll(String::toUpperCase);.
Paul

1
Otrzymuję ten błąd: niezgodne typy: List <Object> nie można przekonwertować na List <String>
Samir

Jest to ogólnie proste rozwiązanie, ale jak usunąć duplikaty z tablicy Arraylist int []?
Nooby Programmer

56

Załóżmy, że mamy listę Stringtakich jak:

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

Następnie możemy usunąć duplikaty elementów na wiele sposobów.

Przed Javą 8

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

Uwaga: jeśli chcemy zachować kolejność wstawiania, musimy użyć LinkedHashSetzamiastHashSet

Korzystanie z guawy

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

Korzystanie z Java 8

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

Uwaga: W przypadku, gdy chcemy zebrać wynik w konkretnej implementacji listy, np. LinkedListMożemy zmodyfikować powyższy przykład jako:

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

Możemy użyć parallelStreamrównież w powyższym kodzie, ale może nie dać oczekiwanych korzyści w zakresie wydajności. Sprawdź to pytanie, aby uzyskać więcej.


Tak, kiedy wpisałem moje poprzednie komentarze, miałem wrażenie, że parallel streamszawsze da lepszą wydajność. Ale to mit. Później dowiedziałem się, że istnieją pewne scenariusze, w których należy stosować strumienie równoległe. W tym scenariuszu równoległe strumienie nie poprawią wydajności. i tak równoległe strumienie mogą w niektórych przypadkach nie dać pożądanych rezultatów. List<String> deDupStringList3 = stringList.stream().map(String::toLowerCase).distinct().collect(Collectors.toList());powinno być odpowiednim rozwiązaniem w tym przypadku
Diablo,

53

Jeśli nie chcesz duplikatów, użyj zestawu zamiast List. Aby przekonwertować Listna a Set, możesz użyć następującego kodu:

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

Jeśli to naprawdę konieczne, możesz użyć tej samej konstrukcji, aby przekształcić z Setpowrotem w plik List.


Podobnie u dołu wątku podałem odpowiedź, w której używam zestawu dla obiektu niestandardowego. W przypadku, gdy ktoś ma niestandardowy obiekt, taki jak „Kontakt” lub „Uczeń”, może użyć tej odpowiedzi, która działa dobrze dla mnie.
Muhammad Adil

Problem pojawia się, gdy musisz uzyskać konkretny dostęp do elementu. Na przykład podczas wiązania obiektu z widokiem elementu listy w Androidzie otrzymujesz jego indeks. Dlatego Setnie można go tutaj użyć.
TheRealChx101

Jak mogę aproach tego, gdy lista jest lista obiektów
jvargas

28

Możesz to zrobić w ten sposób i zachować porządek:

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

Myślę, że to najlepszy sposób na usunięcie duplikatów w ArrayList. Zdecydowanie polecam. Dziękuję @Nenad za odpowiedź.
ByWaleed

25

Strumienie Java 8 zapewniają bardzo prosty sposób usuwania zduplikowanych elementów z listy. Używając odrębnej metody. Jeśli mamy listę miast i chcemy usunąć duplikaty z tej listy, można to zrobić w jednym wierszu -

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

Jak usunąć zduplikowane elementy z tablicy arraylist


25

Oto sposób, który nie wpływa na porządkowanie listy:

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 to oryginalna lista, a l2 to lista bez powtarzających się elementów (upewnij się, że twoja klasa ma metodę równości zgodnie z tym, co chcesz stać na równości)


W tej odpowiedzi brakuje dwóch rzeczy: 1) Nie używa ona ogólnych, ale typów surowych ( ArrayList<T>należy użyć zamiast ArrayList) 2) Można uniknąć jawnego tworzenia iteratora za pomocą for (T current : l1) { ... }. Nawet jeśli chcesz użyć Iteratorjawnie, iteradorjest źle napisane.
RAnders00

4
I ta implementacja działa w czasie kwadratowym, w porównaniu do implementacji połączonego zestawu skrótów działającej w czasie liniowym. (tj. zajmuje to 10 razy dłużej na liście z 10 elementami, 10 000 razy dłużej na liście z 10 000 elementów. Implementacja JDK 6 dla ArrayList.contains , JDK8 impl jest taka sama.)
Patrick M

21

Możliwe jest usunięcie duplikatów z tablicy arraylist bez użycia HashSet lub jeszcze jednej tablicy arraylist .

Wypróbuj ten kod ..

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

Dane wyjściowe to

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

Jest powolny i może pojawić się wyjątek ConcurrentModificationException.
maaartinus

@maaartinus Czy próbowałeś tego kodu? Nie spowoduje żadnych wyjątków, a także jest dość szybki. Próbowałem kodu przed wysłaniem.
CarlJohn

4
Masz rację, to nie jest tak, jak w przypadku iteracji tablicy zamiast listy. Jest jednak powolny jak diabli. Wypróbuj z kilkoma milionami elementów. Porównaj to ImmutableSet.copyOf(lst).toList().
maaartinus

odpowiada na pytanie, które mi zadawano w wywiadzie. Jak usunąć powtarzające się wartości z ArrayList bez użycia zestawów. Dzięki
Aniket Paul

Wewnętrznie indexOfiteruje lstużycie pętli for.
Patrick M,


19

może to rozwiązać problem:

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
Bardziej podobało mi się to rozwiązanie.
Tushar Gogna,

12

Prawdopodobnie trochę przesada, ale podoba mi się ten rodzaj odosobnionego problemu. :)

Ten kod używa zestawu tymczasowego (do kontroli unikalności), ale usuwa elementy bezpośrednio z oryginalnej listy. Ponieważ usuwanie elementów wewnątrz tablicy ArrayList może spowodować ogromną ilość kopiowania tablicy, unika się metody remove (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);
    }
}

Skoro już o tym mowa, oto wersja LinkedList (o wiele ładniejsza!):

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

Użyj interfejsu znaczników, aby przedstawić ujednolicone rozwiązanie dla listy:

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

EDYCJA: Wydaje mi się, że rzeczy ogólne nie dodają tutaj żadnej wartości .. No cóż. :)


1
Dlaczego warto używać ArrayList w parametrze? Dlaczego nie tylko Lista? Czy to nie zadziała?
Shervin Asgari,

Lista będzie absolutnie działać jako parametr dla pierwszej wymienionej metody. Metoda jest jednak zoptymalizowana do użycia z listą o swobodnym dostępie, taką jak ArrayList, więc jeśli zamiast tego zostanie przekazana lista LinkedList, wydajność będzie mniejsza. Na przykład ustawienie n-tego elementu na LinkedList zajmuje czas O (n), podczas gdy ustawienie n-tego elementu na liście o swobodnym dostępie (np. ArrayList) zajmuje czas O (1). Ponownie jednak jest to prawdopodobnie przesada ... Jeśli potrzebujesz tego rodzaju specjalistycznego kodu, mam nadzieję, że znajdzie się w odosobnionej sytuacji.
salwa

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

Ta implementacja nie zwraca żadnego elementu na liście z powodu ostatniego j--
neo7

1
Ta implementacja działa bardzo dobrze. Nie ma z tym żadnego problemu i do tego zadania używam tylko jednego arraylisty. Więc ta odpowiedź jest całkowicie dobra. Przed udzieleniem negatywnej opinii należy też dodać testcase, aby każdy mógł zrozumieć wynik. Manash
Manash Ranjan Dakua

5

Jeśli chcesz korzystać z biblioteki innej firmy, możesz użyć tej metody distinct()w Eclipse Collections (wcześniej GS Collection).

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

Zaletą używania distinct()zamiast konwersji do zestawu, a następnie powrotu do listy jest to, że distinct()zachowuje kolejność oryginalnej listy, zachowując pierwsze wystąpienie każdego elementu. Jest realizowany przy użyciu zarówno zestawu, jak i listy.

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;

Jeśli nie możesz przekonwertować oryginalnej listy na typ kolekcji Eclipse, możesz użyć ListAdapter, aby uzyskać ten sam interfejs API.

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

Uwaga: jestem osobą odpowiedzialną za kolekcje Eclipse.


3

Te trzy wiersze kodu mogą usunąć zduplikowany element z ArrayList lub dowolnej kolekcji.

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

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

2

Podczas wypełniania ArrayList użyj warunku dla każdego elementu. Na przykład:

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

Otrzymamy tablicę {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}


2

Jeśli chcesz zachować swoje zamówienie, najlepiej użyć LinkedHashSet . Ponieważ jeśli chcesz przekazać tę listę do zapytania wstawiania poprzez iterację, kolejność zostanie zachowana.

Spróbuj tego

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

Ta konwersja będzie bardzo pomocna, jeśli chcesz zwrócić listę, ale nie zestaw.


2

Kod:

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

Uwaga: Zdecydowanie będzie narzut pamięci.


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 załatwi sprawę.

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

// wynik: 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

Służy to do listy obiektów niestandardowych

   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

możesz użyć zagnieżdżonej pętli w następujący sposób:

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

Jak powiedziano wcześniej, powinieneś użyć klasy implementującej interfejs Set zamiast List, aby mieć pewność, że elementy są wyjątkowe. Jeśli musisz zachować kolejność elementów, możesz użyć interfejsu SortedSet; klasa TreeSet implementuje ten interfejs.


1

Jeśli używasz modelu typu List <T> / ArrayList <T>. Mam nadzieję, że ci pomoże.

Oto mój kod bez użycia jakiejkolwiek innej struktury danych, takiej jak set lub 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

Jeśli chcesz usunąć duplikaty z ArrayList oznacza znaleźć poniższą logikę,

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
Dlaczego opublikowałbyś kwadratowe rozwiązanie pytania, które ma już 2-letnie rozwiązania liniowe i logarytmiczno-liniowe, które są również prostsze?
abarnert
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.