У меня есть два списка ArrayList.
ArrayList A содержит:
['2009-05-18','2009-05-19','2009-05-21']
ArrayList B содержит:
['2009-05-18','2009-05-18','2009-05-19','2009-05-19','2009-05-20','2009-05-21','2009-05-21','2009-05-22']
Мне нужно сравнить ArrayList A и ArrayList B. Результат ArrayList должен содержать список, которого нет в ArrayList A.
Результатом ArrayList должно быть:
['2009-05-20','2009-05-22']
как сравнивать?
equals
, позволяющую сравнивать ваши объекты. Прочтите также о реализацииhashCode
. Например, обратите внимание , какString::equals
это чувствительно к регистру , поэтому «яблоко» и «Яблоко» не будут считаться таким же.removeAll
призываетfirstList.contains
к каждому элементуsecondList
. Использование aHashSet
могло бы предотвратить это, и ниже есть несколько хороших ответов.У тебя уже есть правильный ответ. И если вы хотите сделать более сложные и интересные операции между списками (коллекциями), используйте коллекции общего доступа apache ( CollectionUtils ). Это позволяет вам создавать соединение / дизъюнкцию, находить пересечения, проверять, является ли одна коллекция подмножеством другой, и другие приятные вещи.
источник
В Java 8 с потоками все довольно просто. РЕДАКТИРОВАТЬ: может быть эффективным без потоков, см. Ниже.
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());
Обратите внимание, что хэш-набор создается только один раз: ссылка на метод привязана к его методу contains. То же самое с лямбда-выражением потребует наличия набора в переменной. Создание переменной - неплохая идея, особенно если вы находите ее неприглядной или трудной для понимания.
Вы не можете легко отрицать предикат без чего-то вроде этого служебного метода (или явного приведения), поскольку вы не можете напрямую вызвать ссылку на метод отрицания (сначала требуется вывод типа).
private static <T> Predicate<T> not(Predicate<T> predicate) { return predicate.negate(); }
Если бы у потоков был
filterOut
метод или что-то в этом роде, это выглядело бы лучше.Кроме того, @Holger подал мне идею.
ArrayList
имеет свойremoveAll
метод, оптимизированный для множественного удаления, он только один раз переупорядочивает свои элементы. Однако он используетcontains
метод, предоставляемый данной коллекцией, поэтому нам нужно оптимизировать эту часть, еслиlistA
она совсем не крошечная.С
listA
иlistB
заявил ранее, это решение не нуждается в Java 8 , и это очень эффективно.List<String> result = new ArrayList(listB); result.removeAll(new HashSet<>(listA));
источник
Predicates.in(new HashSet<>(listA)).negate()
.ArrayList.remove
имеет линейную сложность, ноArrayList.removeAll
не полагается,remove
а выполняет операцию обновления линейного массива, копируя каждый оставшийся элемент на свое последнее место. Напротив, эталонная реализацияLinkedList
не оптимизирована,removeAll
но выполняетremove
операцию для каждого затронутого элемента, которая будет обновлять до пяти ссылок каждый раз. Таким образом, в зависимости от соотношения между удаленными и оставшимися элементами,ArrayList
'sremoveAll
может работать значительно лучше, чемLinkedList
' s, даже для огромных списков.РЕДАКТИРОВАТЬ: исходный вопрос не указывал язык. Мой ответ на C #.
Вместо этого вы должны использовать для этой цели HashSet. Если вам необходимо использовать ArrayList, вы можете использовать следующие методы расширения:
var a = arrayListA.Cast<DateTime>(); var b = arrayListB.Cast<DateTime>(); var c = b.Except(a); var arrayListC = new ArrayList(c.ToArray());
используя 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
источник
Я использовал Guava Sets.difference .
Параметры - это наборы, а не общие коллекции, но удобный способ создания наборов из любой коллекции (с уникальными элементами) - это Guava ImmutableSet.copyOf (Iterable).
(Сначала я разместил это по связанному / обманчивому вопросу , но я копирую его и здесь, поскольку считаю, что это хороший вариант, которого пока нет.)
источник
Хотя это очень старый вопрос в Java 8, вы могли бы сделать что-то вроде
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());
источник
Collection
методcontains
, он очень неэффективен. Если он не найден, его нужно пройти через весь список. Выполнение этого для каждого элементаa2
может быть мучительно медленным для больших списков, поэтому я делаю этоa1
в своем ответе.Я думаю, вы говорите о C #. Если да, вы можете попробовать это
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; }
источник
b
спискаa.Count
.HashSet
Вместо этого вы можете создать метод для использованияContains
или использоватьRemoveAll
метод набора для получения именно тех результатов, которые вам нужны.Вы просто сравниваете строки.
Поместите значения в ArrayList A как ключи в HashTable A.
Поместите значения в ArrayList B как ключи в HashTable B.
Затем для каждого ключа в HashTable A удалите его из HashTable B, если он существует.
В HashTable B у вас остались строки (ключи), которые не были значениями в ArrayList A.
Пример C # (3.0) добавлен в ответ на запрос кода:
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>();
источник
hashA
переменная фактически бесполезна.listA
Вместо этого вы можете создать foreach, посколькуhashA
он только повторяется иContains
никогда не вызывается.Привет, используйте этот класс, это сравнит оба списка и точно покажет несоответствие ч / б обоих списков.
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; } }
источник
ЭТА РАБОТАЕТ ТАКЖЕ С 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);
источник
remove
иcontains
требуют поиска по всему списку. При повторном вызове в цикле (который происходит вremoveAll
) вы получите квадратичную сложность. Однако вы можете использовать хэш-набор и сделать его просто линейным.