【问题标题】:Compare partial object in an ArrayList Java比较 ArrayList Java 中的部分对象
【发布时间】:2018-02-27 16:14:45
【问题描述】:

我有一个对象如下:

public class Record{
    Int ID;
    String title;
    Date date;
    Duration time;

    public Record createRecord(int ID, String title, Date date, Duration time){
        this.ID= ID;
        this.title = title;
        this.date = date;
        this.time = time;
        return this;
    }
}

我将多个对象存储在一个列表中。在插入新记录时,我需要检查列表是否已经有一个只有相同标题和日期的对象,并替换其中的时间。

我正在寻找任何可以达到 O(1) 时间的解决方案。

【问题讨论】:

  • 您使用哪种数据结构来存储您的对象?我会选择搜索时间复杂度为 O(1) 的 HashSet
  • 我正在使用 HashMap,其中日期为键,对象列表为值。从 HashMap 中检索到对象列表后,我需要执行上述操作。
  • 为什么不按您的 ID 字段进行比较呢?如果 ID 可以存在于其他对象中,那么拥有 ID 并没有多大意义……相反,拥有两个具有相同标题和日期的对象的概率不同。此外,您的构造函数中没有太多需要 return this;,在 Java 中,构造函数已经返回新的对象实例。
  • @AnthonyAudette 不幸的是,ID 不一定是唯一的,因此存在问题
  • 在一个不相关的,你可能想把 createRecord 变成一个静态工厂方法

标签: java list object arraylist object-oriented-database


【解决方案1】:

利用 Map 实现,该实现为您提供 O(1) 访问权限,例如 HashMapConcurrentHashMap

伪代码:

class Record {
    static class Key {
        Date date
        String title
        // proper hashCode and equals
    }
    Date date
    String title
    int id
    Time time
    Key getKey() {...}
}


Map<Record.Key, Record> recordMap = new HashMap<>();
for (record : records) {
    recordMap.merge(record.getKey(), record, 
                   (oldRecord, newRecord) -> oldRecord.setTime(newRecord.getTime()));
}

【讨论】:

    【解决方案2】:

    在 ArrayList 中搜索现有元素将花费您 O(n) 的时间,如果是已排序的 ArrayList(例如,您维护已排序的记录),则需要 O(logn) 时间。因此,为了实现所需的功能,我会使用 Map 结构,按标题索引,然后按日期索引。像这样的:

    // Create general records DB
    Map<String, Map<Date, Record>> records = new HashMap<>();
    
    // Create sub DB for records with same ID
    Map<Date, Record> subRecords = new HashMap<>();
    
    // Assuming you've got from somewhere id, title and rest of the parameters
    subRecords.put(recordDate, new Record(id, title, time, duration));
    records.put(recordId, subRecords)
    
    // Now checking and updating records as simple as
    sub = records.get(someTitle); // Assuming you've got someTitle
    if (sub != null) {
       record = sub.get(someDate); // Same for someDate
       if (record != null) {
           record.updateTime(newTime);
       }
    }
    

    使用 Map 的 Map 将避免您需要覆盖 equals 和 hashCode 方法,而我同意 Map&lt;String, Map&lt;Date, Record&gt;&gt; 可能看起来有点花哨或奇怪。 While 将为您提供在 O(1) 时间内更新记录或检查是否存在的能力。另外一点是您不需要创建记录来检查是否存在或更新,您可以直接使用标题和日期来检索您需要的内容。

    【讨论】:

      【解决方案3】:

      您可以通过 HashSet 做到这一点

      并实施

      @Override
          public boolean equals(Object obj) {
              if(this == obj) return true;
              if(!(obj instanceof Record)) return false;
              Record otherRecord = (Record)obj;
              return (this.time.equals(otherRecord.time) && this.title.equals(otherRecord.title));
          }
      
          @Override
          public int hashCode() {        
              int result = titile != null ? titile.hashCode() : 0;
              result = 31 * result + (time != null ? time.hashCode() : 0);
              return result;
      
          }
      

      并使用哈希集插入

         HashSet hset = new HashSet<Record>();
         if(!hset.add(record)){
              hset.remove(record);
              hset.add(record);
         }
      

      然后你可以将 HashSet 转换为你想要的 List。

      【讨论】:

      • 不要将Strings 与== 进行比较!
      • 我了解您需要维护此列表并能够随时更新记录。因此考虑到 O(1) 的要求,在 HashSet 和 List 之间切换不是一个真正的选择。 Is 应该始终是 HashSet 或某个 Map。
      • 时间也是如此。
      • 继续@Oleg 的同一行和之前关于字符串比较的评论 - hashCode 也远非最佳,我将首先解释为什么您需要实现这些功能。
      • @ArtemBarger 我正在修改它,我自己从来没有写过哈希码,只是从 IDE 生成的
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-12-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多