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

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 
}  

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

Woot4Moo
источник
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. Это позволит вам проверить, содержит ли один другой элемент в постоянное время.

npinti
источник
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

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

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

удачи

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

REL
источник
0

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

boolean var = lis1.stream().filter(element -> list2.contains(element)).findFirst().isPresent();
Нарендра Джагги
источник
0

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

if (list1.contains(Object o))
{
   //do this
}
Факипо
источник