在开始了解Json的原理之前,首先看一段代码,在这里以阿里的FastJson为例。

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 这个类的构造函数,如下:

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指针,

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,但是它的顺序还是可以保证的。

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则是按找链表的顺序来的。

总结

JSON底层为一个Map,根据传入的参数选择有序的HashMap还是无序的LinkedHashMap。

最新文章

  1. AC日记——地鼠游戏 codevs 1052
  2. oracle---触发器总结
  3. MongoDB环境准备
  4. Mac Pro 安装 Sublime Text 2.0.2,个性化设置,主题 和 插件 收藏
  5. HDU 1018 Big Number (数学题)
  6. OpenGL的GLUT初始化函数[转]
  7. 20 款超棒免费的 Bootstrap 管理和前端模板
  8. 如何实现Windows Phone代码与Unity相互通信(事件方式)
  9. 八大排序方法汇总(选择排序,插入排序-简单插入排序、shell排序,交换排序-冒泡排序、快速排序、堆排序,归并排序,计数排序)
  10. Javascript之&lt;script&gt;标签
  11. MVC3的一个意外的异常 String was not recognized as a valid Boolean. @using (Html.BeginForm())
  12. iOS人脸识别(CoreImage)
  13. Android手机配置gcc,实现手机编译代码
  14. java&#160;实现Excel压缩成Zip导出
  15. struts2.0中struts.xml配置文件详解
  16. [算法] dijkstra单源无负权最小路径算法
  17. 转自知乎-wifi破解
  18. gcc编译相关tips
  19. 【BootStrap】 布局组件 I
  20. Warning: mysqli_connect(): The server requested authentication method unknown to the client [caching_sha2_password]报错解决方法

热门文章

  1. idea查看源码没有注释的问题
  2. idea的eclipse快速定位link with editor
  3. IdentityServer4学习记录
  4. 【leetcode算法-简单】9. 回文数
  5. 亿级Web系统搭建――单机到分布式集群 转载
  6. 【51nod】2027 期望问题
  7. axios拦截器的介绍
  8. mybatis的BLOB存储与读取
  9. Vue-img-preload
  10. fetch---基本使用