Простой способ найти, если два разных списка содержат одни и те же элементы?

Как проще всего найти, если два списка содержат одинаковые элементы в стандартных библиотеках Java?

Не должно иметь значения, являются ли два списка одинаковым экземпляром или нет, и не должно иметь значения, отличаются ли параметры типа списков.

например

List list1
List<String> list2; 
// ... construct etc

list1.add("A");
list2.add("A"); 
// the function, given these two lists, should return true

Там, наверное, что-то смотрит мне в лицо, которое я знаю :-)


РЕДАКТИРОВАТЬ: Чтобы уточнить, я искал точно так же элементы и количество элементов, по порядку.

Ответ 1

Если вы заботитесь о порядке, просто используйте метод equals:

list1.equals(list2)

От Javadoc:

Сравнивает указанный объект с этим списком на равенство. Возвращает true, если и только если указанный объект также является списком, оба списка имеют одинаковый размер, и все соответствующие пары элементов в двух списках равны. (Два элемента e1 и e2 равны, если (e1 == null? E2 == null: e1.equals(e2)).) Другими словами, два списка определяются как равные, если они содержат одинаковые элементы в одинаковом порядке., Это определение гарантирует, что метод equals работает должным образом в разных реализациях интерфейса List.

Если вы хотите проверять независимо от порядка, вы можете скопировать все элементы в Sets и использовать equals в результирующих наборах:

public static <T> boolean listEqualsIgnoreOrder(List<T> list1, List<T> list2) {
    return new HashSet<>(list1).equals(new HashSet<>(list2));
}

Ограничением этого подхода является то, что он не только игнорирует порядок, но и частоту повторяющихся элементов. Например, если list1 был ["A", "B", "A"], а list2 был ["A", "B", "B"], подход Set рассматривал бы их как равные.

Если вам нужно быть нечувствительным к порядку, но чувствительным к частоте дублирования, вы можете:

Ответ 2

Я отправил кучу вещей в комментарии, я думаю, что он гарантирует свой собственный ответ.

Как все говорят здесь, использование equals() зависит от порядка. Если вам не нужен порядок, у вас есть 3 варианта.

Вариант 1

Используйте containsAll(). Этот вариант не идеален, на мой взгляд, потому что он предлагает наихудшую производительность, O (n ^ 2).

Вариант 2

Есть два варианта:

2a) Если вы не заботитесь о сохранении порядка ваших списков... используйте Collections.sort() в обоих списках. Затем используйте equals(). Это O (nlogn), потому что вы делаете два вида, а затем сравниваете O (n).

2b) Если вам нужно сохранить порядок списков, вы можете скопировать оба списка в первую очередь. THEN вы можете использовать решение 2a в обоих скопированных списках. Однако это может быть непривлекательным, если копирование очень дорого.

Это приводит к:

Вариант 3

Если ваши требования совпадают с частью 2b, но копирование слишком дорого. Вы можете использовать TreeSet для сортировки для вас. Дамп каждого списка в свой собственный TreeSet. Он будет отсортирован в наборе, и исходные списки останутся нетронутыми. Затем выполните сравнение equals() на TreeSet s. TreeSets можно построить в O (nlogn) времени, а equals() - O (n).

Возьмите свой выбор: -).

EDIT: Я почти забыл ту же оговорку, что указывает Лоуренс Гонсалвис. Реализация TreeSet устраняет дубликаты. Если вам нужны дубликаты, вам понадобится какой-то сортированный мультимножество.

Ответ 3

Если вы используете (или с удовольствием используете) Коллекции Apache Commons, вы можете использовать CollectionUtils.isEqualCollection, которая "возвращает истину, если указанные Коллекции содержат точно такие же элементы с одинаковыми кардинальными числами".

Ответ 4

Очень поздно вечеринке, но хотелось добавить эту нулевую безопасную проверку:

Objects.equals(list1, list2)

Ответ 5

Я знаю, что это старый поток, но ни один из других ответов полностью не разрешил мой вариант использования (я думаю, что Guava Multiset может сделать то же самое, но здесь нет примера). Пожалуйста, извините мое форматирование. Я по-прежнему новичок в публикации на стеке. Кроме того, дайте мне знать, если есть ошибки

Предположим, что у вас есть List<T> a и List<T> b, и вы хотите проверить, равны ли они следующим условиям:

