【问题标题】:max or min value on the basis of group by in stream java 8基于流java 8中的group by的最大值或最小值
【发布时间】:2016-12-22 02:13:26
【问题描述】:

似乎类似于之前回答的问题:Java 8 stream group by min and max

其实不然!

我有一个三列的表: LogId, StartTime, EndTime

现在我们有多个具有不同 StartTime 和 EndTime 的 LogId 条目

问题是:

  1. 我所有的列都是字符串,所以如何根据它们的值计算任何列的最小值或最大值。

  2. 我需要通过 LogId 找出 min(StartTime)、max(EndTime) 组到单个 Stream 中。

如何在 java 8 中使用流以最少的代码和最大的效率来实现这一点。

附上示例类:

public class Log {  

    private static final String inputFileName = "D:\\path\\to\\Log.csv";

    private static final String outputFileName = "D:\\path\\to\\Output\\Log.csv";

    private static List<Log> logList = null;

    private static Map<String, List<Log>> groupByLogId = new HashMap<String, List<Log>>();

    private String log_Id;
    private String startTime;
    private String endTime;

    public static Map<String, List<Log>> createLogMap() throws IOException {
        Function<String, Log> mapToLog = (line) -> {
            String[] p = line.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)", -1);
            Log log = new Log(p[0],p[1],
                    p[2]);

            return log;
        };


        InputStream is = null;
        BufferedReader br = null;

            is = new FileInputStream(new File(inputFileName));

            br = new BufferedReader(new InputStreamReader(is));

            logList = br.lines()
                            .skip(1)
                            .map(mapToLog)
                            .collect(Collectors.toList());




            logList.stream().forEach(System.out::println);

            groupByLogId = logList.stream()
                            .collect(Collectors.groupingBy(Log::getLog_Id));


            for (Entry<String, List<Log>> entryForLog : groupByLogId.entrySet()) {
                System.out.println(" Entity Id " + entryForLog.getKey()
                        + "        |        Value : " + entryForLog.getValue());
            }






            br.close();
            return groupByLogId;



    }



    public String getLog_Id() {
        return log_Id;
    }



    public void setLog_Id(String log_Id) {
        this.log_Id = log_Id;
    }



    public String getStartTime() {
        return startTime;
    }



    public void setStartTime(String startTime) {
        this.startTime = startTime;
    }



    public String getEndTime() {
        return endTime;
    }



    public void setEndTime(String endTime) {
        this.endTime = endTime;
    }



    public static List<Log> getLoglist() {
        return logList;
    }



    public Log(String log_Id, String startTime, String endTime) {
        super();
        this.log_Id = log_Id;
        this.startTime = startTime;
        this.endTime = endTime;
    }



    @Override
    public String toString() {

        return (new StringBuffer()
                    .append(log_Id).append(",")
                    .append(startTime).append(",")
                    .append(endTime)
                    ).toString();

    }


}

非常感谢任何帮助,

预期输出:

LogId: logid,min(StartTime),max(EndTime)

【问题讨论】:

  • "如何在 java 8 中使用流以最少的代码和最大的效率来实现这一点。" - 我觉得它太宽泛了,请尝试更具体地回答您的问题。
  • 尝试在group之前使用mapint/long
  • 那个代码看起来古老。您应该使用StringBuilder 而不是StringBuffer,但实际上,简单地使用log_Id+","+startTime+","+endTime 会自动为您带来好处。您更复杂的 toString() 实现与简单表达式相比没有任何好处。此外,您应该使用try( …) { … } 语句来管理资源。无需手动调用close(),异常情况下关闭不正确的问题就消失了。此外,您不再需要重复类型参数:groupByLogId=new HashMap&lt;&gt;() 将根据变量推断类型。

标签: java java-8 java-stream


【解决方案1】:

当然,将时间存储为字符串并不是一个好主意。最好改用LocalDateTime 之类的东西。在这个答案中,我假设您的字符串时间戳表示具有可比性,因此我可以使用 date1.compareTo(date2)

此外,我强烈建议您删除使 Log 对象不可变的设置器。它们不会增加任何价值,只会在您偶尔更改现有对象时使您的程序更难调试。

回到你的问题,添加这样的合并方法:

class Log {
    ...
    Log merge(Log other) {
        if(!other.getLog_Id().equals(this.getLog_Id())) {
            throw new IllegalStateException();
        }
        String start = this.getStartTime().compareTo(other.getStartTime()) < 0 ?
                       this.getStartTime() : other.getStartTime();
        String end = this.getEndTime().compareTo(other.getEndTime()) > 0 ?
                     this.getEndTime() : other.getEndTime();
        return new Log(this.getLog_Id, start, end);
    }
}

现在您可以简单地使用 toMap() 收集器来提供合并功能:

streamOfLogs.collect(
    Collectors.toMap(Log::getLog_Id, Function.identity(), Log::merge));

这样当出现两个具有相同Log_Id的日志条目时,将调用merge方法为它们创建合并的日志条目。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-24
    • 1970-01-01
    • 1970-01-01
    • 2023-03-30
    • 2016-11-28
    • 2018-10-22
    相关资源
    最近更新 更多