【问题标题】:Using java 8 steam to find matching rows between two list and map to another object使用 java 8 流查找两个列表之间的匹配行并映射到另一个对象
【发布时间】:2019-03-18 19:08:30
【问题描述】:

我试图在两个不同的列表之间找到匹配的元素,然后我试图映射到不同对象的列表,其中包含两个列表中的元素以及一些选定的元素。

这是我的两颗豆子 -

public class LogsData {
    LocalDate eventDate ;
    String correlationId;
    String authId;
    int numberofSQL;
    // getter and setter
}

public class DistributionData {
    LocalDate eventDate;
    String correlationId;
    String callingProId;
    long transactionCount ;
    // getter and setter 
}

public class ResultBean {
    LocalDate eventDate;
    String correlationId;
    String callingProId;
    long transactionCount ;
    String authId ;
    int numberOfSQL;
}

如何获得包含与 correlationIdeventDate 匹配的最终列表,结果,我想要 ResultBean 的列表。

有人可以帮忙吗?

提前致谢。

【问题讨论】:

  • 如果您不费心使用流,这几乎肯定会容易得多
  • 日志数据列表中是否可以有多个具有相同(eventDate、correlationId)的元素?分发数据列表也一样。如果是,如果日志列表中有 2 个重复项,分发列表中有 3 个重复项,并且在两个列表中都具有相同的(eventDate,correlationId),您是否希望结果包含所有可能的组合,即 6 个元素?

标签: java dictionary java-stream


【解决方案1】:

一种方法是将logsData 列表的元素累积到一个映射中,其中键为logsData#getEventDatelogsData#getCorrelationId 连接。

Map<String, LogsData> accumulator = 
        logsData.stream()
                .collect(toMap(l -> l.getEventDate() + l.getCorrelationId(), Function.identity()));

然后流过distributionData列表并获取地图中的相应元素,然后将它们转换为ResultBean

List<ResultBean> resultSet = distributionData.stream()
       .map(d -> {
              LogsData logs = accumulator.get(d.getEventDate() + d.getCorrelationId());
              if (logs != null)
                   return new ResultBean(d.getEventDate(), d.getCorrelationId(),
                     d.getCallingProId(), d.getTransactionCount(), logs.getAuthId(), logs.getNumberofSQL());
                 return null;
       })
       .filter(Objects::nonNull)
       .collect(Collectors.toList());

这假设 ResultBean 有一个构造函数,如果不是这种情况,那么只需调用 setter 方法来设置必要的数据。

进口:

import static java.util.stream.Collectors.*;
import java.util.stream.*;
import java.util.function.*;

另一种方法,虽然效率较低:

List<ResultBean> resultSet = logsData.stream()
                .map(l -> distributionData.stream()
                        .filter(d -> l.getEventDate().equals(d.getEventDate()) &&
                                l.getCorrelationId().equals(d.getCorrelationId()))
                        .findFirst()
                        .map(d -> new ResultBean(d.getEventDate(), d.getCorrelationId(),
                                d.getCallingProId(), d.getTransactionCount(), l.getAuthId(), l.getNumberofSQL()))
                        .orElse(null))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

略有不同,在JDK9中避免.orElse(null)).filter(Objects::nonNull)模式:

List<ResultBean> resultSet = logsData.stream()
                .flatMap(l -> distributionData.stream()
                        .filter(d -> l.getEventDate().equals(d.getEventDate()) &&
                                l.getCorrelationId().equals(d.getCorrelationId()))
                        .findFirst()
                        .map(d -> new ResultBean(d.getEventDate(), d.getCorrelationId(),
                                d.getCallingProId(), d.getTransactionCount(), l.getAuthId(), l.getNumberofSQL()))
                        .stream()
                )
                .collect(Collectors.toList());

【讨论】:

    【解决方案2】:

    Louis Wasserman's comment 之后,可能可以在具有优势的地方使用 Streams。 考虑让LogsDataDistributionData(以及可选的ResultBean)扩展一个基本类型:

    class Data {
    
        LocalDate eventDate ;
        String correlationId;
    
        Data(LocalDate eventDate, String correlationId) {
            this.eventDate = eventDate;
            this.correlationId = correlationId;
        }
    
        LocalDate getEventDate() { return eventDate; }
        String getCorrelationId(){ return correlationId; }
    
        @Override
        public boolean equals(Object o) {
            if(!(o instanceof Data)) { return false; }
            Data d = (Data) o;
            return eventDate.equals(d.getEventDate())
                    && correlationId.equals(d.getCorrelationId() );
        }
    }
    

    给列表:

    List<LogsData> logsData = new ArrayList<>();
    List<DistributionData> dData = new ArrayList<>();
    

    你可以简单地将两个列表相交

    logsData.retainAll(dData);
    dData.retainAll(logsData);
    

    让它们按相同的顺序排序:

    //sort so two lists have the same order. If correlationId is not unique you may need 
    //to enhance the comperator 
    Collections.sort(dData, (a, b) -> a.getCorrelationId().compareToIgnoreCase(b.getCorrelationId()));
    Collections.sort(logsData, (a, b) -> a.getCorrelationId().compareToIgnoreCase(b.getCorrelationId()));
    

    并使用Stream 构造ResultBean 对象的列表:

    List<ResultBean> resultList = IntStream.range(0, logsData.size())
        .mapToObj( i ->
                new ResultBean(dData.get(i).getEventDate(), dData.get(i).getCorrelationId(),
                dData.get(i).getCallingProId(), dData.get(i).getTransactionCount(),
                logsData.get(i).getAuthId(), logsData.get(i).getNumberofSQL())
                )
        .collect(Collectors.toList());
    

    【讨论】:

      【解决方案3】:

      我改变了一点设计以使其工作,我将分发列表添加到日志 Beans 中,因为它具有一对多的关系,我能够合并两个列表中的数据,但它运行速度非常慢,因为集合大小相当尤其是分发列表很大,有人可以建议一些高性能或更好的设计。

       List<LogsData> logsData = logs.stream()
                  .filter(e -> (distributionData.stream()
                          .filter(d -> 
                 d.getCorrelationId.equals(e.getCorrelationId))
             .filter(d-> d.getEventDate().equals(e.getEventDate()))
             .map(mapper-> e.getDistribution().add(mapper); return e; })
                          .count())<1)
                          .collect(Collectors.toList()); 
      

      【讨论】:

        猜你喜欢
        • 2011-06-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-05-23
        • 1970-01-01
        • 1970-01-01
        • 2017-06-01
        • 2023-01-13
        相关资源
        最近更新 更多