Skillfully use one line of HashMap code to count the number of words
brief introduction
JDK has been updated iteratively, and many familiar classes have quietly added some new method features. For example, our most commonly used HashMap.
Today, let's talk about two new methods compute and merge added by HashMap in jdk8, so as to realize the function of word statistics in one line of code. Let's have a look.
Love before jdk8
Jdk8 introduces many very useful new features for us, such as stream and lambda expressions, which can make our program more concise.
What if we need to count the number of words in an array?
This is not about algorithms, so you can directly use HashMap:
public void countBefore8(){
Map<String,Integer> wordCount= new HashMap<>();
String[] wordArray= new String[]{"we","are","the","world","we"};
for(String word: wordArray){
//如果存在则加1,否则将值设置为1
if(wordCount.containsKey(word)) {
wordCount.put(word,wordCount.get(word) + 1);
}else{
wordCount.put(word,1);
}
}
}
Basically, the process is like the above. We traverse the array, and then judge whether the word exists in the HashMap. If it exists, + 1.
The logic is simple, but it looks bloated.
Don't be afraid, we have jdk8.
Use compute in jdk8
Let's first look at the definition of compute in jdk8:
default V compute(K key,BiFunction<? super K,? super V,? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
V oldValue = get(key);
V newValue = remappingFunction.apply(key,oldValue);
if (newValue == null) {
// delete mapping
if (oldValue != null || containsKey(key)) {
// something to remove
remove(key);
return null;
} else {
// nothing to do. Leave things as they were.
return null;
}
} else {
// add or replace old mapping
put(key,newValue);
return newValue;
}
}
You can see that compute has the second parameter bifunction. Bifunction is a function. Enter two parameters and return one parameter.
The two parameters of bifunction are key and oldvalue corresponding to key.
Considering our word statistics, we can directly add oldvalue + 1. Therefore, using compute, you can rewrite the method to:
public void countAfter8WithCompute(){
Map<String,"we"};
Arrays.asList(wordArray).forEach(word ->{
wordCount.putIfAbsent(word,0);
wordCount.compute(word,(w,count)->count+1);
});
}
Of course, we can put putifabsent into compute:
public void countAfter8WithCompute2(){
Map<String,"we"};
Arrays.asList(wordArray).forEach(word -> wordCount.compute(word,count)->count == null ? 1 : count + 1));
}
One line of code is complete.
Merge used in jdk8
Take another look at the merge method:
default V merge(K key,V value,BiFunction<? super V,? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
Objects.requireNonNull(value);
V oldValue = get(key);
V newValue = (oldValue == null) ? value :
remappingFunction.apply(oldValue,value);
if (newValue == null) {
remove(key);
} else {
put(key,newValue);
}
return newValue;
}
The merge method requires three parameters. The first parameter is a key, the second parameter is a value with an empty oldvalue corresponding to the key, that is, a null default value, and the third parameter is a bifunction parameter.
The difference is that the first parameter of bifunction is oldvalue and the second parameter is value.
The logic of generating newvalue is: if oldvalue does not exist, use value. If oldvalue exists, call bifunction to merge oldvalue and value.
We can write the corresponding code as follows:
public void countAfter8WithMerge(){
Map<String,"we"};
Arrays.asList(wordArray).forEach(word->wordCount.merge(word,1,(oldCount,one) -> oldCount + one));
}
The following functions can be replaced by integer:: sum:
public void countAfter8WithMerge(){
Map<String,Integer::sum));
}
Examples of this article https://github.com/ddean2009/learn-java-base-9-to-20/tree/master/java-base