时间计算问题在Java中

huangapple go评论67阅读模式
英文:

Issue with Time Calculation in Java

问题

以下是您要求的翻译内容:

在一个筛选测试中,我被要求编写一个程序,以获取一天中的最大空闲时间。

例如:

输入: "10:00AM-12:30PM","02:00PM-02:45PM","09:10AM-09:50AM"
期望的输出: 01:30

这些时间范围可能不是按递增顺序排列的。

针对这个问题,我尝试用Java写了一小段代码:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.time.Duration;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;

public class TimeDiff {

    public static void main(String[] args) throws NullPointerException {
        String accStr = null;

        System.out.println("输入白天的时间段:");
        try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
            accStr = br.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        String unQuoted = accStr.replace("\"", "");
        String[] inputSplit = unQuoted.split(",");
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("hh:mma");
        Map<LocalTime, LocalTime> timeMap = new TreeMap<>();
        for (String x : inputSplit) {
            String[] furtherSplit = x.split("-");
            timeMap.put(LocalTime.parse(furtherSplit[0], dateTimeFormatter), LocalTime.parse(furtherSplit[1], dateTimeFormatter));
        }
        Iterator iterator = timeMap.entrySet().iterator();

        List<Long> duration = new ArrayList<Long>();
        LocalTime[][] convertedMaptoArray = new LocalTime[timeMap.size()][2];
        int rowIndex = 0;

        while (iterator.hasNext()) {
            int colIndex = 0;
            Map.Entry pair = (Map.Entry) iterator.next();
            //System.out.println(pair.getKey() + " " + pair.getValue());
            convertedMaptoArray[rowIndex][colIndex] = (LocalTime) pair.getKey();
            convertedMaptoArray[rowIndex][colIndex + 1] = (LocalTime) pair.getValue();
            rowIndex++;
        }

        LocalTime previousRecordEndTime = null;
        LocalTime currentRecordStartTime = null;
        for (int i = 0; i <= convertedMaptoArray.length - 1; i++) {
            currentRecordStartTime = convertedMaptoArray[i][0];
            //System.out.println("current :" + currentRecordStartTime);
            if (i > 0) {
                previousRecordEndTime = convertedMaptoArray[i - 1][1];
                //System.out.println("Previous : " + previousRecordEndTime);
                duration.add(ChronoUnit.MINUTES.between(previousRecordEndTime, currentRecordStartTime));
            }
        }

        System.out.println("最长持续时间:" + LocalTime.MIN.plus(Duration.ofMinutes(Collections.max(duration))).toString());

    }
}

我被淘汰了,但我对上述代码有一些问题:

  1. 如何进一步优化这段代码?
  2. 以上代码是否违反了任何编码规则?
  3. 在Java中计算时间的更好方法是什么?

请帮忙解答。提前致谢。

英文:

In a screening test I've been asked to write a program to get the maximum free time available during a day.

For example :
>Input: "10:00AM-12:30PM","02:00PM-02:45PM","09:10AM-09:50AM"
<br>Expected Output: 01:30

These time ranges may not be in increasing order.

