简体   繁体   English

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

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

Every client has an id, and many invoices, with dates, stored as Hashmap of clients by id, of a hashmap of invoices by date:每个客户都有一个 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 solution seems to be to use getOrDefault : Java 解决方案似乎是使用getOrDefault

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

But if get is not null, I still want put (date, invoice) to execute, and also adding data to "allInvoicesAllClients" is still needed.但是如果 get 不为空,我仍然希望 put (date, invoice) 执行,并且仍然需要向“allInvoicesAllClients”添加数据。 So it doesn't seem to help much.所以它似乎没有多大帮助。

This is an excellent use-case for Map#computeIfAbsent .这是Map#computeIfAbsent一个很好的用例。 Your snippet is essentially equivalent to:您的代码段本质上等同于:

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

If id isn't present as a key in allInvoicesAllClients , then it'll create mapping from id to a new HashMap and return the new HashMap .如果id没有作为allInvoicesAllClients的键allInvoicesAllClients ,那么它将创建从id到新HashMap映射并返回新的HashMap If id is present as a key, then it'll return the existing HashMap .如果id作为键存在,那么它将返回现有的HashMap

computeIfAbsent is a great solution for this particular case.对于这种特殊情况, computeIfAbsent是一个很好的解决方案。 In general, I'd like to note the following, since nobody mentioned it yet:总的来说,我想注意以下几点,因为还没有人提到它:

The "outer" hashmap just stores a reference to the "inner" hashmap, so you can just reorder the operations to avoid the code duplication: “外部”哈希映射仅存储对“内部”哈希映射的引用,因此您可以重新排序操作以避免代码重复:

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

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

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

You should pretty much never use "double brace" map initialization.您几乎不应该使用“双括号”映射初始化。

{{  put(date, invoice); }}

In this case, you should use computeIfAbsent在这种情况下,您应该使用computeIfAbsent

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

If there is no map for this ID, you will insert one.如果此 ID 没有映射,您将插入一个。 The result will be the existing or computed map.结果将是现有的或计算出的地图。 You can then put items in that map with guarantee that it won't be null.然后,您可以put项目放入该地图中,并保证它不会为空。

This is longer than the other answers, but imho far more readable:这比其他答案更长,但恕我直言更具可读性:

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

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

You are doing two separate things here: ensuring that the HashMap exists, and adding the new entry to it.您在这里做两件单独的事情:确保HashMap存在,并向其中添加新条目。

The existing code makes sure to insert the new element first before registering the hash map, but that isn't necessary, because the HashMap doesn't care about the ordering here.现有代码确保在注册哈希映射之前先插入新元素,但这不是必需的,因为HashMap不关心这里的排序。 Neither variant is threadsafe, so you're not losing anything.这两个变体都不是线程安全的,因此您不会丢失任何东西。

So, like @Heinzi suggested, you can just split these two steps.因此,就像@Heinzi 建议的那样,您可以将这两个步骤分开。

What I would also do is offload the creation of the HashMap to the allInvoicesAllClients object, so the get method cannot return null .我还会做的是将HashMap的创建卸载到allInvoicesAllClients对象,因此get方法不能返回null

This also reduces the possibility for races between separate threads that could both get null pointers from get and then decide to put a new HashMap with a single entry -- the second put would probably discard the first, losing the Invoice object.这也减少了单独线程之间竞争的可能性,这些线程既可以从get获取null指针,然后又决定put一个新的HashMap put单个条目——第二个put可能会丢弃第一个,从而丢失Invoice对象。

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

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