Java 8 Streams - 比较两个列表的对象值并将值添加到新列表? [英] Java 8 Streams - Compare two Lists' object values and add value to new List?

查看:45
本文介绍了Java 8 Streams - 比较两个列表的对象值并将值添加到新列表?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个 List 包含这个类的对象:

I have two Lists containing objects of this class:

public class SchoolObj
{
    private String name;
    private String school;

    public SchoolObj()
    {
        this(null, null);
    }

    public SchoolObj(String nameStr, String schoolStr)
    {
        this.setName(nameStr);
        this.setSchool(schoolStr);
    }

    public String getName()
    {
        return this.name;
    }

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

    public String getSchool()
    {
        return this.school;
    }

    public void setSchool(String school)
    {
        this.school = school;
    }

    @Override
    public String toString()
    {
        return this.getName() + ' ' + this.getSchool();
    }
}

我想通过nameschool 比较这两个列表中的对象.如果它们相等,我需要创建一个新的 List,其中包含在两个列表中都可以找到的那些 SchoolObj 对象.

I want to compare the objects in those two lists by name and school. If they are equal I need to create a new List containing those SchoolObj objects which are found in both Lists.

我知道我们可以使用两个 for 循环,并在下面的 createSharedListViaLoop 方法中进行.

I know we can use two for loops and do it is in the createSharedListViaLoop method below.

我的问题是,我怎样才能用 Java 流完成同样的事情?

My question is, how can I accomplish the same thing with Java streams?

我尝试使用下面的 createSharedListViaStream,但它没有按预期工作.

I tried with createSharedListViaStream below, but it is not working as expected.

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class StreamTest
{
    public static void main(String[] args)
    {
        List<SchoolObj> listOne = new ArrayList<SchoolObj>();
        // TODO: Add sample data to listOne.
        listOne.add(new SchoolObj("nameA", "schoolX"));
        listOne.add(new SchoolObj("nameC", "schoolZ"));

        List<SchoolObj> listTwo = new ArrayList<SchoolObj>();
        // TODO: Add sample data to listTwo.
        listTwo.add(new SchoolObj("nameA", "schoolX"));
        listTwo.add(new SchoolObj("nameB", "schoolY"));

        // Print results from loop method.
        System.out.println("Results from loop method:");
        List<SchoolObj> resultsViaLoop = StreamTest.createSharedListViaLoop(listOne, listTwo);
        for (SchoolObj obj : resultsViaLoop)
        {
            System.out.println(obj);
        }

        // Print results from stream method.
        System.out.println("Results from stream method:");
        List<SchoolObj> resultsViaStream = StreamTest.createSharedListViaStream(listOne, listTwo);
        for (SchoolObj obj : resultsViaStream)
        {
            System.out.println(obj);
        }
    }

    public static List<SchoolObj> createSharedListViaLoop(List<SchoolObj> listOne, List<SchoolObj> listTwo)
    {
        List<SchoolObj> result = new ArrayList<SchoolObj>();

        for (SchoolObj one : listOne)
        {
            for (SchoolObj two : listTwo)
            {
                if (one.getName().equals(two.getName()) && one.getSchool().equals(two.getSchool()))
                {
                    result.add(one);
                }
            }
        }

        return result;
    }

    public static List<SchoolObj> createSharedListViaStream(List<SchoolObj> listOne, List<SchoolObj> listTwo)
    {
        List<SchoolObj> listOneList = listOne.stream().filter(two -> listTwo.stream()
              .anyMatch(one -> one.getName().equals(two.getName()) && two.getSchool().equals(one.getSchool()))) 
              .collect(Collectors.toList());

        return listOneList;
    }
}

推荐答案

让我们跑一遍代码的每一部分.首先,createSharedListViaStream:

Let's run through each part of the code. First, createSharedListViaStream:

public static List<SchoolObj> createSharedListViaStream(List<SchoolObj> listOne, List<SchoolObj> listTwo)
{
    // We create a stream of elements from the first list.
    List<SchoolObj> listOneList = listOne.stream()
    // We select any elements such that in the stream of elements from the second list
    .filter(two -> listTwo.stream()
    // there is an element that has the same name and school as this element,
        .anyMatch(one -> one.getName().equals(two.getName()) 
            && two.getSchool().equals(one.getSchool())))
    // and collect all matching elements from the first list into a new list.
    .collect(Collectors.toList());
    // We return the collected list.
    return listOneList;
}

