【问题标题】:Summing values in a List. Could I be doing this more efficiently?对列表中的值求和。我可以更有效地做到这一点吗?
【发布时间】:2014-03-27 19:44:12
【问题描述】:

我有一个 Fact 对象列表。每个对象都有一个日期字段 (reportingDate) 和一个长字段 (numberSaved)。每个报告日期都有几个结果。我正在尝试获取每个报告日期的所有 numberSaved 值的总和。目前,我正在这样做:

private static List<Fact> sumFacts(List<Fact> facts) {
    List<Fact> summedFacts = new ArrayList<Fact>();

    for (Fact fact : facts) {
        boolean found = false;
        for (Fact sumFact : summedFacts) {
            if(sumFact.getReportingDate().equals(fact.getReportingDate())) {
                found = true;
                sumFact.setNumberSaved(sumFact.getNumberSaved() + fact.getNumberSaved());
            }
        }            
        if (!found) summedFacts.add(fact);
    }        
    return summedFacts;
}

public class Fact {
    String reportingDate;
    long numberSaved;

    public String getReportingDate() {
        return reportingDate;
    }
    public void setReportingDate(String reportingDate) {
        this.reportingDate = reportingDate;
    }
    public long getNumberSaved() {
        return numberSaved;
    }
    public void setNumberSaved(long numberSaved) {
        this.numberSaved = numberSaved;
    }
}

对于原始列表中的每个项目,它会遍历新列表以寻找匹配的日期。如果它找到一个具有匹配日期的对象,它会将其 numberSaved 值添加到它。如果它通过整个列表但没有找到匹配的日期,它会将自己添加到新列表中。

有没有更有效的方法可以将值汇总到具有唯一日期的 Fact 对象列表中?

编辑:

我忘了说我需要维护物品的顺序

【问题讨论】:

    标签: java performance list


    【解决方案1】:

    与其将您的事实保存在 List 中并对其进行迭代(产生 O(n^2) 复杂度),您可以将它们存储在地图中,形成事实对象的报告日期,从而为您提供 O( n) 复杂性:

    private static List<Fact> sumFacts(List<Fact> facts) {
        Map<String, Fact> summedFacts = new HashMap<Fact>();
    
        for (Fact fact : facts) {
            summedFact = summedFacts.get(fact.getReportingDate());
            if (summedFact == null) {
                summedFacts.put (fact.getReportingDate(), fact);
            } else {
                summedFact.setNumberSaved(summedFact.getNumberSaved() + fact.getNumberSaved());
            }
        }
        return new ArrayList<Fact>(summedFacts.values());
    }
    

    【讨论】:

    • 他的date实际上只是一个String,否则这正是我要提出的,而且绝对正确。
    • 谢谢!我没有想过使用日期作为键,然后将整个对象作为值。我忘了提到我必须维护秩序,所以我使用了 LinkedHashMap。这会影响使用地图的性能提升吗?
    • AFAIK,它会有一些轻微的性能损失(与简单的 HashMap 相比),但是检查键是否在地图中的顺序或数量级(O(1),O(n ) 对于方法) 应该是相同的。
    【解决方案2】:

    唯一可能更快的方法是如果两个列表都按某个键排序(很可能是您正在使用的日期)。检查未排序列表中是否存在对象是O(n),而您正在为另一个列表的每个元素执行此操作,从而导致问题O(m * n)

    这表明您的解决方案在没有预先排序列表的情况下尽可能高效。

    您最多可以改进的是使用 List.add(int, Object) 以便将项目插入到列表的前面,这样它就不会再次循环。

    【讨论】:

      【解决方案3】:

      HashTable 用于summedFacts 可以大大提高性能(在http://docs.oracle.com/javase/7/docs/api/java/util/Hashtable.html 上阅读更多信息)

      您可以将日期转换为字符串并将其用作HashTable 的键。 HashTable 的值将包含具有相同日期的 Fact 对象的总和。

      HashTable 访问是即时的 (O(1)),因此此解决方案将引导您使用 O(n) 实现而不是 O(n*m) 实现。

      例如:

      private static HashTable<string, Fact> sumFacts(List<Fact> facts) {
      HashTable<string, Fact> summedFacts = new Hashtable<string, Fact>();
      
      for (Fact fact : facts) {
          // Check if the item with this date is already added to the HashTable. If not, then add it
          if (summedFacts.get(sumFact.getReportingDate()) == null) 
                        summedFacts.put(fact.getReportingDate(), fact); // add the value to the HashTable.
            else {
                  // If the date is already there, than perform adition. 
                  currentFact = summedFacts.get(fact.getReportingDate()); 
                  currentFact.setNumberSaved(fact.getNumberSaved() + currentFact.getNumberSaved());
              }
          }            
      }        
      return summedFacts;
      

      }

      【讨论】:

        猜你喜欢
        • 2012-03-07
        • 1970-01-01
        • 2011-02-27
        • 1970-01-01
        • 2015-04-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-07
        相关资源
        最近更新 更多