代码之家  ›  专栏  ›  技术社区  ›  StillLearningToCode

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

  •  3
  • StillLearningToCode  · 技术社区  · 7 年前

    假设我有这两张单子

    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 回复  |  直到 7 年前
        1
  •  5
  •   Mureinik    7 年前

    YCF的解决方案应该有效,但它是一个O(N )解决方案。通过将一个列表转换为从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());
    
        2
  •  3
  •   Youcef LAIDANI    7 年前

    您可以这样尝试:

    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为Person的元数据,否则您将得到 NullPointerException .

        3
  •  1
  •   Kraylog    7 年前

    我相信你要找的是 zip 很遗憾,API中省略了函数。

    这个 protonpack library 提供它,这将允许您压缩tuple,然后将其映射到新结构。

    StreamUtils.zip(persons, metadata, (person, metadata) -> ... )
    
        4
  •  1
  •   Garreth Golding    7 年前

    下面的示例构建 Map 属于 Metadata 使用ID作为键的对象。这将有助于提高性能,因为不需要迭代 元数据 每个的列表 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());
    
    }