1) ожидаемое время работы O (n)
2) Равенство определяется как: для всех элементов в или b количество раз, когда элемент происходит в a, равно числу раз, когда элемент встречается в b. Равенство элемента определяется как T.equals()

private boolean listsAreEquivelent(List<? extends Object> a, List<? extends Object> b) {
    if(a==null) {
        if(b==null) {
            //Here 2 null lists are equivelent. You may want to change this.
            return true;
        } else {
            return false;
        }
    }
    if(b==null) {
        return false;
    }
    Map<Object, Integer> tempMap = new HashMap<>();
    for(Object element : a) {
        Integer currentCount = tempMap.get(element);
        if(currentCount == null) {
            tempMap.put(element, 1);
        } else {
            tempMap.put(element, currentCount+1);
        }
    }
    for(Object element : b) {
        Integer currentCount = tempMap.get(element);
        if(currentCount == null) {
            return false;
        } else {
            tempMap.put(element, currentCount-1);
        }
    }
    for(Integer count : tempMap.values()) {
        if(count != 0) {
            return false;
        }
    }
    return true;
}

Время выполнения - O (n), потому что мы делаем вставки O (2 * n) в хэш-карту и выбираем hashmap O (3 * n). Я не полностью тестировал этот код, поэтому будьте осторожны:)

//Returns true:
listsAreEquivelent(Arrays.asList("A","A","B"),Arrays.asList("B","A","A"));
listsAreEquivelent(null,null);
//Returns false:
listsAreEquivelent(Arrays.asList("A","A","B"),Arrays.asList("B","A","B"));
listsAreEquivelent(Arrays.asList("A","A","B"),Arrays.asList("A","B"));
listsAreEquivelent(Arrays.asList("A","A","B"),null);

Ответ 6

Попробуйте эту версию, которая не требует, чтобы порядок был таким же, но поддерживает множественность одного и того же значения. Они соответствуют только, если каждый имеет одинаковое количество любого значения.

public boolean arraysMatch(List<String> elements1, List<String> elements2) {
    // Optional quick test since size must match
    if (elements1.size() != elements2.size()) {
        return false;
    }
    List<String> work = newArrayList(elements2);
    for (String element : elements1) {
        if (!work.remove(element)) {
            return false;
        }
    }
    return work.isEmpty();
}

Ответ 7

Решение для случая, когда два списка имеют одинаковые элементы, но различный порядок:

public boolean isDifferentLists(List<Integer> listOne, List<Integer> listTwo) {
    if(isNullLists(listOne, listTwo)) {
        return false;
    }

    if (hasDifferentSize(listOne, listTwo)) {
        return true;
    }

    List<Integer> listOneCopy = Lists.newArrayList(listOne);
    List<Integer> listTwoCopy = Lists.newArrayList(listTwo);
    listOneCopy.removeAll(listTwoCopy);

    return CollectionUtils.isNotEmpty(listOneCopy);
}

private boolean isNullLists(List<Integer> listOne, List<Integer> listTwo) {
    return listOne == null && listTwo == null;
}

private boolean hasDifferentSize(List<Integer> listOne, List<Integer> listTwo) {
    return (listOne == null && listTwo != null) || (listOne != null && listTwo == null) || (listOne.size() != listTwo.size());
}

Ответ 8

Том отвечает отлично, я полностью согласен с его ответами!

Интересный аспект этого вопроса заключается в том, нужен ли вам сам тип List и его внутреннее упорядочение.

Если нет, вы можете перейти на Iterable или Collection что дает вам некоторую гибкость при передаче структур данных, отсортированных по времени вставки, а не в тот момент, когда вы хотите проверить.

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

Если порядок имеет значение, но определяется временем вставки (и у вас нет дубликатов), рассмотрите LinkedHashSet который похож на TreeSet, но упорядочен по времени вставки (дубликаты не учитываются). Это также дает вам O(1) амортизированный доступ за O(log n).

Ответ 9

Метод equals в List сделает это, списки упорядочены, поэтому, чтобы быть равными, два списка должны иметь одинаковые элементы в одном порядке.

return list1.equals(list2);

Ответ 10

list1.equals(list2);

Если ваш список содержит пользовательский класс MyClass, этот класс должен переопределить функцию equals.

 class MyClass
  {
  int field=0;
  @0verride
  public boolean equals(Object other)
        {
        if(this==other) return true;
        if(other==null || !(other instanceof MyClass)) return false;
        return this.field== MyClass.class.cast(other).field;
        }
  }

