Introduction
After java8, some very practical functions have been added to the commonly used Map interface, which can greatly simplify the code writing in some specific scenarios and improve the readability of the code. Let’s take a look.
computeIfAbsent function
For example, many times we need to group data, which becomesMap<Integer, List<?>>
The form, before Java8, is generally implemented as follows:
List<Payment> payments = getPayments(); Map<Integer, List<Payment>> paymentByTypeMap = new HashMap<>(); for(Payment payment : payments){ if(!(())){ ((), new ArrayList<>()); } (()) .add(payment); }
It can be found that only one grouping operation needs to be considered carefully. When there is no corresponding value in the Map, you need to stuff an empty List first.
But if using java8 providedcomputeIfAbsent
Methods and code will be much simplified, as follows:
List<Payment> payments = getPayments(); Map<Integer, List<Payment>> paymentByTypeMap = new HashMap<>(); for(Payment payment : payments){ ((), k -> new ArrayList<>()) .add(payment); }
computeIfAbsent
The logic of the method is that if there is no corresponding key in the map, then the lambda expression is executed to generate a default value and put it in the map and return it, otherwise the value already in the map is returned.
Map with default valueSince this kind of map that requires default values is too common, I usually wrap a tool class and use it as follows:
public class DefaultHashMap<K, V> extends HashMap<K, V> { Function<K, V> function; public DefaultHashMap(Supplier<V> supplier) { = k -> (); } @Override @SuppressWarnings("unchecked") public V get(Object key) { return ((K) key, ); } }
Then use it like this, as follows:
List<Payment> payments = getPayments(); Map<Integer, List<Payment>> paymentByTypeMap = new DefaultHashMap<>(ArrayList::new); for(Payment payment : payments){ (()) .add(payment); }
Haha, this is a bit like pythondefaultdict(list)
It's
Temporary CacheSometimes, in a for loop, a temporary cache is needed to reuse the query results in the loop. You can also use computeIfAbcent, as follows:
List<Payment> payments = getPayments(); Map<Integer, PayType> payTypeCacheMap = new HashMap<>(); for(Payment payment : payments){ PayType payType = ((), k -> (k)); (()); }
Because the pay_type_id of different payments in payments is most likely the same, using this method can avoid a large number of repeated queries, but if you do not use the computeIfAbcent function, the code will be a bit cumbersome and obscure.
computeIfPresent function
The computeIfPresent function is the opposite of the logic of computeIfAbcent. If there is a corresponding key in the map, then a lambda expression is executed on its value to generate a new value and put it in the map and return it, otherwise null is returned.
This function is generally used when two sets are related to equivalent values, and the judgment logic can be written less once, as follows:
@Data public static class OrderPayment { private Order order; private List<Payment> payments; public OrderPayment(Order order) { = order; = new ArrayList<>(); } public OrderPayment addPayment(Payment payment){ (payment); return this; } }
public static void getOrderWithPayment(){ List<Order> orders = getOrders(); Map<Long, OrderPayment> orderPaymentMap = new HashMap<>(); for(Order order : orders){ ((), new OrderPayment(order)); } List<Payment> payments = getPayments(); //Associate payment to the relevant order for(Payment payment : payments){ ((), (k, orderPayment) -> (payment)); } }
Compute function
The compute function is actually similar to the computeIfPresent and computeIfAbcent functions, but it does not care whether there are values in the map. It executes a lambda expression to calculate the new value and put it into the map and returns.
This function is suitable for grouping iterative calculations. For example, when grouping sums up amounts, it is suitable for using the compute function, as follows:
List<Payment> payments = getPayments(); Map<Integer, BigDecimal> amountByTypeMap = new HashMap<>(); for(Payment payment : payments){ ((), (key, oldVal) -> oldVal == null ? () : (()) ); }
When oldValue is null, it means that the value of the corresponding key is calculated for the first time in the map, just give it to the amount directly. When the calculation is accumulated again, just summarize it directly through the add function.
merge function
It can be found that when using compute to summarize the amount, the lambda expression needs to determine whether it is the first time to calculate the key value, which is a little troublesome. If you use the merge function, you can further simplify the code, as follows:
List<Payment> payments = getPayments(); Map<Integer, BigDecimal> amountByTypeMap = new HashMap<>(); for(Payment payment : payments){ ((), (), BigDecimal::add); }
This function is too concise. The first parameter of merge is key, the second parameter is value, and the third parameter is value merge function.
When it is the first time that the value of the corresponding key is calculated, it is directly put into the value in the map. When calculating again later, use the value merging functionBigDecimal::add
Calculate the new summary value and put it in the map.
putIfAbsent function
putIfAbsent can also know its function from the naming. When there is no corresponding key in the map, the put value is only put into the map. It is mainly used in the following scenarios:
If you convert a list to a map, if there are duplicate values in the list, the difference between put and putIfAbsent is as follows:
- put retains the latest inserted data.
- putIfAbsent retains the earliest inserted data.
forEach function
To be honest, it is quite long-winded to traverse maps in Java, whether it is the entrySet method or the keySet method, as follows:
for(<String, BigDecimal> entry: ()){ Integer payTypeId = (); BigDecimal amount = (); ("payTypeId: %s, amount: %s \n", payTypeId, amount); }
Let’s take a look at the writing method in python or go, as follows:
for payTypeId, amount in (): print("payTypeId: %s, amount: %s \n" % (payTypeId, amount))
It can be found that the map traversal writing method in python requires fewer lines of code to be written. However, although Java does not support this writing method at the syntax level, using map's forEach function can also simplify similar effects, as follows:
((payTypeId, amount) -> { ("payTypeId: %s, amount: %s \n", payTypeId, amount); });
Summarize
For a long time, Java has been widely criticized by developers for its cumbersome code writing. However, starting from java8, from Map, Stream, var, multiline-string and record, Java has made a lot of simplifications in the code writing level, and Java seems to be enlightened.
This is the end of this article about the summary of convenient and practical Map functions in Java 8. For more related Java 8 Map functions, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!