通过匹配其他两个列表中的条目来创建新的Java对象列表

通过匹配其他两个列表中的条目来创建新的Java对象列表,java,java-8,java-stream,Java,Java 8,Java Stream,假设我有这两个列表 List<Person> persons = Arrays.asList( new Person(1, "Mike", "Canada"), new Person(2, "Jill", "England"), new Person(3, "Will", "Whales"), new Person(4, "Mary", "Spain"));

假设我有这两个列表

List<Person> persons = Arrays.asList(
                new Person(1, "Mike", "Canada"),
                new Person(2, "Jill", "England"),
                new Person(3, "Will", "Whales"),
                new Person(4, "Mary", "Spain"));


List<Metadata> metadata= Arrays.asList(
                new metadata(1, "2000-01-01", "Naturalized", "Bachelor's of Arts"),
                new metadata(2, "2001-01-01", "ExPat", "Masters of Chemestry"),
                new metadata(3, "2017-05-01", "Citizen", "Buiness Management"),
                new metadata(4, "2018-04-16", "Work Visa", "Nursing"));
List persons=Arrays.asList(
新人(1,“迈克”,“加拿大”),
新人(2,“吉尔”,“英格兰”),
新人(3,“遗嘱”、“鲸鱼”),
新人(4名,“玛丽”、“西班牙”);
列表元数据=Arrays.asList(
新元数据(1,“2000-01-01”,“归化”,“文学学士”),
新元数据(2,“2001-01-01”、“外籍人士”、“化学硕士”),
新元数据(3,“2017-05-01”,“公民”,“企业管理”),
新元数据(4,“2018-04-16”,“工作签证”,“护理”);
如果最终结果是一个新列表:

List<PersonWithMetadata> personsAndMEtadata = Arrays.asList(
                new PersonWithMetadata(1, "Mike", "Canada", "2000-01-01", "Naturalized", "Bachelor's of Arts"),
                new PersonWithMetadata(2, "Jill", "England", "2001-01-01", "ExPat", "Masters of Chemestry"),
                new PersonWithMetadata(3, "Will", "Whales", "2017-05-01", "Citizen", "Buiness Management"),
                new PersonWithMetadata(4, "Mary", "Spain", "2018-04-16", "Work Visa", "Nursing"));
List personsAndMEtadata=Arrays.asList(
拥有元数据的新人(1,“迈克”,“加拿大”,“2000-01-01”,“归化”,“文学学士”),
拥有元数据的新人(2,“吉尔”、“英格兰”、“2001-01-01”、“外籍人士”、“化学硕士”),
拥有元数据的新人(3,“遗嘱”、“鲸鱼”、“2017-05-01”、“公民”、“企业管理”),
具有元数据的新人(4,“玛丽”、“西班牙”、“2018-04-16”、“工作签证”、“护理”);
我试图找到一种Java streams方法,将前两个列表合并到第三个列表中——就像第一个输入上的SQL连接是一个id号。似乎应该有办法做到这一点,但我不知所措。这是怎么做到的?另外,假设两个输入列表之间最多有一个匹配项

您可以这样尝试:

List<PersonWithMetadata> personsAndMEtadata = persons.stream()
        .map(p -> {
                    //search for the meta data based on the person id
                    Metadata meta = metadata.stream()
                            .filter(m -> m.getId() == p.getId())
                            .findFirst()
                            .get();
                    // then create a PersonWithMetadata object based on Person and metadata
                    return new PersonWithMetadata(
                            p.getId(), p.getFirstName(), p.getLastName(),
                            meta.getDate(), meta.getCity(), meta.getJob()
                    );

                }
        ).collect(Collectors.toList());

我假设您有一个id为person的元数据,否则您将得到
NullPointerException

我相信您要寻找的是API中不幸遗漏的
zip
函数

提供了它,这将允许您压缩元组,然后将元组映射到新结构

StreamUtils.zip(persons, metadata, (person, metadata) -> ... )

下面的示例使用ID作为键构建
元数据
对象的
映射。这将有助于提高性能,因为无需为
列表中的每个
人员
迭代
元数据
列表

代码

publicstaticvoidmain(字符串[]args){
List persons=Arrays.asList(
新人(1,“迈克”,“加拿大”),
新人(2,“吉尔”,“英格兰”),
新人(3,“遗嘱”、“鲸鱼”),
新人(4名,“玛丽”、“西班牙”);
List metadataList=Arrays.asList(
新元数据(1,“2000-01-01”,“归化”,“文学学士”),
新元数据(2,“2001-01-01”、“外籍人士”、“化学硕士”),
新元数据(3,“2017-05-01”,“公民”,“企业管理”),
新元数据(4,“2018-04-16”,“工作签证”,“护理”);
//在metadataList上迭代一次,并根据ID作为键创建映射
Map metadataMap=metadataList.stream()
.collect(Collectors.groupingBy(Metadata::getId));
//迭代personList并从映射中获取元数据,以构建PersonWithMetadata
List personWithMetadataList=persons.stream().map(person->{
列表元数据=metadataMap.get(person.id);
if(metadata.isEmpty()){
//TODO:处理没有个人元数据的场景
}
//TODO:使用元数据构建PersonWithMetadata
返回新PersonWithMetadata();
}).collect(Collectors.toList());
}

YCF\u L的溶液应该可以工作,但它是O(n2)溶液。O(n)解决方案可以通过将一个列表转换为从id到对象的映射,然后迭代另一个列表并从映射中获取匹配值来实现:

Map<Integer, Person> personMap = 
    persons.stream().collect(Collectors.toMap(Person::getId, Function.identity());

List<PersonWithMetadata> result = 
    metadata.stream()
            .map(m -> new PersonWithMetadata(personMap.get(m.getId()), m)
            .collect(Collectors.toList());
Map personMap=
persons.stream().collect(Collectors.toMap(Person::getId,Function.identity());
列表结果=
metadata.stream()
.map(m->newpersonwithmetadata(personMap.get(m.getId()),m)
.collect(Collectors.toList());
在样本数据中,列表具有按匹配顺序匹配的对象。如果此假设对实际问题也是正确的,则解决方案可能会更简单-您可以流式处理索引,并从列表中获取相应的值:

List<PersonWithMetadata> result = 
    IntStream.reange(0, persons.size())
             .map(i -> new PersonWithMetadata(persons.get(i), metadata.get(i))
             .collect(Collectors.toList());
列表结果=
IntStream.reange(0,persons.size())
.map(i->newpersonwithmetadata(persons.get(i),metadata.get(i))
.collect(Collectors.toList());

它们是否遵循相同的顺序?您是否专门尝试了某项功能,但失败了?您是否有使用streams的特殊原因?请尝试从IntSream.range开始,并以这种方式遍历索引。此代码具有“设计气味”考虑一个适当的数据结构。如果你使用一个地图,你可以通过ID来查找。最好是在一个适当的类中存储ID中的所有相关信息。而不是并行列表。我首先发布了一个用GAVA <代码>流的例子。zip 但后来找到了这个答案:另一个更一般的答案:我删除了我的答案B。但是我想提一下相关的答案。只是想指出一点,这个解决方案是O(n^2),而这个问题很容易在O(n)中解决,而不需要使用流…@Obicere检查Mureinik的答案,你可以在O(n)中解决它@Obicere旁边的不是完全的O(n^2)如果这两个列表都是按id排序的,那么它总是会选择第一个元素,而不是针对个人的,但这就是许多人不喜欢流的原因。许多开发人员喜欢编写快速代码,在这种情况下,您是第一个回答的人。快速编写代码是好的,但不会牺牲效率。总的来说,这是一个错误nswer用streams来推广这种“完成任务”的态度,我认为这是一个合理的否决票,因为这是一个糟糕的问题答案。而且,即使它在大约一半的时间内找到了它,它仍然是O(n^2)。
Map<Integer, Person> personMap = 
    persons.stream().collect(Collectors.toMap(Person::getId, Function.identity());

List<PersonWithMetadata> result = 
    metadata.stream()
            .map(m -> new PersonWithMetadata(personMap.get(m.getId()), m)
            .collect(Collectors.toList());
List<PersonWithMetadata> result = 
    IntStream.reange(0, persons.size())
             .map(i -> new PersonWithMetadata(persons.get(i), metadata.get(i))
             .collect(Collectors.toList());