在Java

时间:2016-09-08 19:53:07

标签: java datetime hashmap

我在Java中有一个类型为String,List<Date>的HashMap。现在我想要做的是每个键我想要找到总时间(通过在每个日期中添加单独的时间),然后忽略具有最高时间的键。如果有两个具有相同最高时间的键,则忽略其键的整数形式较小的键(键100小于101)。对于其余的键,我希望通过将时间(以秒为单位)乘以50得出结果成本,如果时间小于3分钟(如果时间是00:02:59则乘以179 by 50)或者如果超过3分钟然后将时间(四舍五入到下一分钟)乘以80(如果时间为00:03:01,则乘以4 by 80)。

我拥有的HashMap数据样本是:

100 [Thu Jan 01 00:05:01 EST 1970]
101 [Thu Jan 01 00:01:07 EST 1970, Thu Jan 01 00:05:03 EST 1970]
102 [Thu Jan 01 00:07:11 EST 1970]
103 [Thu Jan 01 00:02:59 EST 1970, Thu Jan 01 00:04:38 EST 1970, Thu Jan 01 00:06:16 EST 1970]

在上面的示例中,您可以看到键103的时间最长(添加00:02:5900:04:3800:06:16),因此我将忽略此键中的所有值我的最终费用计算。对于其他密钥,我想按照上面提到的规则计算最终费用 - 例如,对于密钥101,总费用为67*50 (00:01:07 is less than 3 minutes) + 6 * 80 (00:05:03 is more than 3 minutes)

更新:我提出一些建议之后的代码是:

long totalCost = map.values().stream()
    .mapToLong(dates -> dates.stream().mapToLong(Date::getTime).sum())
    .sorted().limit(map.size() - 1)
    .map(Myclass::getCost).sum();

    System.out.println(totalCost);

这段代码的一个问题是假设有两个键具有相同的总时间,在这种情况下我想忽略其键值在数值上较小的键(如在键100中小于101)。到目前为止,代码忽略了排序后的第一个可用密钥(在行sorted().limit(map.size() - 1)中)。如何修改它以检查是否有多个键具有相同的最大时间而忽略了具有最低数值的键。

注意:每个值的日期时间值都有一个日期组件,与1970年1月1日星期四18月1日相同,与计算无关。

1 个答案:

答案 0 :(得分:1)

正确的类型

您正在使用所有错误的数据类型。

Integer

你的String类型的键实际上是一个整数,你说我们应该用数字比较它的值。因此,首先使用Integer类。

Duration

您滥用java.util.Date类(日期时间值)作为时间跨度。首先,完全避免麻烦的旧遗留日期时间类并使用它们的替换java.time类。其次,使用一个时间跨度来表示一段时间。在您的特定情况下,这将意味着java.time.Duration类。

Duration类可以在standard ISO 8601 format中解析并生成其值的String表示。例如,PT1M30S表示“一分半钟”。

避免使用时间格式来表示时间跨度,因为它会产生歧义; ISO 8601格式易于解析,易于阅读,避免歧义,并且是专为此目的而设计的。

不是Map

你使用Map变得很尴尬,因为你真的使用三个值而不是一对:

  • 整数(密钥,标识符)
  • 列表(时间跨度的集合)
  • 整数(每个持续时间生成的“成本”值的总和)

自定义类

所以用这三个值创建一个类。最后一个是只读值,可以在运行中计算,也可以缓存以获得更好的性能。

这是一个示例类。我将其命名为Event,因为问题中没有给出任何名称。警告:完全使用此源代码需要您自担风险。不是线程安全的。没有测试过。不保证任何方式。

此类动态地重新计算其Duration对象列表的成本。缓存会提高性能,使用次数非常多或数据量非常大。

此类使用自定义方法实现Comparable,该方法考虑了比较成本的主要规则,然后比较成本相等的ID数。

成为Comparable意味着您可以轻松地对这些Event个对象的集合进行排序。然后忽略第一个/最后一个项目来处理省略最高得分事件的规则。

package example;

import java.time.Duration;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

/**
 * Example class posted on Stack Overflow:
 * http://stackoverflow.com/q/39399156/642706
 *
 * Caution: NOT thread-safe. NOT tested. NOT guaranteed.
 *
 * © 2016 Basil Bourque. This source code may be used in accordance with the ISC
 * license: https://opensource.org/licenses/ISC
 *
 * @author Basil Bourque.
 */
public class Event implements Comparable<Event> {

    private Integer id;
    private List<Duration> durations;

    // Constants
    static final public long COST_PER_SECOND = 50L;
    static final public long COST_PER_MINUTE = 80L;

    // Constructor.
    public Event ( Integer id , List<Duration> durations ) {
        this.id = id;
        this.durations = durations;
    }

    public Integer getId () {
        return this.id;
    }

    public List<Duration> getDurations () {
        List<Duration> defensiveCopyOfList = new ArrayList<> ( this.durations );
        return defensiveCopyOfList;
    }

    public Long getCost () {
        // Calculate the cost of each Duration in our list.
        // Perhaps cache this calculated value for better performance.
        long sum = 0;
        for ( Duration duration : this.durations ) {
            long c = this.calculateCostOfDuration ( duration );
            sum = ( sum + c );
        }
        return sum;
    }

    private long calculateCostOfDuration ( Duration duration ) {
        long result = 0;
        long countMinutes = duration.toMinutes ();
        long limit = 3L; // We care if the duration is under 3 minutes (or else is 3 or more minutes).
        if ( countMinutes < limit ) {
            result = ( duration.getSeconds () * Event.COST_PER_SECOND );
        } else if ( countMinutes >= limit ) {
            // Round up to next minute. If ane even multiple of 60 seconds, then use count of minutes. Otherwise add one.
            long m = ( duration.getSeconds () % 60 == 0 ) ? countMinutes : ( countMinutes + 1 );
            result = ( m * Event.COST_PER_MINUTE );
        } else {
            System.out.println ( "ERROR - Reached impossible ELSE condition." );
        }
        return result;
    }

    @Override
    public int compareTo ( Event o ) {
        // Compare cost of each Event.
        // If cost is equal, then secondarily compare the id members so a '101' sorts before a '102.
        int c = this.getCost ().compareTo ( o.getCost () );
        if ( c == 0 ) {
            c = this.getId ().compareTo ( o.getId () );
        }
        return c;
    }

    @Override
    public String toString () {
        return "Event{ " + "id=" + id + " | durations=" + durations + " | cost=" + this.getCost () + " }";  // For debugging purposes.
    }

}

使用问题中给出的示例数据来使用此类的示例。

List<Event> events = new ArrayList<> ( 4 );

List<Duration> d = new ArrayList<> ();
d.add ( Duration.parse ( "PT5M1S" ) );
events.add ( new Event ( 100 , d ) );

d = new ArrayList<> ();
d.add ( Duration.parse ( "PT1M7S" ) );
d.add ( Duration.parse ( "PT5M3S" ) );
events.add ( new Event ( 101 , d ) );

d = new ArrayList<> ();
d.add ( Duration.parse ( "PT7M11S" ) );
events.add ( new Event ( 102 , d ) );

d = new ArrayList<> ();
d.add ( Duration.parse ( "PT2M59S" ) );
d.add ( Duration.parse ( "PT4M38S" ) );
d.add ( Duration.parse ( "PT6M16S" ) );
events.add ( new Event ( 103 , d ) );

Collections.sort ( events );

System.out.println ( "events: \n" + events );

运行时。请注意,排序顺序现在为100 | 102 | 101 | 103。

  

[事件{id = 100 |持续时间= [PT5M1S] | cost = 480},Event {id = 102 |持续时间= [PT7M11S] | cost = 640},Event {id = 101 |持续时间= [PT1M7S,PT5M3S] | cost = 3830},Event {id = 103 |持续时间= [PT2M59S,PT4M38S,PT6M16S] |成本= 9910}]