繁体   English   中英

如何避免重复代码初始化 hashmap 的 hashmap?

[英]How can I avoid repeating code initializing a hashmap of hashmap?

每个客户都有一个 id 和许多带有日期的发票,这些发票按 id 存储为客户的 Hashmap,按日期存储为发票的哈希图:

HashMap<LocalDateTime, Invoice> allInvoices = allInvoicesAllClients.get(id);

if(allInvoices!=null){
    allInvoices.put(date, invoice);      //<---REPEATED CODE
}else{
    allInvoices = new HashMap<>();
    allInvoices.put(date, invoice);      //<---REPEATED CODE
    allInvoicesAllClients.put(id, allInvoices);
}

Java 解决方案似乎是使用getOrDefault

HashMap<LocalDateTime, Invoice> allInvoices = allInvoicesAllClients.getOrDefault(
    id,
    new HashMap<LocalDateTime, Invoice> (){{  put(date, invoice); }}
);

但是如果 get 不为空,我仍然希望 put (date, invoice) 执行,并且仍然需要向“allInvoicesAllClients”添加数据。 所以它似乎没有多大帮助。

这是Map#computeIfAbsent一个很好的用例。 您的代码段本质上等同于:

allInvoicesAllClients.computeIfAbsent(id, key -> new HashMap<>()).put(date, invoice);

如果id没有作为allInvoicesAllClients的键allInvoicesAllClients ,那么它将创建从id到新HashMap映射并返回新的HashMap 如果id作为键存在,那么它将返回现有的HashMap

对于这种特殊情况, computeIfAbsent是一个很好的解决方案。 总的来说,我想注意以下几点,因为还没有人提到它:

“外部”哈希映射仅存储对“内部”哈希映射的引用,因此您可以重新排序操作以避免代码重复:

HashMap<LocalDateTime, Invoice> allInvoices = allInvoicesAllClients.get(id);

if (allInvoices == null) {           
    allInvoices = new HashMap<>();
    allInvoicesAllClients.put(id, allInvoices);
}

allInvoices.put(date, invoice);      // <--- no longer repeated

您几乎不应该使用“双括号”映射初始化。

{{  put(date, invoice); }}

在这种情况下,您应该使用computeIfAbsent

allInvoicesAllClients.computeIfAbsent(id, (k) -> new HashMap<>())
                     .put(date, allInvoices);

如果此 ID 没有映射,您将插入一个。 结果将是现有的或计算出的地图。 然后,您可以put项目放入该地图中,并保证它不会为空。

这比其他答案更长,但恕我直言更具可读性:

if(!allInvoicesAllClients.containsKey(id))
    allInvoicesAllClients.put(id, new HashMap<LocalDateTime, Invoice>());

allInvoicesAllClients.get(id).put(date, invoice);

您在这里做两件单独的事情:确保HashMap存在,并向其中添加新条目。

现有代码确保在注册哈希映射之前先插入新元素,但这不是必需的,因为HashMap不关心这里的排序。 这两个变体都不是线程安全的,因此您不会丢失任何东西。

因此,就像@Heinzi 建议的那样,您可以将这两个步骤分开。

我还会做的是将HashMap的创建卸载到allInvoicesAllClients对象,因此get方法不能返回null

这也减少了单独线程之间竞争的可能性,这些线程既可以从get获取null指针,然后又决定put一个新的HashMap put单个条目——第二个put可能会丢弃第一个,从而丢失Invoice对象。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM