摘要:本篇涉及少許以下簡稱新特性,請驢友們系好安全帶,準備開車。觀光線路圖是在中新增的一個方法,相對而言較為陌生。其作用是把的計算結果關聯到上即返回值作為新。實際上,乃縮寫,即二元函數之意類似。
本文以jdk1.8中HashMap.compute()方法為切入點,分析其中難理解、有價值的源碼片段(類似源碼查看是ctrl+鼠標左鍵的過程)。本篇涉及少許Java8(以下簡稱J8)新特性,請驢友們系好安全帶,準備開車。觀光線路圖:compute() --> BiFunction --> @FunctionalInterface --> afterNodeAccess() --> computeIfAbsent() --> computeIfPresent()...
? compute()@Override public V compute(K key, BiFunction super K, ? super V, ? extends V> remappingFunction) { if (remappingFunction == null) throw new NullPointerException(); int hash = hash(key); Node[] tab; Node first; int n, i; int binCount = 0; TreeNode t = null; Node old = null; if (size > threshold || (tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; if ((first = tab[i = (n - 1) & hash]) != null) { if (first instanceof TreeNode) old = (t = (TreeNode )first).getTreeNode(hash, key); else { Node e = first; K k; do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { old = e; break; } ++binCount; } while ((e = e.next) != null); } } V oldValue = (old == null) ? null : old.value; V v = remappingFunction.apply(key, oldValue); if (old != null) { if (v != null) { old.value = v; afterNodeAccess(old); } else removeNode(hash, key, null, false, true); } else if (v != null) { if (t != null) t.putTreeVal(this, tab, hash, key, v); else { tab[i] = newNode(hash, key, v, first); if (binCount >= TREEIFY_THRESHOLD - 1) treeifyBin(tab, hash); } ++modCount; ++size; afterNodeInsertion(true); } return v; }
compute()是java8在Map中新增的一個方法,相對而言較為陌生。其作用是把remappingFunction的計算結果關聯到key上(即remappingFunction返回值作為新value)。寫一段它的簡單應用的代碼,并與“同級生”merge()類比加深理解:
HashMap map = new HashMap(); map.put("a", "c"); map.put("b", "h"); map.put("c", "e"); map.compute("a", (k, v) -> "C") ; map.merge("b", "h", (k, v) -> "H") ; map.compute("d", (k, v) -> "D") ; map.merge("c", "e", (k, v) -> null) ; System.out.println(map.toString()); // 輸出結果為:{a=C, b=H, d=D}
下面用一張表來總結源碼最后的判斷對應的操作:
vold | null | not null |
---|---|---|
null | remove | |
not null | put | replace |
/** * Represents a function that accepts two arguments and produces a result. * This is the two-arity specialization of {@link Function}. * *This is a functional interface * whose functional method is {@link #apply(Object, Object)}. * * @param
the type of the first argument to the function * @param the type of the second argument to the function * @param the type of the result of the function * * @see Function * @since 1.8 */ @FunctionalInterface public interface BiFunction { /** * Applies this function to the given arguments. * * @param t the first function argument * @param u the second function argument * @return the function result */ R apply(T t, U u); /** * Returns a composed function that first applies this function to * its input, and then applies the {@code after} function to the result. * If evaluation of either function throws an exception, it is relayed to * the caller of the composed function. * * @param the type of output of the {@code after} function, and of the * composed function * @param after the function to apply after this function is applied * @return a composed function that first applies this function and then * applies the {@code after} function * @throws NullPointerException if after is null */ default BiFunction andThen(Function super R, ? extends V> after) { Objects.requireNonNull(after); return (T t, U u) -> after.apply(apply(t, u)); } }
是的,這也J8新增的梗。當我第一眼看到逼函數(BiFunction)的時候就原地炸了。實際上,“Bi”乃binary縮寫,即“二元函數”之意(類似“1+1=2”)。這類接口稱為“函數式接口”,可以看出,它的==方法有方法體==。且以“default”修飾符修飾,不影響接口的實現類,算是一種向下兼容吧。
? @FunctionalInterface/** * An informative annotation type used to indicate that an interface * type declaration is intended to be a functional interface as * defined by the Java Language Specification. * * Conceptually, a functional interface has exactly one abstract * method. Since {@linkplain java.lang.reflect.Method#isDefault() * default methods} have an implementation, they are not abstract. If * an interface declares an abstract method overriding one of the * public methods of {@code java.lang.Object}, that also does * not count toward the interface"s abstract method count * since any implementation of the interface will have an * implementation from {@code java.lang.Object} or elsewhere. * *Note that instances of functional interfaces can be created with * lambda expressions, method references, or constructor references. * *
If a type is annotated with this annotation type, compilers are * required to generate an error message unless: * *
However, the compiler will treat any interface meeting the * definition of a functional interface as a functional interface * regardless of whether or not a {@code FunctionalInterface} * annotation is present on the interface declaration. * * @jls 4.3.2. The Class Object * @jls 9.8 Functional Interfaces * @jls 9.4.3 Interface Method Body * @since 1.8 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface FunctionalInterface {}
“@FunctionalInterface”并非必須,就像javascript中的“use strict”,使得編譯器能檢查該接口是否存在語法錯誤。此外,從注釋還可以看出:
? afterNodeAccess()函數接口僅有一個抽象方法;
default方法、Object的重載方法(、靜態方法)非抽象方法;
// Callbacks to allow LinkedHashMap post-actions void afterNodeAccess(Nodep) { } void afterNodeInsertion(boolean evict) { } void afterNodeRemoval(Node p) { }
從注釋可以看到這是為LinkedHashMap留的后路,不過HashMap存取操作中經常發現他們的身影,即使實現為空。。
? computeIfAbsent()/computeIfPresent()@Override public V computeIfAbsent(K key, Function super K, ? extends V> mappingFunction) { if (mappingFunction == null) throw new NullPointerException(); int hash = hash(key); Node[] tab; Node first; int n, i; int binCount = 0; TreeNode t = null; Node old = null; if (size > threshold || (tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; if ((first = tab[i = (n - 1) & hash]) != null) { if (first instanceof TreeNode) old = (t = (TreeNode )first).getTreeNode(hash, key); else { Node e = first; K k; do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { old = e; break; } ++binCount; } while ((e = e.next) != null); } V oldValue; if (old != null && (oldValue = old.value) != null) { afterNodeAccess(old); return oldValue; } } V v = mappingFunction.apply(key); if (v == null) { return null; } else if (old != null) { old.value = v; // old.value null afterNodeAccess(old); return v; } else if (t != null) t.putTreeVal(this, tab, hash, key, v); else { tab[i] = newNode(hash, key, v, first); if (binCount >= TREEIFY_THRESHOLD - 1) treeifyBin(tab, hash); } ++modCount; ++size; afterNodeInsertion(true); return v; } public V computeIfPresent(K key, BiFunction super K, ? super V, ? extends V> remappingFunction) { if (remappingFunction == null) throw new NullPointerException(); Node e; V oldValue; int hash = hash(key); if ((e = getNode(hash, key)) != null && (oldValue = e.value) != null) { V v = remappingFunction.apply(key, oldValue); if (v != null) { e.value = v; afterNodeAccess(e); return v; } else removeNode(hash, key, null, false, true); } return null; }
computeIfAbsent()與computeIfPresent()可以說是compute()的“子集”。
這次的功夫主要花在了學習J8的知識點上,經過前2篇后HashMap本身不再那么可怕。你覺得呢?
更多有意思的內容,歡迎訪問筆者小站: rebey.cn
彩蛋最后分享幾個學習Java8過程中看到良心網址(以下鏈接為網站系列文章之一,希望細心的你舉一反三):
Java8初體驗(二)Stream語法詳解;
Java 8 中的 Streams API 詳解;
Java 8 flatMap example;
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/70100.html
摘要:如今行至于此,當觀賞一方。由于所返回的無執行意義。源碼閱讀總體門檻相對而言比,畢竟大多數底層都由實現了。比心可通過這篇文章理解創建一個實例過程圖工作原理往期線路回顧源碼一帶一路系列之源碼一帶一路系列之源碼一帶一路系列之 本文以jdk1.8中LinkedHashMap.afterNodeAccess()方法為切入點,分析其中難理解、有價值的源碼片段(類似源碼查看是ctrl+鼠標左鍵的過程...
摘要:一路至此,風景過半。與雖然名字各異,源碼實現基本相同,除了增加了線程安全。同時注意溢出情況處理。同時增加了考慮并發問題。此外,源碼中出現了大量泛型如。允許為非線程安全有序。 一路至此,風景過半。ArrayList與Vector雖然名字各異,源碼實現基本相同,除了Vector增加了線程安全。所以作者建議我們在不需要線程安全的情況下盡量使用ArrayList。下面看看在ArrayList源...
摘要:同樣在源碼的與分別見看到老朋友和。這樣做可以降低性能消耗的同時,還可以減少序列化字節流的大小,從而減少網絡開銷框架中。使用了反射來尋找是否聲明了這兩個方法。十進制和,通過返回值能反應當前狀態。 Map篇暫告段落,卻并非離我們而去。這不在本篇中你就能經常見到她。HashSet、LinkedHashSet、TreeSet各自基于對應Map實現,各自源碼內容較少,因此歸納為一篇。 HashS...
摘要:觀光線路圖將涉及到的源碼全局變量哈希表初始化長度默認值是負載因子默認表示的填滿程度。根據是否為零將原鏈表拆分成個鏈表,一部分仍保留在原鏈表中不需要移動,一部分移動到原索引的新鏈表中。 前言 本文以jdk1.8中HashMap.putAll()方法為切入點,分析其中難理解、有價值的源碼片段(類似ctrl+鼠標左鍵查看的源碼過程)。?觀光線路圖:putAll() --> putMapEnt...
摘要:表示該類本身不可比表示與對應的之間不可比。當數目滿足時,鏈表將轉為紅黑樹結構,否則繼續擴容。至此,插入告一段落。當超出時,哈希表將會即內部數據結構重建至大約兩倍。要注意的是使用許多有這相同的鍵值肯定會降低哈希表性能。 回顧上期?觀光線路圖:putAll() --> putMapEntries() --> tableSizeFor() --> resize() --> hash() --...
閱讀 2825·2023-04-25 20:06
閱讀 1446·2021-08-26 14:15
閱讀 2234·2021-08-12 13:27
閱讀 1772·2019-08-30 15:55
閱讀 3469·2019-08-30 13:20
閱讀 2825·2019-08-29 15:12
閱讀 3330·2019-08-29 15:06
閱讀 2858·2019-08-29 14:13