Mam dwie ArrayLists.
ArrayList A zawiera:
['2009-05-18','2009-05-19','2009-05-21']
ArrayList B zawiera:
['2009-05-18','2009-05-18','2009-05-19','2009-05-19','2009-05-20','2009-05-21','2009-05-21','2009-05-22']
Muszę porównać ArrayList A i ArrayList B. Wynik ArrayList powinien zawierać listę, która nie istnieje w ArrayList A.
Wynik ArrayList powinien wyglądać następująco:
['2009-05-20','2009-05-22']
jak porównać?
equals
która umożliwi porównywanie obiektów. Przeczytaj również o wdrażaniuhashCode
. Na przykład zwróć uwagę, jak rozróżnianaString::equals
jest wielkość liter , więc „jabłko” i „Apple” nie będą traktowane tak samo.removeAll
odwołujefirstList.contains
się do każdego elementusecondList
. UżycieHashSet
litery zapobiegłoby temu, a kilka dobrych odpowiedzi jest niższych.Masz już właściwą odpowiedź. A jeśli chcesz wykonać bardziej skomplikowane i interesujące operacje między Listami (kolekcjami), użyj kolekcji apache commons ( CollectionUtils ). Pozwala to na tworzenie koniukcji / rozłączania, znajdowanie przecięcia, sprawdzanie, czy jedna kolekcja jest podzbiorem innej i innych fajnych rzeczy.
źródło
W Javie 8 ze strumieniami jest to całkiem proste. EDYCJA: Może być wydajna bez strumieni, patrz niżej.
List<String> listA = Arrays.asList("2009-05-18","2009-05-19","2009-05-21"); List<String> listB = Arrays.asList("2009-05-18","2009-05-18","2009-05-19","2009-05-19", "2009-05-20","2009-05-21","2009-05-21","2009-05-22"); List<String> result = listB.stream() .filter(not(new HashSet<>(listA)::contains)) .collect(Collectors.toList());
Zwróć uwagę, że zestaw skrótów jest tworzony tylko raz: odwołanie do metody jest powiązane z metodą zawierającą. Zrobienie tego samego z lambdą wymagałoby posiadania zestawu w zmiennej. Tworzenie zmiennej nie jest złym pomysłem, zwłaszcza jeśli uznasz to za brzydkie lub trudniejsze do zrozumienia.
Nie możesz łatwo zanegować predykatu bez czegoś takiego jak ta metoda narzędziowa (lub jawne rzutowanie), ponieważ nie możesz bezpośrednio wywołać odwołania do metody negacji (najpierw wymagane jest wnioskowanie o typie).
private static <T> Predicate<T> not(Predicate<T> predicate) { return predicate.negate(); }
Gdyby strumienie miały jakąś
filterOut
metodę lub coś, wyglądałoby to ładniej.Również @Holger dał mi pomysł.
ArrayList
ma swojąremoveAll
metodę zoptymalizowaną pod kątem wielokrotnych usunięć, zmienia układ elementów tylko raz. Jednak używacontains
metody dostarczonej przez daną kolekcję, więc musimy zoptymalizować tę część, jeśli nielistA
jest mała.Z
listA
ilistB
oświadczył wcześniej, to rozwiązanie nie wymaga Javy 8 i jest bardzo wydajny.List<String> result = new ArrayList(listB); result.removeAll(new HashSet<>(listA));
źródło
Predicates.in(new HashSet<>(listA)).negate()
.ArrayList.remove
ma liniową złożoność, aleArrayList.removeAll
nie polega na,remove
ale wykonuje liniową operację aktualizacji tablicy, kopiując każdy pozostały element na jego ostateczne miejsce. W przeciwieństwie do tego implementacja referencyjnaLinkedList
nie została zoptymalizowana,removeAll
ale wykonujeremove
operację dla każdego elementu, którego dotyczy problem, i za każdym razem aktualizuje do pięciu odniesień. Tak więc, w zależności od stosunku pomiędzy usuniętymi i pozostałych elementów,ArrayList
jestremoveAll
może jeszcze znacznie lepiej niż wykonaćLinkedList
s ', nawet dla dużych list.EDYCJA: Oryginalne pytanie nie określało języka. Moja odpowiedź jest w C #.
W tym celu należy zamiast tego użyć HashSet. Jeśli musisz użyć ArrayList, możesz użyć następujących metod rozszerzających:
var a = arrayListA.Cast<DateTime>(); var b = arrayListB.Cast<DateTime>(); var c = b.Except(a); var arrayListC = new ArrayList(c.ToArray());
przy użyciu HashSet ...
var a = new HashSet<DateTime>(); // ...and fill it var b = new HashSet<DateTime>(); // ...and fill it b.ExceptWith(a); // removes from b items that are in a
źródło
Użyłem Guava Sets.difference .
Parametry są zestawami, a nie zbiorami ogólnymi, ale wygodnym sposobem tworzenia zestawów z dowolnej kolekcji (z unikalnymi przedmiotami) jest Guava ImmutableSet. (Iterable).
(Po raz pierwszy opublikowałem to w pytaniu pokrewnym / fałszywym , ale kopiuję to również tutaj, ponieważ uważam, że jest to dobra opcja, której do tej pory brakuje.)
źródło
Chociaż jest to bardzo stare pytanie w Javie 8, możesz zrobić coś takiego
List<String> a1 = Arrays.asList("2009-05-18", "2009-05-19", "2009-05-21"); List<String> a2 = Arrays.asList("2009-05-18", "2009-05-18", "2009-05-19", "2009-05-19", "2009-05-20", "2009-05-21","2009-05-21", "2009-05-22"); List<String> result = a2.stream().filter(elem -> !a1.contains(elem)).collect(Collectors.toList());
źródło
Collection
metodęcontains
, jest ona bardzo nieefektywna. Jeśli nie zostanie znaleziony, musi przejść przez całą listę. Robienie tego dla każdego elementua2
może być boleśnie powolne na większych listach, dlategoa1
w mojej odpowiedzi zestawiam.Myślę, że mówisz o C #. Jeśli tak, możesz spróbować
ArrayList CompareArrayList(ArrayList a, ArrayList b) { ArrayList output = new ArrayList(); for (int i = 0; i < a.Count; i++) { string str = (string)a[i]; if (!b.Contains(str)) { if(!output.Contains(str)) // check for dupes output.Add(str); } } return output; }
źródło
b
listęa.Count
razy. MożeszHashSet
zamiast tego utworzyć metodę, która będzie używana dla metodyContains
lub użyćRemoveAll
metody w zestawie, aby uzyskać dokładnie takie wyniki, jakie chcesz.Po prostu porównujesz struny.
Umieść wartości w ArrayList A jako klucze w HashTable A.
Umieść wartości w ArrayList B jako klucze w HashTable B.
Następnie dla każdego klucza w HashTable A usuń go z HashTable B, jeśli istnieje.
W HashTable B pozostały ci ciągi (klucze), które nie były wartościami w ArrayList A.
Przykład C # (3.0) dodany w odpowiedzi na żądanie kodu:
List<string> listA = new List<string>{"2009-05-18","2009-05-19","2009-05-21'"}; List<string> listB = new List<string>{"2009-05-18","2009-05-18","2009-05-19","2009-05-19","2009-05-20","2009-05-21","2009-05-21","2009-05-22"}; HashSet<string> hashA = new HashSet<string>(); HashSet<string> hashB = new HashSet<string>(); foreach (string dateStrA in listA) hashA.Add(dateStrA); foreach (string dateStrB in listB) hashB.Add(dateStrB); foreach (string dateStrA in hashA) { if (hashB.Contains(dateStrA)) hashB.Remove(dateStrA); } List<string> result = hashB.ToList<string>();
źródło
hashA
zmienna jest praktycznie bezużyteczna. Zamiast tego można utworzyć foreach,listA
którehashA
jest tylko iterowane iContains
nigdy nie jest wywoływane.Cześć, używam tej klasy, to porównuje obie listy i pokazuje dokładnie niezgodność b / w obu list.
import java.util.ArrayList; import java.util.List; public class ListCompare { /** * @param args */ public static void main(String[] args) { List<String> dbVinList; dbVinList = new ArrayList<String>(); List<String> ediVinList; ediVinList = new ArrayList<String>(); dbVinList.add("A"); dbVinList.add("B"); dbVinList.add("C"); dbVinList.add("D"); ediVinList.add("A"); ediVinList.add("C"); ediVinList.add("E"); ediVinList.add("F"); /*ediVinList.add("G"); ediVinList.add("H"); ediVinList.add("I"); ediVinList.add("J");*/ List<String> dbVinListClone = dbVinList; List<String> ediVinListClone = ediVinList; boolean flag; String mismatchVins = null; if(dbVinListClone.containsAll(ediVinListClone)){ flag = dbVinListClone.removeAll(ediVinListClone); if(flag){ mismatchVins = getMismatchVins(dbVinListClone); } }else{ flag = ediVinListClone.removeAll(dbVinListClone); if(flag){ mismatchVins = getMismatchVins(ediVinListClone); } } if(mismatchVins != null){ System.out.println("mismatch vins : "+mismatchVins); } } private static String getMismatchVins(List<String> mismatchList){ StringBuilder mismatchVins = new StringBuilder(); int i = 0; for(String mismatch : mismatchList){ i++; if(i < mismatchList.size() && i!=5){ mismatchVins.append(mismatch).append(","); }else{ mismatchVins.append(mismatch); } if(i==5){ break; } } String mismatch1; if(mismatchVins.length() > 100){ mismatch1 = mismatchVins.substring(0, 99); }else{ mismatch1 = mismatchVins.toString(); } return mismatch1; } }
źródło
TO DZIAŁA RÓWNIEŻ Z Arraylist
// Create a couple ArrayList objects and populate them // with some delicious fruits. ArrayList<String> firstList = new ArrayList<String>() {/** * */ private static final long serialVersionUID = 1L; { add("apple"); add("orange"); add("pea"); }}; ArrayList<String> secondList = new ArrayList<String>() { /** * */ private static final long serialVersionUID = 1L; { add("apple"); add("orange"); add("banana"); add("strawberry"); }}; // Show the "before" lists System.out.println("First List: " + firstList); System.out.println("Second List: " + secondList); // Remove all elements in firstList from secondList secondList.removeAll(firstList); // Show the "after" list System.out.println("Result: " + secondList);
źródło
remove
icontains
muszą przeszukiwać całą listę. Jeśli zostaniesz wywołany wielokrotnie w cyklu (co dzieje się wremoveAll
), otrzymasz kwadratową złożoność. Możesz jednak użyć zestawu skrótu i mieć go tylko liniowo.