在运行完代码后,它会完全按照您的意愿去做.现在,让我们运行createSharedListViaLoop:

After running through the code, it does exactly what you want it to do. Now, let's run through createSharedListViaLoop:

public static List<SchoolObj> createSharedListViaLoop(List<SchoolObj> listOne, List<SchoolObj> listTwo)
{
    // We build up a result by...
    List<SchoolObj> result = new ArrayList<SchoolObj>();
    // going through each element in the first list,
    for (SchoolObj one : listOne)
    {
    // going through each element in the second list,
        for (SchoolObj two : listTwo)
        {
    // and collecting the first list's element if it matches the second list's element.
            if (one.getName().equals(two.getName()) && one.getSchool().equals(two.getSchool()))
            {
                result.add(one);
            }
        }
    }
    // We return the collected list
    return result;
}

到目前为止,还不错……对吧?实际上,您在createSharedListViaStream 中的代码从根本上是正确的;相反,是您的 createSharedListViaLoop 可能导致输出出现差异.

So far, so good... right? In fact, your code in createSharedListViaStream is fundamentally correct; instead, it is your createSharedListViaLoop that may be causing discrepancies in output.

考虑以下输入集:
List1 = [SchoolObj("nameA","SchoolX"), SchoolObj("nameC","SchoolZ")]
List2 = [SchoolObj("nameA","SchoolX"), SchoolObj("nameA","SchoolX"), SchoolObj("nameB","SchoolY")]

此处,createSharedListViaStream 将返回出现在两个列表中的第一个列表的唯一元素:SchoolObj("nameA","SchoolX").但是,createSharedListViaLoop 将返回以下列表:[SchoolObj("nameA","SchoolX"),SchoolObj("nameA","SchoolX")].更准确地说,createSharedListViaLoop 将收集正确的对象,但它会这样做两次.我怀疑这是基于与 createSharedListViaLoop 输出的比较,createSharedListViaStream 的输出不正确"的原因.

Here, createSharedListViaStream will return the only element of the first list that appears in both lists: SchoolObj("nameA","SchoolX"). However, createSharedListViaLoop will return the following list: [SchoolObj("nameA","SchoolX"),SchoolObj("nameA","SchoolX")]. More precisely, createSharedListViaLoop will collect the correct object, but it will do so twice. I suspect this to be the reason for the output of createSharedListViaStream to be "incorrect" based on comparison to the output of createSharedListViaLoop.

createSharedListViaLoop 进行这种复制的原因是没有终止其内部 for 循环.尽管我们遍历第一个列表的所有元素以检查它们是否存在于第二个列表中,但找到单个匹配项就足以将该元素添加到结果中.我们可以通过将内部循环更改为以下内容来避免添加冗余元素:

The reason that createSharedListViaLoop does this duplication is based on the lack of termination of its inner for loop. Although we iterate over all elements of the first list to check if they are present in the second, finding a single match will suffice to add the element to the result. We can avoid redundant element addition by changing the inner loop to the following:

for (SchoolObj one : listOne)
    {
    for (SchoolObj two : listTwo)
    {
        if (one.getName().equals(two.getName()) && one.getSchool().equals(two.getSchool()))
        {
            result.add(one);
            break;
        }
    }
}

此外,如果您不希望列表中出现重复的对象(按内存中的位置),您可以使用 distinct 像这样:

Additionally, if you don't want duplicate Objects in your list (by location in memory), you can use distinct like so:

List<SchoolObj> result = ...;
result = result.stream().distinct().collect(Collectors.toList());

作为最后的警告,上述内容将在以下情况下保持不同的结果:

As a final caution, the above will keep the results distinct in the following scenario:

List<SchoolObj> list = new ArrayList<>();
SchoolObj duplicate = new SchoolObj("nameC", "schoolD");
listOne.add(duplicate);
listOne.add(duplicate);
list.stream().distinct().forEach(System.out::println); 
// prints:
// nameC schoolD

但是,它在以下场景中不起作用,除非您覆盖 equals SchoolObj 方法:

However, it will not work in the following scenario, unless you override the equals method for SchoolObj:

List<SchoolObj> list = new ArrayList<>();
listOne.add(new SchoolObj("nameC", "schoolD"));
listOne.add(new SchoolObj("nameC", "schoolD"));
list.stream().distinct().forEach(System.out::println); 
// prints (unless Object::equals overridden)
// nameC schoolD
// nameC schoolD

这篇关于Java 8 Streams - 比较两个列表的对象值并将值添加到新列表?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