Проверьте, содержит ли один список элемент из другого


108

У меня есть два списка с разными объектами.

List<Object1> list1;
List<Object2> list2;

Я хочу проверить, существует ли элемент из списка1 в списке2 на основе определенного атрибута (Object1 и Object2 имеют (среди прочего) один общий атрибут (с типом Long) с именем attributeSame).

щас делаю вот так:

boolean found = false;
for(Object1 object1 : list1){
   for(Object2 object2: list2){
       if(object1.getAttributeSame() == object2.getAttributeSame()){
           found = true;
           //also do something
       }
    }
    if(!found){
        //do something
    }
    found = false;
}

Но я думаю, что есть лучший и более быстрый способ сделать это :) Может кто-нибудь предложить?

Спасибо!


во-первых, когда вы устанавливаете found = true; тогда просто сломайте; или выходите из
строя

stackoverflow.com/questions/5187888/… . Более того, для быстрого поиска попробуйте использовать двоичный поиск и измените свой DS в соответствии с ситуацией ...
jsist 03

имеют ли они общего родителя помимо Object?
Woot4Moo 03

@ Woot4Moo нет, они этого не делают
Нед

Ответы:


226

Если вам просто нужно проверить базовое равенство, это можно сделать с помощью базового JDK без изменения списков ввода в одной строке

!Collections.disjoint(list1, list2);

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

list1.stream()
   .map(Object1::getProperty)
   .anyMatch(
     list2.stream()
       .map(Object2::getProperty)
       .collect(toSet())
       ::contains)

... который собирает различные значения list2и проверяет каждое значение list1на наличие.


1
Разве это не всегда будет возвращать false, поскольку это два разных объекта?
Venki

2
Эм нет? Disjoint проверяет, нет ли друг другу объектов equals () между двумя коллекциями.
Луи Вассерман

13
Также обратите внимание, что для списков это будет O (n * m); если вы хотите скопировать list1в a Setперед сравнением, вы получите O (n) + O (m), то есть O (n + m), за счет некоторой дополнительной RAM; это вопрос выбора между скоростью или памятью.
Haroldo_OK 01

Это будет работать, только если «List <Person> list1; List <Person> list2», но не для двух разных объектов или типов данных, таких как List <Person> list1; Список <Сотрудник> list2.
whoami

Конечно, это не сработает для этих сценариев @Zephyr, на заданный вопрос он работает идеально, если у вас есть правильные равные. это все имеет значение!
Сайед Сирадж Уддин

38

Вы можете использовать Apache Commons CollectionUtils :

if(CollectionUtils.containsAny(list1,list2)) {  
    // do whatever you want
} else { 
    // do other thing 
}  

Это предполагает, что вы правильно перегрузили функцию равенства для своих настраиваемых объектов.


9
прошло 4 года, и я также явно называю пакет и функцию.
Woot4Moo

1
Голосование против apache commons, когда есть решение только для
jdk

9
@ohcibi Java также имеет встроенный регистратор, вы должны голосовать против людей, которые предлагают использовать Log4j и Log4j2, пока вы на нем.
Woot4Moo

1
@ Woot4Moo это зависит от обстоятельств. Нет причин понижать голос, если есть причина использовать Log4j для решения проблемы OP. В этом случае apache commons будет просто бесполезным раздуванием, как в 99% ответов, которые предлагают apache commons.
ohcibi

1
@ohcibi, но если вы уже используете общие ресурсы Apache, то это не совсем раздувание. Это был хороший ответ.
vab2048

20

Чтобы сократить логику Нарендры, вы можете использовать это:

boolean var = lis1.stream().anyMatch(element -> list2.contains(element));

3
этот ответ недооценен.
Kervvv

1
Если хотите, можете сократить его еще немного:list1.stream().anyMatch(list2::contains);
тарка

9

Существует один метод с Collectionименем named, retainAllно с некоторыми побочными эффектами для справки.

Сохраняет только те элементы в этом списке, которые содержатся в указанной коллекции (дополнительная операция). Другими словами, удаляет из этого списка все его элементы, не содержащиеся в указанной коллекции.

истина, если этот список изменился в результате вызова

Это как

boolean b = list1.retainAll(list2);

5

Ответ Лойуса правильный, я просто хочу добавить пример:

listOne.add("A");
listOne.add("B");
listOne.add("C");

listTwo.add("D");
listTwo.add("E");
listTwo.add("F");      

boolean noElementsInCommon = Collections.disjoint(listOne, listTwo); // true

1
Я думаю, если вы добавите элемент «A» во второй список listTwo.add («A»); хотя Collections.disjoint (listOne, listTwo); возвращает истину.
Сайрам Кукадала

2

более быстрый способ потребует дополнительного места.

Например:

  1. поместите все элементы в один список в HashSet (вы должны реализовать хэш-функцию самостоятельно, чтобы использовать object.getAttributeSame ())

  2. Просмотрите другой список и проверьте, есть ли какой-либо элемент в HashSet.

Таким образом, каждый объект посещается не более одного раза. и HashSet достаточно быстр, чтобы проверить или вставить любой объект в O (1).


2

Согласно JavaDoc для .contains(Object obj):

Возвращает истину, если этот список содержит указанный элемент. Более формально, возвращает true тогда и только тогда, когда этот список содержит хотя бы один элемент e такой, что (o == null? E == null: o.equals (e)).

Итак, если вы переопределите свой .equals()метод для данного объекта, вы должны иметь возможность:if(list1.contains(object2))...

Если элементы будут уникальными (т. Е. Иметь разные атрибуты), вы можете переопределить .equals()и .hashcode()и сохранить все в HashSets. Это позволит вам проверить, содержит ли один другой элемент в постоянное время.


2

чтобы было быстрее, можно добавить перерыв; таким образом цикл остановится, если для параметра found установлено значение true:

boolean found = false;
for(Object1 object1 : list1){
   for(Object2 object2: list2){
       if(object1.getAttributeSame() == object2.getAttributeSame()){
           found = true;
           //also do something  
           break;
       }
    }
    if(!found){
        //do something
    }
    found = false;
}

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


Привет, Том, спасибо, что заметил! Да, забыл "ломать" при наборе текста. Но я подумал, может быть, есть какой-то алгоритм, или мне стоит изменить эти списки на какие-то другие коллекции.
Нед

разве нет ничего лучше О (н * м)?
Woot4Moo

.getAttributeSame ()?
Maveň ツ

Реализация метода getAttributeSame () из Object1 и Object2 не предоставляется, но также не имеет отношения к вопросу и ответу; он просто возвращает атрибут (attributeSame, a Long), который есть у обоих классов.
Том

0

Можете ли вы определить тип данных, которые вы храните? это большие данные? это отсортировано? Я думаю, что нужно учитывать разные подходы к эффективности в зависимости от данных.

Например, если ваши данные большие и несортированные, вы можете попытаться перебрать два списка вместе по индексу и сохранить каждый атрибут списка в другом помощнике списка. тогда вы можете перекрестно проверить текущие атрибуты в списках помощников.

удачи

отредактировал: и я бы не рекомендовал перегружать равные. это опасно и, вероятно, против вашего смысла объекта.



0

С помощью java 8мы можем сделать, как показано ниже, чтобы проверить, содержит ли один список какой-либо элемент другого списка.

boolean var = lis1.stream().filter(element -> list2.contains(element)).findFirst().isPresent();

0

Если вы хотите проверить, существует ли элемент в списке, используйте метод contains.

if (list1.contains(Object o))
{
   //do this
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.