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

时间:2018-07-13 18:29:31

标签: 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<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<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"));

我正在尝试找到一种将前两个列表组合成第三个列表的Java流方法-就像第一个输入上的SQL连接是一个ID号。似乎应该有办法做到这一点,但我很茫然。这是怎么做的?另外,假设两个输入列表之间最多有一个匹配项。

4 个答案:

答案 0 :(得分:5)

YCF_L的解决方案应该可以使用,但是它是O(n 2 )解决方案。通过将一个列表转换为从id到对象的映射,然后在另一个列表上进行迭代并从映射中获取匹配值,可以实现O(n)解决方案:

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());

答案 1 :(得分:3)

您可以尝试这种方式:

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());

关于此行:

Metadata meta = metadata.stream().filter(m -> m.getId() == p.getId()).findFirst().get();

我假设您有一个人名为id的元数据,否则您将获得NullPointerException

答案 2 :(得分:1)

我相信您正在寻找的是zip函数,可惜该API省略了该函数。

protonpack library提供了它,它使您可以压缩然后将元组映射到新结构。

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

答案 3 :(得分:1)

下面的示例使用ID作为键,构建了Map个对象中的Metadata个。这将有助于提高性能,因为无需为Metadata

中的每个Person遍历List列表

代码

public static void main(String[] args) {
    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> metadataList = 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"));

    //Iterate over metadataList once and create map based on ID as key
    Map<Integer, List<Metadata>> metadataMap = metadataList.stream()
            .collect(Collectors.groupingBy(Metadata::getId));

    //Iterate over personList and fetch metadata from Map to build PersonWithMetadata
    List<PersonWithMetadata> personWithMetadataList = persons.stream().map(person -> {
        List<Metadata> metadata = metadataMap.get(person.id);
        if (metadata.isEmpty()) {
            //TODO: Handle scenario for no metadata for person
        }

        //TODO: Build PersonWithMetadata

        return new PersonWithMetadata();

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

}