在开始了解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的设计都是很精妙的...