摘要:在開(kāi)始了解的原理之前,首先看一段代碼,在這里以阿里的為例。翻開(kāi)的源碼可以發(fā)現(xiàn),在其節(jié)點(diǎn)類(lèi)里面,在的基礎(chǔ)上又添加了一個(gè)和指針,那么這兩個(gè)指針就是雙向鏈表的指針??偨Y(jié)其實(shí)每一個(gè)的設(shè)計(jì)都是很精妙的
在開(kāi)始了解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()); } }
當(dāng)看到上述代碼的時(shí)候,可能一般的程序員都會(huì)想到的是輸出為如下Json串
{"id":"a","name":"b"}
但是運(yùn)行這段程序,你會(huì)發(fā)現(xiàn)控制臺(tái)打印出來(lái)的是如下代碼:
{"name":"b","id":"a"}
那么為什么會(huì)出現(xiàn)這種情況呢,翻開(kāi)FastJson的源碼便知道了,首先定位到 JsonObject 這個(gè)類(lèi)的構(gòu)造函數(shù),如下:
public JSONObject(int initialCapacity, boolean ordered){ if (ordered) { map = new LinkedHashMap(initialCapacity); } else { map = new HashMap (initialCapacity); } }
這里的 ordered 為一個(gè)構(gòu)造參數(shù),表示的是是否按照順序添加,此處先不管,然后可以發(fā)現(xiàn)在阿里的FastJson中,其實(shí)默認(rèn)的Json實(shí)現(xiàn)是一個(gè)Map,那么對(duì)于LinkedHashMap來(lái)講,它是一個(gè)map和雙向鏈表的整合體,所以在LinkedList中,每一個(gè)Node都會(huì)有一個(gè)前指針和一個(gè)后指針
HashMapLinkedHashMap 是一個(gè)HashMap的變種,大家都知道,一個(gè)HashMap是由一個(gè)桶和一個(gè)桶后面的節(jié)點(diǎn)組成的,而桶其實(shí)是一個(gè)數(shù)組,每一個(gè)桶的索引所對(duì)應(yīng)的值都是由Hash()函數(shù)計(jì)算得出的。那么這樣就會(huì)導(dǎo)致桶的元素是一個(gè)亂序的存儲(chǔ)的,例如在本段代碼中的id和name,它們所在的桶索引可能是:
這樣就導(dǎo)致了一個(gè)問(wèn)題,就是Json的鍵的順序是無(wú)法保證的,那么既然HashMap是無(wú)法保證的,為什么LinkedHashMap卻可以保證順序。
LinkedHashMap翻開(kāi)LinkedHashMap的源碼可以發(fā)現(xiàn),在其節(jié)點(diǎn)類(lèi)里面,LinkedHashMap在 HashMap的Entry基礎(chǔ)上又添加了一個(gè)before和after指針,
static class Entryextends HashMap.Node { Entry before, after; Entry(int hash, K key, V value, Node next) { super(hash, key, value, next); } }
那么這兩個(gè)指針就是雙向鏈表的指針。有了這兩個(gè)指針之后,每一個(gè)新插入的節(jié)點(diǎn)都會(huì)知道他的前驅(qū)結(jié)點(diǎn)和后置節(jié)點(diǎn),那么對(duì)于LinkedHashMap的插入順序就會(huì)有保證了。所以其對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)如圖:
在這個(gè)結(jié)構(gòu)里面,桶索引是id的第一個(gè)節(jié)點(diǎn)是一個(gè)頭節(jié)點(diǎn),在新插入name的時(shí)候,LinkedHashMap會(huì)將head節(jié)點(diǎn)的after指針指向name,所以雖然這是一個(gè)HashMap,但是它的順序還是可以保證的。
LinkedHashMap的迭代區(qū)別于HashMap以索引的方式進(jìn)行迭代,LinkedHashMap是以鏈表的指針進(jìn)行迭代的,如以下代碼所示:
abstract class LinkedHashIterator { LinkedHashMap.Entrynext; LinkedHashMap.Entry current; int expectedModCount; LinkedHashIterator() { next = head; expectedModCount = modCount; current = null; } final LinkedHashMap.Entry nextNode() { LinkedHashMap.Entry e = next; //next就是head節(jié)點(diǎn) if (modCount != expectedModCount) throw new ConcurrentModificationException(); if (e == null) throw new NoSuchElementException(); current = e; next = e.after; //此處每一次的迭代都是鏈表的after return e; }
可以看到在每一次迭代的時(shí)候LinkedHashMap都是以鏈表的next節(jié)點(diǎn)作為下一個(gè)迭代,那么HashMap呢?
HashMap的迭代abstract class HashIterator { Nodenext; // next entry to return Node current; // current entry int expectedModCount; // for fast-fail int index; // current slot HashIterator() { expectedModCount = modCount; Node [] 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 nextNode() { Node [] t; Node 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[]中第一個(gè)不為null的桶,所以其實(shí)HashMap的迭代就是依據(jù)桶中的順序來(lái)的,但是LinkedHashMap則是按找鏈表的順序來(lái)的。
總結(jié)其實(shí)每一個(gè)java的設(shè)計(jì)都是很精妙的...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/77055.html
摘要:今天逛了逛,順手精選出了一下近幾個(gè)月以來(lái)上最熱門(mén)的個(gè)項(xiàng)目。相關(guān)閱讀正式開(kāi)源,幫助應(yīng)用快速容器化未來(lái)可能會(huì)上熱門(mén)的項(xiàng)目地址介紹哈哈,皮一下很開(kāi)心。這是我自己開(kāi)源的一份文檔,目前仍在完善中,歡迎各位英雄好漢一起完善。 showImg(https://segmentfault.com/img/remote/1460000015766827?w=391&h=220);今天逛了逛Github,順...
摘要:云加速服務(wù)商日前宣布,正式推出實(shí)時(shí)狀態(tài)與性能監(jiān)控功能。的實(shí)時(shí)狀態(tài)和性能監(jiān)控,覆蓋全國(guó)所有省份,幫助所有用戶(hù)直接全面了解服務(wù)情況,及時(shí)發(fā)現(xiàn)定位和解決突發(fā)問(wèn)題,實(shí)現(xiàn)產(chǎn)品性能的最大優(yōu)化。 云加速服務(wù)商 UPYUN 日前宣布,正式推出 CDN 實(shí)時(shí)狀態(tài)與性能監(jiān)控功能。通過(guò)對(duì)全國(guó) 120 個(gè) CDN 節(jié)點(diǎn)日志的數(shù)據(jù)分析,將速度、耗時(shí)、ISP 線(xiàn)路、地區(qū)、請(qǐng)求占比等多維度的精準(zhǔn)數(shù)據(jù),以地圖的方式具...
摘要:應(yīng)用常見(jiàn)安全漏洞一覽注入注入就是通過(guò)給應(yīng)用接口傳入一些特殊字符,達(dá)到欺騙服務(wù)器執(zhí)行惡意的命令。此外,適當(dāng)?shù)臋?quán)限控制不曝露必要的安全信息和日志也有助于預(yù)防注入漏洞。 web 應(yīng)用常見(jiàn)安全漏洞一覽 1. SQL 注入 SQL 注入就是通過(guò)給 web 應(yīng)用接口傳入一些特殊字符,達(dá)到欺騙服務(wù)器執(zhí)行惡意的 SQL 命令。 SQL 注入漏洞屬于后端的范疇,但前端也可做體驗(yàn)上的優(yōu)化。 原因 當(dāng)使用外...
摘要:應(yīng)用常見(jiàn)安全漏洞一覽注入注入就是通過(guò)給應(yīng)用接口傳入一些特殊字符,達(dá)到欺騙服務(wù)器執(zhí)行惡意的命令。此外,適當(dāng)?shù)臋?quán)限控制不曝露必要的安全信息和日志也有助于預(yù)防注入漏洞。 web 應(yīng)用常見(jiàn)安全漏洞一覽 1. SQL 注入 SQL 注入就是通過(guò)給 web 應(yīng)用接口傳入一些特殊字符,達(dá)到欺騙服務(wù)器執(zhí)行惡意的 SQL 命令。 SQL 注入漏洞屬于后端的范疇,但前端也可做體驗(yàn)上的優(yōu)化。 原因 當(dāng)使用外...
摘要:番茄工作法簡(jiǎn)約而不簡(jiǎn)單,本書(shū)亦然。在番茄工作法一個(gè)個(gè)短短的分鐘內(nèi),你收獲的不僅僅是效率,還會(huì)有意想不到的成就感。 @author ASCE1885的 Github 簡(jiǎn)書(shū) 微博 CSDN 知乎本文由于潛在的商業(yè)目的,不開(kāi)放全文轉(zhuǎn)載許可,謝謝! showImg(/img/remote/1460000007319503?w=728&h=792); 廣而告之時(shí)間:我的新書(shū)《Android 高...
閱讀 3729·2021-11-22 11:59
閱讀 1010·2021-09-27 13:36
閱讀 3748·2021-09-24 09:47
閱讀 2350·2021-09-01 11:39
閱讀 1040·2021-08-31 09:37
閱讀 2393·2021-08-05 10:01
閱讀 1765·2019-08-30 15:55
閱讀 755·2019-08-30 15:54