TNN
TNN

Reputation: 436

Find element matching in 2 lists using java 8 stream

My case is:

class Person {
    String id ;
    String name;
    String age;
}
List<Person> list1 = {p1,p2, p3};
List<Person> list2 = {p4,p5, p6}; 

I want to know if there is person in list1 that has the same name and age in list2 but don't mind about id.

What is best and fast way?

Upvotes: 12

Views: 45589

Answers (8)

Prashant
Prashant

Reputation: 5383

This would work:

class PresentOrNot { boolean isPresent = false; };
final PresentOrNot isPresent = new PresentOrNot ();
l1.stream().forEach(p -> {
    isPresent.isPresent = isPresent.isPresent || l2.stream()
        .filter(p1 -> p.name.equals(p1.name) && p.age.equals(p1.age))
        .findFirst()
        .isPresent();
});
System.err.println(isPresent.isPresent);

Since forEach() takes Consumer, we have no way of returning and PresentOrNot {} is a workaround.

Aside : Where did you get such a requirement ? :)

Upvotes: 2

Yoga Gowda
Yoga Gowda

Reputation: 367

public static void main(String[] args) {
    OTSQuestions ots = new OTSQuestions();

    List<Attr> attrs = ots.getAttrs();
    List<String> ids = new ArrayList<>();
    ids.add("101");
    ids.add("104");
    ids.add("102");

    List<Attr> finalList = attrs.stream().filter(
            attr -> ids.contains(attr.getId()))
            .collect(Collectors.toList());
}

public class Attr {
    private String id;
    private String name;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

private List<Attr> getAttrs() {
    List<Attr> attrs = new ArrayList<>();
    Attr attr = new Attr();
    attr.setId("100");
    attr.setName("Yoga");
    attrs.add(attr);

    Attr attr1 = new Attr();
    attr1.setId("101");
    attr1.setName("Yoga1");
    attrs.add(attr1);

    Attr attr2 = new Attr();
    attr2.setId("102");
    attr2.setName("Yoga2");
    attrs.add(attr2);

    Attr attr3 = new Attr();
    attr3.setId("103");
    attr3.setName("Yoga3");
    attrs.add(attr3);

    Attr attr4 = new Attr();
    attr4.setId("104");
    attr4.setName("Yoga4");
    attrs.add(attr4);

    return attrs;
}

Upvotes: 0

Faiz Akram
Faiz Akram

Reputation: 558

<h3>Find List of Object passing String of Array Using java 8?</h3>
[Faiz Akram][1]
    <pre>
    public class Student {
        private String name;
        private Integer age;
        public Student(String name, Integer age) {
            super();
            this.name = name;
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Integer getAge() {
            return age;
        }
        public void setAge(Integer age) {
            this.age = age;
        }
    }
    </pre>
    // Main Class
    <pre>
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    import java.util.stream.Collectors;
    public class JavaLamda {
        public static void main(String[] k)
        {
        List<Student> stud = new ArrayList<Student>();  
        stud.add(new Student("Faiz", 1));
        stud.add(new Student("Dubai", 2));
        stud.add(new Student("Akram", 5));
        stud.add(new Student("Rahul", 3));
        String[] name= {"Faiz", "Akram"};
        List<Student> present = Arrays.asList(name)
                .stream()
                .flatMap(x -> stud
                    .stream()
                    .filter(y -> x.equalsIgnoreCase(y.getName())))
                .collect(Collectors.toList());
        System.out.println(present);
        }
    }
    </pre>
    OutPut //[Student@404b9385, Student@6d311334]


