2010-05-17 17 views
50

Chcę usunąć duplikaty z listy, ale co robie nie działa:Jak usunąć duplikaty z listy?

List<Customer> listCustomer = new ArrayList<Customer>();  
for (Customer customer: tmpListCustomer) 
{ 
    if (!listCustomer.contains(customer)) 
    { 
    listCustomer.add(customer); 
    } 
} 
+9

nie należy napisać kod jak to w Javie, podobnie jak nie powinieneś pisać własnych procedur sortowania w Javie. Jeśli ma taką możliwość, wbudowaną w coś takiego jak Set, użyj tego. –

Odpowiedz

44

Jeśli ten kod nie działa, prawdopodobnie nie zaimplementowano prawidłowo klasy equals(Object) w klasie Customer.

Przypuszczalnie istnieje pewien klucz (nazwijmy to customerId), który jednoznacznie identyfikuje klienta; na przykład

class Customer { 
    private String customerId; 
    ... 

Odpowiednia definicja equals(Object) będzie wyglądać następująco:

public boolean equals(Object obj) { 
     if (obj == this) { 
      return true; 
     } 
     if (!(obj instanceof Customer)) { 
      return false; 
     } 
     Customer other = (Customer) obj; 
     return this.customerId.equals(other.customerId); 
    } 

Dla kompletności, ty powinny również wdrożyć hashCode tak że dwa Customer obiekty, które są równe zwróci tę samą wartość skrótu. Dopasowanie hashCode dla wyżej definicja equals byłoby:

public int hashCode() { 
     return customerId.hashCode(); 
    } 

Warto również zauważyć, że nie jest to skuteczny sposób, aby usunąć duplikaty jeśli lista jest duża. (W przypadku listy zawierającej klientów N należy wykonać porównania w najgorszym przypadku, tzn. Gdy nie ma duplikatów). Aby uzyskać bardziej wydajne rozwiązanie, należy użyć czegoś takiego, jak HashSet, aby wykonać duplikat sprawdzania.

8

Podejrzewam, że nie mogło Customer.equals() realizowane prawidłowo (lub w ogóle).

List.contains() używa equals() do sprawdzenia, czy którykolwiek z jego elementów jest identyczny z obiektem przekazanym jako parametr. Jednak domyślna implementacja testów tożsamości fizycznej, nie wartościowej. Więc jeśli nie zastąpiłeś go w Customer, zwróci on wartość false dla dwóch różnych obiektów Klienta mających identyczny stan.

Oto kilka drobnych szczegółów z how to implement equals (i hashCode, która jest jego parą - musisz praktycznie zawsze zaimplementować oba, jeśli potrzebujesz zaimplementować którekolwiek z nich). Ponieważ nie pokazałeś nam klasy Klientów, trudno jest udzielić bardziej konkretnej porady.

Jak zauważyli inni, lepiej jest używać zestawu zamiast wykonywać pracę ręcznie, ale nawet w tym przypadku nadal trzeba wdrożyć te metody.

+0

jak mogę to wdrożyć? – Mercer

+0

Pomijanie równań i metod hashCode z java.lang.Object. Będziesz chciał przeczytać: http://java.sun.com/developer/Books/effectivejava/Chapter3.pdf – duffymo

+0

poprawnym sposobem usuwania duplikatów z listy w Javie jest użycie zestawu. I nie można po prostu zastąpić equals() bez przesłonięcia hashCode(). –

12

Lista → Ustaw → List (odrębny)

Wystarczy dodać wszystkie elementy do Set: nie pozwala to elementy, które należy powtórzyć. Jeśli potrzebujesz później listy, użyj później konstruktora ArrayList(theSet) (gdzie theSet to wynikowy zestaw).

+3

Używanie zestawu dawałoby dokładnie takie same wyniki, jak kod napisany powyżej, tylko szybciej. Plakat mówi "nie działa", a nie "działa zbyt wolno". – DJClayworth

+0

dobrze Ustawia prace i jego kod, a nie który jest lepszym, działającym kodem, który jest poprawny i nie musisz pisać, lub błędnym kodem, którego naprawdę nie rozumiesz i nie działa. –

+1

Sądzę, że zakładasz, że chce tylko usunąć zduplikowane odniesienia do tego samego obiektu. Gdyby tak było, wtedy opublikowany kod zadziałałby. – DJClayworth

0

Prawidłowa odpowiedź dla języka Java to: Set. Jeśli masz już List<Customer> i chcą de powielać to

Set<Customer> s = new HashSet<Customer>(listCustomer); 

Otherise prostu użyć Set implementacja HashSet, TreeSet bezpośrednio i pominąć fazę List budowlanego.

Będziesz musiał zastąpić hashCode() and equals() na swoich klasach domeny, które są umieszczone w Set, a także, aby upewnić się, że zachowanie, które chcesz faktycznie, co otrzymujesz. equals() może być tak proste, jak porównywanie unikalnych identyfikatorów obiektów do tak złożonych, jak porównywanie każdego pola. hashCode() może być tak proste jak zwrócenie hashCode() unikatowej reprezentacji id 'String lub hashCode().

+0

Zestaw mógłby zrobić to samo, co opublikowany kod, po prostu szybciej. – DJClayworth

+0

prędkość nie jest tak ważna jak łatwość konserwacji, nie trzeba utrzymywać kodu Set i jest to dokumentacja własna oraz właściwy idiom Java. –

+4

Homer: Możesz zrobić to we właściwy sposób, w niewłaściwy sposób lub na drodze Max Power. Bart: Jaka jest droga Max Power? Homer: To zła droga, tylko szybciej. – DJClayworth

1

Jak już wspomnieli inni, prawdopodobnie nie stosujesz poprawnie equals().

Należy jednak zauważyć, że kod ten jest uważany za dość nieefektywny, ponieważ środowisko wykonawcze może być liczbą elementów podniesionych do kwadratu.

Być może warto rozważyć użycie zamiast struktury Listy zamiast Seta lub najpierw zbudować Set, a następnie zamienić go na listę.

3

dwie sugestie:

