java 使用java 8流在2个列表中查找元素匹配

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/34130697/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 22:31:09  来源:igfitidea点击:

Find element matching in 2 lists using java 8 stream

javajava-8java-stream

提问by TNN

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 list1that has the same name and age in list2but don't mind about id.

我想知道里面list1有没有同名同龄list2但不介意的人id

What is best and fast way?

什么是最好和快速的方法?

采纳答案by Holger

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:

为自己定义一个关键对象,用于保存和比较所需的属性。在这种简单的情况下,您可以使用一个小列表,而每个索引对应一个属性。对于更复杂的情况,您可以使用Map(使用属性名称作为键)或专用类:

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 Setto accelerate the lookup (changing the task's time complexity from O(n2)to O(n)):

当您有相当大的列表时,这可能会导致性能不佳。当您有大型列表(或无法预测它们的大小)时,您应该使用中间体Set来加速查找(将任务的时间复杂度从 更改O(n2)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);

回答by Tunaki

A simple way to do that is to override equalsand hashCode. Since I assume the equality between Personmust also consider the idfield, you can wrap this instance into a PersonWrapperwhich will implement the correct equalsand hashCode(i.e. only check the nameand agefields):

一个简单的方法是覆盖equalshashCode。由于我假设之间的相等性Person还必须考虑id字段,因此您可以将此实例包装为PersonWrapper将实现正确的equalsand hashCode(即仅检查nameage字段):

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 list2into a Setof wrapped persons. The goal of having a Setis to have a constant-time containsoperation for better performance.

此代码将 转换list2Set被包裹的人。拥有 a 的目标Set是进行恒定时间contains操作以获得更好的性能。

Then, the list1is filtered. Every element found in set2is 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.

然后,list1过滤。在set2中找到的每个元素都被保留,如果还有一个元素留下(也就是说,如果findFirst()返回一个非空的Optional),这意味着找到了一个元素。

回答by dkulkarni

Well if you don't care about the idfield, then you can use the equalsmethod to solve this.

那么如果你不关心id领域,那么你可以使用equals方法来解决这个问题。

Here's the Personclass code

这是Person课程代码

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. commonwill contain all Personobjects where nameand ageare the same.

现在,您可以使用流来获得这样的交集。common将包含和相同的所有Person对象。nameage

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

回答by Mrinal

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

蛮力,但纯 java 8 解决方案将是这样的:

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. limitis used as we are interested in first match only, in which case, we do not need to traverse further.

在这里,flatmap 用于连接 2 个列表。limit使用是因为我们只对第一次匹配感兴趣,在这种情况下,我们不需要进一步遍历。

回答by Faiz Akram

<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/

回答by Prashant

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 didi you get such a requirement :)

由于forEach()采取消费者,我们没有办法返回,PresentOrNot {}是一种解决方法。旁白:你从哪里得到这样的要求:)

回答by Víctor Gómez

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
        }
    }
}

回答by Yoga Gowda

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;
}