【问题标题】:new Java List of objects by matching entries in two other lists通过匹配另外两个列表中的条目来创建新的 Java 对象列表
【发布时间】:2018-07-13 18:29:30
【问题描述】:

假设我有这两个列表

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 号。似乎应该有办法做到这一点,但我不知所措。这是怎么做的?此外,假设两个输入列表之间最多有一个匹配项。

【问题讨论】:

  • 他们遵循相同的顺序吗?
  • 你有没有特别尝试过,但失败了?您必须使用流有什么特别的原因吗?
  • 尝试从 IntSream.range 开始并以这种方式遍历索引。这段代码有“设计气味”,考虑适当的数据结构。如果您使用地图会更好,这样您就可以通过 id 进行查找。将该ID的所有相关信息存储在适当的类中。而不是平行列表。
  • 我首先发布了一个答案,显示了 Guava Streams.zip 的示例,但后来在此答案中找到:Google Guava “zip” two lists。另一个更一般的答案:How to zip two Java Lists我删除了我的答案,但想提及链接的答案。

标签: java java-8 java-stream


【解决方案1】:

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

在示例数据中,列表具有匹配顺序的匹配对象。如果这个假设也适用于实际问题,那么解决方案可能会更容易 - 您可以流式传输 indexes 并从列表中获取相应的值:

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

【讨论】:

    【解决方案2】:

    你可以试试这个方法:

    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

    【讨论】:

    • 只是指出一点,这个解决方案是 O(n^2),而这个问题很容易在 O(n) 中解决,而无需使用流......
    • @Obicere 检查 Mureinik 的答案,你可以在 O(n) 中解决它
    • @Obicere 在它旁边不完全是 O(n^2) 如果两个列表是按 id 排序的,所以它总是会选择第一个元素并中断
    • 这与个人无关,但这就是许多人不喜欢流媒体的原因。许多开发人员喜欢编写快速代码,在这种情况下,您是第一个回答的人。快速编写代码是好的,但当你牺牲效率时就不行了。总的来说,这个答案促进了这种“完成它”的态度,我认为这证明了反对票的合理性,因为它是对这个问题的一个糟糕的答案。而且,即使它在大约一半的时间内找到它,它仍然是 O(n^2)。
    【解决方案3】:

    我相信您正在寻找的是 API 中遗憾地省略的 zip 函数。

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

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

    【讨论】:

      【解决方案4】:

      以下示例使用 ID 作为键构建 Metadata 对象中的 Map。这将有助于提高性能,因为无需为 List 中的每个 Person 迭代 Metadata 列表

      代码

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-12-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多