|
1 | 1 | # java8-map-functions |
2 | 2 |
|
| 3 | +_Reference_: https://www.nurkiewicz.com/2014/04/hashmap-performance-improvements-in.html |
| 4 | +_Reference_: https://dzone.com/articles/java-8-hashmaps-keys-and-the-comparable-interface |
| 5 | +_Reference_: https://yermilov.github.io/blog/2017/02/24/tiebreaker-regarding-java-hashmap-treenode-and-tiebreakorder/ |
| 6 | + |
| 7 | +# preface |
| 8 | +Basically when a bucket becomes too big (currently: `TREEIFY_THRESHOLD = 8`), |
| 9 | +`HashMap` dynamically replaces it with an ad-hoc implementation of tree |
| 10 | +map. This way rather than having pessimistic `O(n)` we get much better |
| 11 | +`O(logn)`. How does it work? Well, previously entries with conflicting |
| 12 | +keys were simply appended to linked list, which later had to be traversed. |
| 13 | +Now `HashMap` promotes list into binary tree, using hash code as a |
| 14 | +branching variable. If two hashes are different but ended up in the |
| 15 | +same bucket, one is considered bigger and goes to the right. If hashes |
| 16 | +are equal (as in our case), `HashMap` hopes that the keys are `Comparable`, |
| 17 | +so that it can establish some order. This is not a requirement of |
| 18 | +`HashMap` keys, but apparently a good practice. If keys are not |
| 19 | +comparable, don't expect any performance improvements in case of heavy |
| 20 | +hash collisions. |
| 21 | + |
| 22 | +The tree implementation inside the `HashMap` is a `Red-Black` tree, which |
| 23 | +means it will always be balanced. |
| 24 | + |
| 25 | +When the `HashMap` implementation tries to find the location of a new |
| 26 | +entry in the tree, |
| 27 | +first it checks whether the current and the new values are easily |
| 28 | +comparable (`Comparable` interface) or not. In the latter case, it has |
| 29 | +to fall back to a comparison method called `tieBreakOrder(Object a, |
| 30 | +Object b)`. This method tries to compare the two object based on class |
| 31 | +name first, and then using `System.identityHashCode`. However, when |
| 32 | +the key implements Comparable, the process is much simpler. The key |
| 33 | +itself defines how it compares to other keys, so the whole |
| 34 | +insertion/retrieval process speeds up, as there are no extra method |
| 35 | +calls needed for. It's worth mentioning that the same `tieBreakOrder` |
| 36 | +method is used when two Comparable keys turn out to be equal according |
| 37 | +to the compareTo method (the method returns 0). |
| 38 | + |
| 39 | +# summary |
| 40 | +## comparing |
3 | 41 | * `public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K, V>> comparingByKey()` |
4 | 42 | * `public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K, V>> comparingByValue()` |
5 | 43 | * `public static <K, V> Comparator<Map.Entry<K, V>> comparingByKey(Comparator<? super K> cmp)` |
6 | 44 | * `public static <K, V> Comparator<Map.Entry<K, V>> comparingByValue(Comparator<? super V> cmp)` |
7 | | -* `default V getOrDefault(Object key, V defaultValue)` |
| 45 | + |
| 46 | +## processing |
8 | 47 | * `default void forEach(BiConsumer<? super K, ? super V> action)` |
9 | 48 | * `default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function)` |
| 49 | + |
| 50 | +# modifying |
| 51 | +* `default V getOrDefault(Object key, V defaultValue)` |
10 | 52 | * `default V putIfAbsent(K key, V value)` |
11 | 53 | * `default boolean remove(Object key, Object value)` |
12 | 54 | * `default boolean replace(K key, V oldValue, V newValue)` |
|
0 commit comments