  • użyć HashSet zamiast ArrayList. Przyspieszy to znacząco kontroli include(), jeśli masz długą listę:

  • Upewnij się, że parametry Customer.equals() i Customer.hashCode() są poprawnie implementowane, tzn. Powinny być oparte na połączonych wartościach bazowych. pola w obiekcie klienta.

5

Metoda "zawiera" sprawdza, czy lista zawiera pozycję, która zwraca wartość true z pliku Customer.equals (Obiekt o). Jeśli nie zastąpisz równych (Obiektów) w Kliencie lub jednym z jego rodziców, będzie on wyszukiwał tylko istniejące wystąpienie tego samego obiektu. Być może właśnie tego chciałeś, w takim przypadku Twój kod powinien zadziałać. Ale jeśli nie szukałeś dwóch obiektów reprezentujących tego samego klienta, musisz zastąpić równe (Object), aby zwrócić true, kiedy tak jest.

Prawdą jest również, że użycie jednej z wersji Set zamiast List automatycznie usunie duplikaty i przyspieszy (w przypadku innych niż bardzo małych list). Nadal będziesz musiał podać kod dla równych.

Powinieneś także przesłonić hashCode(), gdy zastępujesz equals().

+0

Nie przesłałem tego, ale myślę, że Twoja sugestia, aby zastąpić 'równe', aby usunąć duplikaty, mogła na to zasłużyć. –

+0

Masz na myśli sugestię, która jest taka sama jak zaakceptowana odpowiedź? – DJClayworth

+1

@DJClayworth: Po dokładniejszym przeczytaniu Twojego wpisu zgadzam się, że jest całkowicie poprawny (w moim pierwszym czytaniu, chociaż sugerowałeś wykonanie "specjalnego przypadku" jest równy). Otrzymujesz moje +1 za niesprawiedliwe zaniechanie. Z drugiej strony, patrząc na inne stanowiska tutaj, ktoś był na pogoni za zemstą. –

13

Czy Klient wykonuje umowę equals()?

Jeśli nie implementuje equals() i hashCode(), następnie listCustomer.contains(customer) będzie sprawdzić, czy dokładnie to samo instancja już istnieje na liście (By przykład mam na myśli dokładnie ten sam obiekt - adres pamięci, etc). Jeśli to, czego szukasz, to sprawdzenie, czy ten sam klient (być może to ten sam klient, który ma tę samą nazwę klienta lub numer klienta) jest już na liście, to musisz zastąpić equals(), aby zapewnić sprawdza, czy odpowiednie pola (np. nazwy klientów) są zgodne.

Uwaga: Nie zapomnij zastąpić hashCode(), jeśli chcesz zastąpić equals()! W przeciwnym razie może pojawić się problem z HashMaps i innymi strukturami danych.Aby dobrze zrozumieć, dlaczego tak jest i jakich pułapek należy unikać, warto przyjrzeć się rozdziałom Josh Blocha Effective Java na equals() i hashCode() (Link zawiera tylko informacje o tym, dlaczego należy wdrożyć hashCode() po wdrożeniu equals(), ale istnieje dobry zasięg jak zastąpić equals()).

Nawiasem mówiąc, czy w twoim zestawie występuje ograniczenie zamówienia? Jeśli nie ma, nieco łatwiejszy sposób na rozwiązanie tego problemu jest używanie Set<Customer> tak:

Set<Customer> noDups = new HashSet<Customer>(); 
noDups.addAll(tmpListCustomer); 
return new ArrayList<Customer>(noDups); 

Który ładnie usunąć duplikaty dla Ciebie, ponieważ nie pozwalają Zestawy duplikaty. Jednak spowoduje to utratę wszelkich zamówień, które zostały zastosowane do tmpListCustomer, ponieważ HashSet nie ma wyraźnego zamówienia (można obejść to za pomocą TreeSet, ale nie jest to dokładnie związane z pytaniem). To może trochę uprościć twój kod.

+3

+1 za zapamiętanie, że Set nie może być użyty, jeśli chcesz zachować porządek. – DJClayworth

+0

dla sugestii 'TreeSet <>()' do utrzymania porządku: +1 – Thomas

84

Zakładając chcesz zachować aktualny porządek i nie chcą Set, najłatwiejszym jest:

List<Customer> depdupeCustomers = 
    new ArrayList<>(new LinkedHashSet<>(customers)); 

Jeśli chcesz zmienić oryginalną listę:

Set<Customer> depdupeCustomers = new LinkedHashSet<>(customers); 
customers.clear(); 
customers.addAll(dedupeCustomers); 
+2

Jeśli nie przesłoniłeś metody obiektu (klienta), HashSet porówna lokalizację obiektów w pamięci, aby nie były równe i nadal będziesz mieć duplikaty w swoim nowym zestawie. –

+2

@GinjaNinja Istnieje domyślne założenie, że 'równy' (i' hashCode') jest zaimplementowany w sposób, który ma sens dla danego typu. Na przykład, 'LinkedHashSet ' usuwa tylko duplikaty, które były dokładnie tym samym obiektem, ponieważ oznacza to, że dla instancji 'JWindow' są równe. –

+0

najlepsza odpowiedź; pracował. –

1

Najczystszy sposób to:

List<XXX> lstConsultada = dao.findByPropertyList(YYY); 
List<XXX> lstFinal = new ArrayList<XXX>(new LinkedHashSet<GrupoOrigen>(XXX)); 

a nd przesłonić hascode i equals nad właściwości identyfikatora każdego podmiotu

5
private void removeTheDuplicates(List<Customer>myList) { 
    for(ListIterator<Customer>iterator = myList.listIterator(); iterator.hasNext();) { 
     Customer customer = iterator.next(); 
     if(Collections.frequency(myList, customer) > 1) { 
      iterator.remove(); 
     } 
    } 
    System.out.println(myList.toString()); 

} 
3

Prawie wszystkie powyższe odpowiedzi są w porządku, ale to, co proponuję jest użycie mapie lub ustawiona podczas tworzenia powiązanych listy, a nie po uzyskanie wydajności. Ponieważ przekształcenie listy w Zestaw lub Mapę, a następnie jej ponowne przekształcenie na Listę, jest sprawą prostą.

Kod

Próbka:

Set<String> stringsSet = new LinkedHashSet<String>();//A Linked hash set 
//prevents the adding order of the elements 
for (String string: stringsList) { 
    stringsSet.add(string); 
} 
return new ArrayList<String>(stringsSet); 
1

IMHO najlepszy sposób, jak to zrobić w tych dniach:

Załóżmy, że masz Collection "dups" i chcesz utworzyć kolejny zbiór zawierający te same elementy, ale z wyeliminowanymi wszystkimi duplikatami. Poniższy jednolinijkowy załatwia sprawę.

Collection<collectionType> noDups = new HashSet<collectionType>(dups); 

Działa, tworząc zestaw, który z definicji nie może zawierać duplikatów.

Na podstawie oracle doc.

+0

Aby dodać do tej odpowiedzi, użycie 'Set' powoduje również marnowanie miejsca, czego potencjalnie można tutaj uniknąć. –

17

java 8 aktualizacja
można użyć strumienia tablicy jak poniżej:

Arrays.stream(yourArray).distinct() 
        .collect(Collectors.toList()); 
+0

Najlepsze rozwiązanie. Z zestawem tracisz zamówienie (co może być wymaganiem lub nie, ale przy takim rozwiązaniu nie musisz o tym myśleć). – cocorossello

+2

Możesz także użyć TreeSet, ale musisz zaimplementować Porównywalne, co nie zawsze jest pożądane – cocorossello

+0

W jaki sposób obejść problem "Niezgodność typu: nie można przekonwertować z listy na listę " z tego powodu? –

-2
Class removeduplicates 
{ 
    public static void main(string args[[]) 
    { 
     int I; 
     for(int =0;i'<10;I++) 
     { 
      system.out.println(+i); 
      if([]I=[j]) 
      { 
       system.out.println(1,2,3,1,1,1,2,2,2) 
      } 
     } 
    } 
} 
1

Korzystanie Java 8 strumienia API.

List<String> list = new ArrayList<>(); 
    list.add("one"); 
    list.add("one"); 
    list.add("two"); 
    System.out.println(list); 
    Collection<String> c = list.stream().collect(Collectors.toSet()); 
    System.out.println(c); 

wyjściowa:

Przed wartości: [jeden, jeden, dwa]

Po wartości: [jeden, dwa]

Powiązane problemy