HashMap維護了一個Entry數組,put(K key,V value)元素到HashMap中時經過key的hash碼計算其在數組中的索引位置,若索引位置上已有元素造成哈希碰撞. html
jdk1.8之前:哈希碰撞以後,在碰撞位置將會造成一個鏈表,新加入的元素將放置於表頭位置(明顯缺點:當碰撞元素過多,鏈表過長,遍歷鏈表查找元素的速度就較慢)java
jdk1.7關鍵代碼: public V put(K key, V value) { if (table == EMPTY_TABLE) { inflateTable(threshold); } //若key爲null,則特殊處理一下(放到數組索引爲0的位置) if (key == null) return putForNullKey(value); //計算key在數組中對應的索引值 int hash = hash(key); int i = indexFor(hash, table.length); //遍歷索引位置上的鏈表,若找到匹配key,則將新值賦給該節點,並返回舊值 for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } //若沒有匹配的已存在的key,則在索引位置對應的鏈表上新增節點,next指向原鏈表的表頭元素 modCount++; addEntry(hash, key, value, i); return null; } void addEntry(int hash, K key, V value, int bucketIndex) { if ((size >= threshold) && (null != table[bucketIndex])) { resize(2 * table.length); hash = (null != key) ? hash(key) : 0; bucketIndex = indexFor(hash, table.length); } createEntry(hash, key, value, bucketIndex); } void createEntry(int hash, K key, V value, int bucketIndex) { Entry<K,V> e = table[bucketIndex]; table[bucketIndex] = new Entry<>(hash, key, value, e); size++; }
jdk1.8:(a)新加入的元素將置於鏈表的尾部;(b)當鏈表元素達到8個時,此鏈表將轉換爲紅黑樹 ,優勢:HashMap的查詢效率在jdk1.8中獲得了大大的提高sql
jdk1.8關鍵代碼: final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; //若map中的tab數組爲空,則先初始化數組 if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; //若tab數組中,當前元素hash值對應的索引位置上爲null,則直接將元素置於該位置 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else { Node<K,V> e; K k; //若元素與鏈表第一個元素的hash值和key值均相等,則直接將value賦給該元素 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; //若當前位置是一個樹節點(說明已經轉爲紅黑樹了),則挨個與樹節點比較,若hash和key與某個節點相等則,則將value賦給該節點,不然新增一個樹節點 else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { //挨個遍歷鏈表中元素,若hash和key匹配,則找到匹配的元素 for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { //若遍歷到表尾仍然沒有找到,則在尾部新增一個元素 p.next = newNode(hash, key, value, null); //若元素個數達到8個,則將鏈表轉成紅黑樹 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold) resize(); afterNodeInsertion(evict); return null; }
JVM運行時數據區去掉永久代(Permanent Generation ),改成元空間(MetaSpace)。數據庫
jdk1.8以前的JVM運行時數據區: 數組
方法區即永久代(Method Area): 全部線程共享,用於存儲JVM加載的類信息、常量、靜態變量、JIT編譯器編譯後的代碼等數據。它只是JVM規範中定義的一個概念,是堆的一個邏輯部分,爲了與普通堆區分開來,有一個別名叫作Non-Heap(非堆)。安全
jdk1.8及之後,永久代被移除,取而代之的是元空間,它直接從操做系統分配內存,獨立且能夠自由擴展,最大可分配空間就是系統可用空間,所以不會遇到PermGen的內存溢出錯誤(java.lang.OutOfMemoryError: PermGen space)。在JVM參數方面,使用-XX:MetaSpaceSize和-XX:MaxMetaspaceSize代替原來的-XX:PermSize和-XX:MaxPermSize。數據結構
Jdk1.8新增了一個關鍵字default,用於修飾接口中的非抽象方法,只能經過接口實現類的對象進行調用app
Jdk1.8容許在接口中定義static方法,調用方式必須爲:接口名.方法名()eclipse
Jdk1.8提出了一個」函數式接口」的概念:只有一個抽象方法的接口稱爲函數式接口,可以使用@FunctionalInterface註解進行檢查(此註解只是用來檢查是不是函數式接口,並非說,加了此註解的接口是函數式接口,若是加了此註解,接口中有多個抽象方法將會編譯報錯)。常見的函數式接口:Runnable,Comparator等ide
Optional 類(java.util.Optional) 是一個容器類,表明一個值存在或不存在,原來用 null 表示一個值不存在,如今 Optional 能夠更好的表達這個概念。而且能夠避免空指針異常。
經常使用方法: Optional.of(T t) : 建立一個 Optional 實例 Optional.empty() : 建立一個空的 Optional 實例 Optional.ofNullable(T t):若 t 不爲 null,建立 Optional 實例,不然建立空實例 isPresent() : 判斷是否包含值 orElse(T t) : 若是調用對象包含值,返回該值,不然返回t orElseGet(Supplier s) :若是調用對象包含值,返回該值,不然返回 s 獲取的值 map(Function f): 若是有值對其處理,並返回處理後的Optional,不然返回 Optional.empty() flatMap(Function mapper):與 map 相似,要求返回值必須是Optional
Lambda表達式是對函數式接口中抽象方法的實現的簡寫方式.
格式: ()-> {} 操做符「->」被稱爲 Lambda 操做符或箭頭操做符。它將 Lambda 分爲兩個部分: 左側: 指定了 Lambda 表達式須要的全部參數 右側: 指定了 Lambda 體,即 Lambda 表達式要執行的功能。 語法格式一:無參數,無返回值 () -> System.out.println("Hello Lambda!"); 語法格式二:有一個參數,而且無返回值 (x) -> System.out.println(x) 語法格式三:有兩個以上的參數,有返回值,而且 Lambda 體中有多條語句 Comparator<Integer> com = (x, y) -> { System.out.println("函數式接口"); return Integer.compare(x, y); }; 語法格式四: (1).Lambda 表達式的參數列表的數據類型能夠省略不寫,jdk1.7引入的「類型推斷」機制在jdk1.8獲得了強化(注意:若不寫,都不寫) (2).若只有一個參數,小括號能夠省略不寫 (3).若 Lambda 體中只有一條語句, return 和 大括號均可以省略不寫
Lambda是基於函數式接口的,難道使用lambda表達式還必需要寫一個函數式接口?固然不是! jdk開發人員想到了這個問題.
Jdk提供了一些函數式接口:四大核心接口,N個擴展接口位於java.util.function包下. 四大核心函數式接口: (1) 消費型接口Comsumer<T>:有一個參數無返回值的抽象方法,參數類型T (2) 供給型接口Supplier<T>:無參數有返回值的抽象方法,返回值類型T (3) 函數型接口Function<T,R>:一個參數有返回值的抽象方法,參數類型T,返回值類型R (4) 斷言型接口Predicate<T>:一個參數有返回值的抽象方法,參數類型T,返回值類型boolean
當要傳遞給Lambda體的操做,已經有實現的方法了,可使用方法引用!(實現抽象方法的參數列表,必須與方法引用方法的參數列表保持一致!)
方法引用:使用操做符 「::」 將方法名和對象或類的名字分隔開來 例如: Lambda表達式: x->System.out.println(x); 等同於: 方法引用(實例名::實例方法名): System.out::println; Lambda表達式: t -> Integer.parseInt(t); 等同於 方法引用(類名::類方法名): Integer::parseInt; Lambda表達式: (x, y) -> x.equals(y); 等同於 方法引用(類名::類方法名): String::equals; 方法引用的判斷規則: (1)方法引用所引用的方法的參數列表與返回值類型,須要與函數式接口中抽象方法的參數列表和返回值類型保持一致! (2)若Lambda 的參數列表的第一個參數,是實例方法的調用者,第二個參數(或無參)是實例方法的參數時,格式: ClassName::MethodName
構造器的參數列表,須要與函數式接口中參數列表保持一致! 格式: 類名::new
Stream 是 Java8 中處理集合的關鍵抽象概念,它能夠指定你但願對集合進行的操做,能夠執行很是複雜的查找、過濾和映射數據等操做。使用Stream API 對集合數據進行操做,就相似於使用 SQL 執行數據庫查詢。也可使用 Stream API 來並行執行操做。簡而言之,Stream API 提供了一種高效且易於使用的處理數據的方式。
注意: (1)Stream 本身不會存儲元素。 (2)Stream 不會改變源對象。相反,他們會返回一個持有結果的新Stream。 (3)Stream 操做是延遲執行的。這意味着他們會等到須要結果的時候才執行。
(1) Java8 中的 Collection 接口被擴展,提供了兩個獲取流的方法: default Stream<E> stream() : 返回一個順序流 default Stream<E> parallelStream() : 返回一個並行流 (2) Java8 中的 Arrays 的靜態方法 stream() 能夠獲取數組流: public static <T> Stream<T> stream(T[] array): 返回一個流 public static IntStream stream(int[] array) public static LongStream stream(long[] array) public static DoubleStream stream(double[] array) (3) 可使用靜態方法 Stream.of(), 經過顯示值建立一個流。它能夠接收任意數量的參數。 public static <T> Stream<T> of(T... values) : 返回一個流
多箇中間操做能夠鏈接起來造成一個流水線,除非流水線上觸發終止操做,不然中間操做不會執行任何的處理!而在終止操做時一次性所有處理,稱爲「惰性求值」 。(例子: MyTestStream3.test1()).
(1) 篩選與切片 filter(Predicate p): 過濾,從流中過濾出符合條件的元素,接收一個斷言型Lambda distinct(): 去重複,根據流中元素的hashCode()和equals()方法去除流中的重複元素 limit(long maxSize): 截斷流,使其元素不超過給定數量 skip(long n): 跳過元素,返回一個扔掉了前 n 個元素的流。若流中元素不足 n 個,則返回一個空流。與 limit(n) 互補 (2) 映射 map(Function f): 接收一個函數做爲參數,該函數會被應用到每一個元素上,並將其映射成一個新的元素。 (3) 排序 sorted(): 產生一個新流,其中按天然順序排序 sorted(Comparator comp): 產生一個新流,其中按比較器順序排序
(1)查找與匹配 allMatch(Predicate p): 檢查是否匹配全部元素 anyMatch(Predicate p): 檢查是否至少匹配一個元素 noneMatch(Predicate p): 檢查是否沒有匹配全部元素 findFirst(): 返回第一個元素 findAny(): 返回流中的任意元素 count(): 返回流中的元素總數 max(Comparator c): 返回流中最大值 min(Comparator c): 返回流中最小值 forEach(Consumer c): 內部迭代 (2)規約 reduce(T iden,BinaryOperator b): 能夠將流中元素反覆結合起來,獲得一個值。返回 T reduce(BinaryOperator b): 能夠將流中元素反覆結合起來,獲得一個值。返回 Optional<T> (3)收集 collect(Collector c): 將流轉換爲其餘形式。接收一個 Collector接口的實現,用於給Stream中元素作彙總的方法, Collectors提供了豐富的靜態方法供咱們使用
Jdk1.8以前的時間日期API的缺陷: 1.Java的日期/時間類的定義不一致,在java.util和java.sql的包中都有日期類,此外用於格式化和解析的類在java.text包中定義。 2.java.util.Date同時包含日期和時間,而java.sql.Date僅包含日期,將其歸入java.sql包並不合理。另外這兩個類都有相同的名字,這自己就是一個很是糟糕的設計。 3.全部的日期類都是可變的,所以他們都不是線程安全的,這是Java日期類最大的問題之一。 4.日期類並不提供國際化,沒有時區支持,所以Java引入了java.util.Calendar和java.util.TimeZone類,但他們一樣存在上述全部的問題。 5.計算兩個日期時間之間的間隔比較麻煩,所以出現了一個joda-time.jar.
Jdk1.8新的時間日期API解決了上述問題
LocalDate、 LocalTime、 LocalDateTime 類的實例是不可變的對象,分別表示使用ISO-8601日曆系統(ISO-8601日曆系統是國際標準化組織制定的現代公民的日期和時間的表示法)的日期、時間、日期和時間。
(1) Duration:用於計算兩個「時間」間隔
(2) Period:用於計算兩個「日期」間隔
java.time.format.DateTimeFormatter 類
PS:JDK1.8的新特性還有不少,這裏只是簡要介紹了一些咱們平時常常接觸到的.還有其餘的須要本身去深刻學習一下!
插件的介紹及用法: http://www.javashuo.com/article/p-gwrlneql-a.html 插件的安裝:將如下壓縮包中的plugins目錄中的jar包放到eclipse的plugins目錄,而後重啓便可