Примечание: если вы хотите проверить равно на java.util.Set, а не на java.util.List, тогда ваш объект должен переопределить функцию hashCode.

Ответ 11

Пример кода:

public static '<'T'>' boolean isListDifferent(List'<'T'>' previousList,
        List'<'T'>' newList) {

    int sizePrevoisList = -1;
    int sizeNewList = -1;

    if (previousList != null && !previousList.isEmpty()) {
        sizePrevoisList = previousList.size();
    }
    if (newList != null && !newList.isEmpty()) {
        sizeNewList = newList.size();
    }

    if ((sizePrevoisList == -1) && (sizeNewList == -1)) {
        return false;
    }

    if (sizeNewList != sizePrevoisList) {
        return true;
    }

    List n_prevois = new ArrayList(previousList);
    List n_new = new ArrayList(newList);

    try {
        Collections.sort(n_prevois);
        Collections.sort(n_new);
    } catch (ClassCastException exp) {
        return true;
    }

    for (int i = 0; i < sizeNewList; i++) {
        Object obj_prevois = n_prevois.get(i);
        Object obj_new = n_new.get(i);
        if (obj_new.equals(obj_prevois)) {
            // Object are same
        } else {
            return true;
        }
    }

    return false;
}

Ответ 12

В дополнение к ответу Лоуренса, если вы также хотите сделать его нулевым:

private static <T> boolean listEqualsIgnoreOrder(List<T> list1, List<T> list2) {
    if (list1 == null)
        return list2==null;
    if (list2 == null)
        return list1 == null;
    return new HashSet<>(list1).equals(new HashSet<>(list2));
}

Ответ 15

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

Предположения, если оба списка имеют значения NULL, я считаю их равными.

private boolean compareLists(List<?> l1, List<?> l2) {
    if (l1 == null && l2 == null) {
        return true;
    } else if (l1 == null || l2 == null) {
        return false;
    }

    if (l1.size() != l2.size()) {
        return false;
    }

    Map<?, Integer> m1 = toMap(l1);
    Map<?, Integer> m2 = toMap(l2);

    return m1.equals(m2);
}

private Map<Object, Integer> toMap(List<?> list) {
    //Effective size, not to resize in the future.
    int mapSize = (int) (list.size() / 0.75 + 1);
    Map<Object, Integer> map = new HashMap<>(mapSize);

    for (Object o : list) {
        Integer count = map.get(o);
        if (count == null) {
            map.put(o, 1);
        } else {
            map.put(o, ++count);
        }
    }

    System.out.println(map);
    return map;
}

Пожалуйста, обратите внимание, что для этих объектов должно быть правильно определено значение equals. fooobar.com/questions/39924/...

Ответ 16

Это зависит от того, какой конкретный класс List вы используете. Абстрактный класс AbstractCollection имеет метод, называемый containsAll (Collection), который берет другую коллекцию (список - это коллекция) и:

Возвращает true, если эта коллекция содержит все элементы указанной коллекции.

Итак, если ArrayList передается, вы можете вызвать этот метод, чтобы убедиться, что он точно такой же.

       List foo = new ArrayList();
    List bar = new ArrayList();
    String str = "foobar";

    foo.add(str);
    bar.add(str);

    foo.containsAll(bar);

Причина для containsAll() заключается в том, что он выполняет итерацию через первый список, который ищет совпадение во втором списке. Поэтому, если они не соответствуют порядку, то equals() не подберет его.

EDIT: Я просто хочу сделать здесь комментарий об амортизированном времени выполнения различных предлагаемых вариантов. Важно ли время работы? Конечно. Это единственное, что вы должны учитывать? Нет.

Стоимость копирования КАЖДЫЙ один элемент из ваших списков в другие списки требует времени, а также занимает хороший кусок памяти (эффективно удваивая используемую память).

Итак, если память в вашей JVM не является проблемой (как правило, она должна быть), вам все равно нужно учитывать время, необходимое для копирования каждого элемента из двух списков на два набора TreeSets. Помните, что он сортирует каждый элемент, когда он входит в них.

Мой последний совет? Вам необходимо рассмотреть свой набор данных и количество элементов, которые у вас есть в вашем наборе данных, а также насколько велики каждый объект в вашем наборе данных, прежде чем вы сможете принять здесь хорошее решение. Играйте с ними, создавайте их каждый и смотрите, какой из них работает быстрее. Это хорошее упражнение.