Json底層一覽

在開始瞭解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都會有一個前指針和一個後指針數組

HashMap

LinkedHashMap 是一個HashMap的變種,你們都知道,一個HashMap是由一個桶和一個桶後面的節點組成的,而桶實際上是一個數組,每個桶的索引所對應的值都是由Hash()函數計算得出的。那麼這樣就會致使桶的元素是一個亂序的存儲的,例如在本段代碼中的idname,它們所在的桶索引多是:
數據結構

這樣就致使了一個問題,就是Json的鍵的順序是沒法保證的,那麼既然HashMap是沒法保證的,爲何LinkedHashMap卻能夠保證順序。函數

LinkedHashMap

翻開LinkedHashMap的源碼能夠發現,在其節點類裏面,LinkedHashMap在 HashMap的Entry基礎上又添加了一個beforeafter指針,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

LinkedHashMap的迭代

區別於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呢?

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的設計都是很精妙的...

相關文章
相關標籤/搜索