  [1]: http://faizakram.com/blog/find-list-object-passing-string-array-using-java-8/

Upvotes: 3

Holger
Holger

Reputation: 298143

Define yourself a key object that holds and compares the desired properties. In this simple case, you may use a small list whereas each index corresponds to one property. For more complex cases, you may use a Map (using property names as keys) or a dedicated class:

Function<Person,List<Object>> toKey=p -> Arrays.asList(p.getName(), p.getAge());

Having such a mapping function. you may use the simple solution:

list1.stream().map(toKey)
     .flatMap(key -> list2.stream().map(toKey).filter(key::equals))
     .forEach(key -> System.out.println("{name="+key.get(0)+", age="+key.get(1)+"}"));

which may lead to poor performance when you have rather large lists. When you have large lists (or can’t predict their sizes), you should use an intermediate Set to accelerate the lookup (changing the task’s time complexity from O(n²) to O(n)):

list2.stream().map(toKey)
     .filter(list1.stream().map(toKey).collect(Collectors.toSet())::contains)
     .forEach(key -> System.out.println("{name="+key.get(0)+", age="+key.get(1)+"}"));

In the examples above, each match gets printed. If you are only interested in whether such a match exists, you may use either:

boolean exists=list1.stream().map(toKey)
     .anyMatch(key -> list2.stream().map(toKey).anyMatch(key::equals));

or

boolean exists=list2.stream().map(toKey)
     .anyMatch(list1.stream().map(toKey).collect(Collectors.toSet())::contains);

Upvotes: 14

Mrinal
Mrinal

Reputation: 1906

Brute force, but pure java 8 solution will be this:

boolean present = list1
        .stream()
        .flatMap(x -> list2
            .stream()
            .filter(y -> x.getName().equals(y.getName()))
            .filter(y -> x.getAge().equals(y.getAge()))
            .limit(1))
        .findFirst()
        .isPresent();

Here, flatmap is used to join 2 lists. limit is used as we are interested in first match only, in which case, we do not need to traverse further.

Upvotes: 6

dkulkarni
dkulkarni

Reputation: 2830

Well if you don't care about the id field, then you can use the equals method to solve this.

Here's the Person class code

public class Person {
  private String id ;
  private String name;
  private String age;

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    Person sample = (Person) o;

    if (!name.equals(sample.name)) return false;
    return age.equals(sample.age);

  }

  @Override
  public int hashCode() {
    int result = name.hashCode();
    result = 31 * result + age.hashCode();
    return result;
  }
}

Now, you can use stream to get the intersection like so. common will contain all Person objects where name and age are the same.

List<Person> common = list1
      .stream()
      .filter(list2::contains)
      .collect(Collectors.toList());

Upvotes: 3

Tunaki
Tunaki

Reputation: 137084

A simple way to do that is to override equals and hashCode. Since I assume the equality between Person must also consider the id field, you can wrap this instance into a PersonWrapper which will implement the correct equals and hashCode (i.e. only check the name and age fields):

class PersonWrapper {

    private Person person;

    private PersonWrapper(Person person) {
        this.person = person;
    }

    public static PersonWrapper wrap(Person person) {
        return new PersonWrapper(person);
    }

    public Person unwrap() {
        return person;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }
        PersonWrapper other = (PersonWrapper) obj;
        return person.name.equals(other.person.name) && person.age.equals(other.person.age);
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + person.name.hashCode();
        result = prime * result + person.age.hashCode();
        return result;
    }

}

With such a class, you can then have the following:

Set<PersonWrapper> set2 = list2.stream().map(PersonWrapper::wrap).collect(toSet());

boolean exists =
    list1.stream()
         .map(PersonWrapper::wrap)
         .filter(set2::contains)
         .findFirst()
         .isPresent();

System.out.println(exists);

This code converts the list2 into a Set of wrapped persons. The goal of having a Set is to have a constant-time contains operation for better performance.

Then, the list1 is filtered. Every element found in set2 is kept and if there is an element left (that is to say, if findFirst() returns a non empty Optional), it means an element was found.

Upvotes: 7

V&#237;ctor G&#243;mez
V&#237;ctor G&#243;mez

Reputation: 722

You need to iterate over the two lists and compare the atributtes.

for(Person person1 : list1) {
    for(Person person2 : list2) {
        if(person1.getName().equals(person2.getName()) && 
                person1.getAge().equals(person2.getAge())) {
            //your code
        }
    }
}

Upvotes: 1

Related Questions