For this issue I've tried to write a small code in java as :

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.time.Duration;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
public class TimeDiff {
public static void main(String[] args) throws NullPointerException {
String accStr = null;
System.out.println(&quot;Enter day time entries : &quot;);
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
accStr = br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
String unQuoted = accStr.replace(&quot;\&quot;&quot;, &quot;&quot;);
String[] inputSplit = unQuoted.split(&quot;,&quot;);
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(&quot;hh:mma&quot;);
Map&lt;LocalTime, LocalTime&gt; timeMap = new TreeMap&lt;&gt;();
for (String x : inputSplit) {
String[] furtherSplit = x.split(&quot;-&quot;);
timeMap.put(LocalTime.parse(furtherSplit[0], dateTimeFormatter), LocalTime.parse(furtherSplit[1], dateTimeFormatter));
}
Iterator iterator = timeMap.entrySet().iterator();
List&lt;Long&gt; duration = new ArrayList&lt;Long&gt;();
LocalTime[][] convertedMaptoArray = new LocalTime[timeMap.size()][2];
int rowIndex = 0;
while (iterator.hasNext()) {
int colIndex = 0;
Map.Entry pair = (Map.Entry) iterator.next();
//System.out.println(pair.getKey() + &quot; &quot; + pair.getValue());
convertedMaptoArray[rowIndex][colIndex] = (LocalTime) pair.getKey();
convertedMaptoArray[rowIndex][colIndex+1]=(LocalTime)pair.getValue();
rowIndex++;
}
LocalTime previousRecordEndTime = null;
LocalTime currentRecordStartTime = null;
for(int i= 0; i&lt;=convertedMaptoArray.length-1;i++)
{
currentRecordStartTime = convertedMaptoArray[i][0];
//System.out.println(&quot;current :&quot; +currentRecordStartTime);
if(i&gt;0){
previousRecordEndTime = convertedMaptoArray[i-1][1];
//System.out.println(&quot;Previous : &quot; + previousRecordEndTime);
duration.add(ChronoUnit.MINUTES.between(previousRecordEndTime, currentRecordStartTime));
}
}
System.out.println(&quot;Longest duration : &quot; + LocalTime.MIN.plus(Duration.ofMinutes(Collections.max(duration))).toString());
}
}

I was screened out but I've few questions regarding above code :

  1. How this code can be optimized more?
  2. Is there any coding rule which above code is breaking?
  3. What should be better approach to calculate time in Java?

Please help. Thanks in advance.

答案1

得分: 3

我会争辩说你正在违反 KISS 原则(过度复杂化事物),同时混合了两个非常不同的职责:解析输入和计算时间间隔 - 你只关心最短的间隔,所以不需要存储所有内容。

输入是否假定为通过控制台输入的字符串?

我已将其拆分为以下方法:

// 01:00AM -> 60
private static int timeStringToMinutesFromMidnight(String time)

// 60 -> 01:00
private static String minutesToHoursAndMinutes(int minutes)

// 11:00AM-11:30AM -> 660 690 加入列表
private static void parseInterval(String interval, List<Integer> minutes)

(在之前的编辑中,我有timeStringIntervalToMinutes,但我误读了问题,并计算了错误的输出)。

在这里你不需要任何复杂的数据结构:一个简单的List就足以完成所有所需的操作。对它进行排序比使用TreeMapTreeSet更容易 - 是的,它们会自行排序,但在占用空间和访问速度方面也更重。

完整代码(导入java.util.*java.io.*):

// 01:00AM -> 60
private static int timeStringToMinutesFromMidnight(String time) {
    // ...
}

// 60 -> 01:00
private static String minutesToHoursAndMinutes(int minutes) {
    // ...
}

// 11:00AM-11:30AM -> 660 690 加入列表
private static void parseInterval(String interval, List<Integer> minutes) {
    // ...
}

// 输入: "10:00AM-12:30PM","02:00PM-02:45PM","09:10AM-09:50AM"
// 预期输出: 01:30
public static void main(String ... args) {
    // ...
}
英文:

I would argue that you are breaking KISS (overcomplicating things) and, at the same time, mixing 2 very different responsibilities: that of parsing the input, and that of calculating intervals - of which you are only interested in the shortest, so storing everything is not necessary.

Is the input assumed to be a string entered through the console?

I have broken it up into the following methods:

// 01:00AM -&gt; 60
private static int timeStringToMinutesFromMidnight(String time)
// 60 -&gt; 01:00
private static String minutesToHoursAndMinutes(int minutes)
// 11:00AM-11:30AM -&gt; 660 690 added to list
private static void parseInterval(String interval, List&lt;Integer&gt; minutes)

(in a previous edit, I had timeStringIntervalToMinutes, but I had misread the question and was calculating the wrong output).

You do not need any complicated data-structure here: a simple List allows you to do everything required. Sorting it is easier than working with a TreeMap or a TreeSet- yes, they do their own sorting, but are also much heavier in terms of footprint and access speed.

Complete code (imports java.util.* and java.io.*):

