Every client has an id, and many invoices, with dates, stored as Hashmap of clients by id, of a hashmap of invoices by date:
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
:
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. So it doesn't seem to help much.
This is an excellent use-case for 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
. If id
is present as a key, then it'll return the existing HashMap
.
computeIfAbsent
is a great solution for this particular case. 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
allInvoicesAllClients.computeIfAbsent(id, (k) -> new HashMap<>())
.put(date, allInvoices);
If there is no map for this ID, you will insert one. 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.
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.
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. Neither variant is threadsafe, so you're not losing anything.
So, like @Heinzi suggested, you can just split these two steps.
What I would also do is offload the creation of the HashMap
to the allInvoicesAllClients
object, so the get
method cannot return 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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.