在開始瞭解Json的原理以前,首先看一段代碼,在這裏以阿里的FastJson
爲例。java
public class JsonRun { public static void main(String[] args) { JSONObject jsonObject =new JSONObject(); jsonObject.put("id","a"); jsonObject.put("name","b"); System.out.println(jsonObject.toJSONString()); } }
當看到上述代碼的時候,可能通常的程序員都會想到的是輸出爲以下Json
串程序員
{"id":"a","name":"b"}
可是運行這段程序,你會發現控制檯打印出來的是以下代碼:
{"name":"b","id":"a"}
那麼爲何會出現這種狀況呢,翻開FastJson
的源碼便知道了,首先定位到 JsonObject 這個類的構造函數,以下:json
public JSONObject(int initialCapacity, boolean ordered){ if (ordered) { map = new LinkedHashMap<String, Object>(initialCapacity); } else { map = new HashMap<String, Object>(initialCapacity); } }
這裏的 ordered
爲一個構造參數,表示的是是否按照順序添加,此處先無論,而後能夠發如今阿里的FastJson中,其實默認的Json實現是一個Map,那麼對於LinkedHashMap來說,它是一個map和雙向鏈表的整合體,因此在LinkedList中,每個Node都會有一個前指針和一個後指針數組
LinkedHashMap 是一個HashMap的變種,你們都知道,一個HashMap是由一個桶和一個桶後面的節點組成的,而桶實際上是一個數組,每個桶的索引所對應的值都是由Hash()
函數計算得出的。那麼這樣就會致使桶的元素是一個亂序的存儲的,例如在本段代碼中的id
和name
,它們所在的桶索引多是:
數據結構
這樣就致使了一個問題,就是Json的鍵的順序是沒法保證的,那麼既然HashMap是沒法保證的,爲何LinkedHashMap卻能夠保證順序。函數
翻開LinkedHashMap的源碼能夠發現,在其節點類裏面,LinkedHashMap在 HashMap的Entry基礎上又添加了一個before
和after
指針,spa
static class Entry<K,V> extends HashMap.Node<K,V> { Entry<K,V> before, after; Entry(int hash, K key, V value, Node<K,V> next) { super(hash, key, value, next); } }
那麼這兩個指針就是雙向鏈表的指針。有了這兩個指針以後,每個新插入的節點都會知道他的前驅結點和後置節點,那麼對於LinkedHashMap的插入順序就會有保證了。因此其對應的數據結構如圖:
設計
在這個結構裏面,桶索引是id
的第一個節點是一個頭節點,在新插入name
的時候,LinkedHashMap會將head節點的after
指針指向name,因此雖然這是一個HashMap,可是它的順序仍是能夠保證的。3d
區別於HashMap以索引的方式進行迭代,LinkedHashMap
是以鏈表的指針進行迭代的,如如下代碼所示:指針
abstract class LinkedHashIterator { LinkedHashMap.Entry<K,V> next; LinkedHashMap.Entry<K,V> current; int expectedModCount; LinkedHashIterator() { next = head; expectedModCount = modCount; current = null; } final LinkedHashMap.Entry<K,V> nextNode() { LinkedHashMap.Entry<K,V> e = next; //next就是head節點 if (modCount != expectedModCount) throw new ConcurrentModificationException(); if (e == null) throw new NoSuchElementException(); current = e; next = e.after; //此處每一次的迭代都是鏈表的after return e; }
能夠看到在每一次迭代的時候LinkedHashMap都是以鏈表的next節點做爲下一個迭代,那麼HashMap呢?
abstract class HashIterator { Node<K,V> next; // next entry to return Node<K,V> current; // current entry int expectedModCount; // for fast-fail int index; // current slot HashIterator() { expectedModCount = modCount; Node<K,V>[] t = table; current = next = null; index = 0; if (t != null && size > 0) { // advance to first entry do {} while (index < t.length && (next = t[index++]) == null); } } final Node<K,V> nextNode() { Node<K,V>[] t; Node<K,V> e = next; if (modCount != expectedModCount) throw new ConcurrentModificationException(); if (e == null) throw new NoSuchElementException(); if ((next = (current = e).next) == null && (t = table) != null) { do {} while (index < t.length && (next = t[index++]) == null); } return e; }
注意這一段代碼
if (t != null && size > 0) { // advance to first entry do {} while (index < t.length && (next = t[index++]) == null); }
這一段代碼的做用是找出table[]
中第一個不爲null的桶,因此其實HashMap的迭代就是依據桶中的順序來的,可是LinkedHashMap則是按找鏈表的順序來的。
其實每個java的設計都是很精妙的...