// 01:00AM -&gt; 60
private static int timeStringToMinutesFromMidnight(String time) {
int hours = Integer.parseInt(time.substring(0,&quot;hh&quot;.length()));
int minutes = Integer.parseInt(time.substring(&quot;hh:&quot;.length(), &quot;hh:mm&quot;.length()));
boolean afternoon = time.substring(&quot;hh:mm&quot;.length()).equalsIgnoreCase(&quot;PM&quot;);
if (afternoon &amp;&amp; hours != 12) {
hours += 12;
}
return hours * 60 + minutes;
}
// 60 -&gt; 01:00
private static String minutesToHoursAndMinutes(int minutes) {
int hours = minutes / 60;
minutes = minutes % 60;
return String.format(&quot;%02d:%02d&quot;, hours, minutes);
}
// 11:00AM-11:30AM -&gt; 660 690 added to list
private static void parseInterval(String interval, List&lt;Integer&gt; minutes) {
String[] parts = interval.split(&quot;-&quot;);
int start = timeStringToMinutesFromMidnight(parts[0]);
int end = timeStringToMinutesFromMidnight(parts[1]);
if (end &lt; start) {
throw new IllegalArgumentException(&quot;interval &quot; + interval + &quot; has end&lt;start&quot;);
}
minutes.add(start);
minutes.add(end);
} 
// Input: &quot;10:00AM-12:30PM&quot;,&quot;02:00PM-02:45PM&quot;,&quot;09:10AM-09:50AM&quot;
// Expected Output: 01:30
public static void main(String ... args) {
List&lt;Integer&gt; endpoints = new ArrayList&lt;&gt;();
try (Scanner sc = new Scanner(System.in).useDelimiter(&quot;[,\&quot;]+&quot;)) {
while (sc.hasNext()) {
parseInterval(sc.next(), endpoints);
}
}
if (endpoints.isEmpty()) {
System.out.println(&quot;No intervals found&quot;);
return;
}
// sort the endpoints (assuming that no intervals overlap, intervals will not be broken)
Collections.sort(endpoints);
int largest = -1;
for (int i=1; i&lt;endpoints.size()-1; i+=2) {
largest = Math.max(largest,  endpoints.get(i+1) - endpoints.get(i));
}        
System.out.println(minutesToHoursAndMinutes(largest));
}

答案2

得分: 2

我建议:

  • 不要使用 Map 存储条目。可以使用一个 pair 类,或者定义一个 TimeRange 类,可以保存两个 LocalTime 对象。然后创建一个包含这些对象的集合。
  • 即使你想要使用 Map,也不要使用原始的 Map.Entry。将其参数化为 Map.Entry&lt;LocalTime, LocalTime&gt;。类似地,将迭代器参数化。另一方面,new ArrayList&lt;Long&gt;() 中的类型参数是多余的;只需使用 new ArrayList&lt;&gt;() 即可。
  • 不要使用 Long 表示持续时间。直接在整个过程中使用 Duration 类。可以使用它的 compareTo 方法进行比较。
  • 我建议先将字符串拆分为时间范围,然后仅去掉开头和结尾的引号。这可以提供一定程度的输入验证。
  • 要找到最大的空闲持续时间,可以使用内置的最大值方法,例如双参数的 Collections.max()。可以传递从 Arrays.asList() 获取的列表,或者更好的是,避免使用数组,将持续时间仅保存在列表中。然后当然需要一个适当的 Comparator 实现。
英文:

I suggest:

  • Don’t use a Map for your entries. Use a pair class or define a TimeRange class that can hold two LocalTime objects. And then make a collection of those.
  • Even if you want to use a Map, don’t use a raw Map.Entry. Parameterize as Map.Entry&lt;LocalTIme, LocalTime&gt;. Similarly parameterize your iterator. (On the other hand the type parameter in new ArrayList&lt;Long&gt;() is redundant; just new ArrayList&lt;&gt;() would be conventional.)
  • Don’t use Long for durations. Just use the Duration class throughout. Use its compareTo method for comparison.
  • I’d split the string into time ranges first and then remove only leading and trailing quotes. This gives a degree of input validation.
  • For finding the max free duration I would use a built-in max method, for example the two-arg Collections.max(). I’d pass a list obtained from Arrays.asList(), or even better, avoid arrays and keep the durations in a list exclusively. And then of course an appropriate Comparator implementation.

huangapple
  • 本文由 发表于 2020年9月23日 03:19:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/64016399.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定