HashMap簡(jiǎn)介

HashMap基于哈希表的Map接口實(shí)現(xiàn)瓷式,是以key-value存儲(chǔ)形式存在闽烙。

系統(tǒng)會(huì)根據(jù)hash算法來計(jì)算key-value的存儲(chǔ)位置遥皂,可以通過key快速存取value指攒。

HashMap基于hashing原理,我們通過put()和get()方法儲(chǔ)存和獲取對(duì)象址儒。當(dāng)我們將鍵值對(duì)傳遞給put()方法時(shí)芹枷,它調(diào)用鍵對(duì)象的hashCode()方法來計(jì)算hashcode,然后找到bucket位置來儲(chǔ)存值對(duì)象莲趣。當(dāng)獲取對(duì)象時(shí)鸳慈,通過鍵對(duì)象的equals()方法找到正確的鍵值對(duì),然后返回值對(duì)象喧伞。

HashMap使用鏈表來解決碰撞問題走芋,當(dāng)發(fā)生碰撞了,對(duì)象將會(huì)儲(chǔ)存在鏈表的下一個(gè)節(jié)點(diǎn)中潘鲫。 HashMap在每個(gè)鏈表節(jié)點(diǎn)中儲(chǔ)存鍵值對(duì)對(duì)象翁逞。

當(dāng)兩個(gè)不同的鍵對(duì)象的hashcode相同時(shí)會(huì)發(fā)生什么? 它們會(huì)儲(chǔ)存在同一個(gè)bucket位置的鏈表中溉仑。鍵對(duì)象的equals()方法用來找到鍵值對(duì)挖函。

定義

HashMap實(shí)現(xiàn)了Map接口,Map接口定義了鍵映射到值的規(guī)則彼念。HashMap繼承了AbstractMap挪圾,AbstractMap提供接口的主要實(shí)現(xiàn),以最大限度的減少HashMap實(shí)現(xiàn)Map接口所需的工作逐沙。

初始容量和負(fù)載因子

默認(rèn)初始容量16哲思,默認(rèn)負(fù)載因子0.75。這兩個(gè)參數(shù)是影響HashMap性能的重要參數(shù)吩案,其中容量表示哈希表中桶的數(shù)量棚赔,初始容量是創(chuàng)建哈希表時(shí)的容量,負(fù)載因子是哈希表在其容量自動(dòng)增加之前可以達(dá)到多滿的一種尺度徘郭,它衡量的是一個(gè)散列表的空間的使用程度靠益,負(fù)載因子越大表示散列表的裝填程度越高,反之愈小残揉。對(duì)于使用鏈表法的散列表來說胧后,查找一個(gè)元素的平均時(shí)間是O(1+a),因此如果負(fù)載因子越大抱环,對(duì)空間的利用更充分壳快,然而后果是查找效率的降低;如果負(fù)載因子太小镇草,那么散列表的數(shù)據(jù)將過于稀疏眶痰,對(duì)空間造成嚴(yán)重浪費(fèi)。系統(tǒng)默認(rèn)負(fù)載因子為0.75梯啤,一般情況下我們是無需修改的竖伯。

數(shù)據(jù)結(jié)構(gòu)

Java中最常用的兩種結(jié)構(gòu)是數(shù)組和模擬指針(引用),幾乎所有的數(shù)據(jù)結(jié)構(gòu)都可以利用這兩種來組合實(shí)現(xiàn)因宇,HashMap也是如此七婴。實(shí)際上HashMap是一個(gè)“鏈表散列”,如下是它數(shù)據(jù)結(jié)構(gòu):

HashMap.png

組察滑,只是數(shù)組的每一項(xiàng)都是一條鏈本姥。其中參數(shù)initialCapacity就代表了該數(shù)組的長度。

//空表
static final Entry<?,?>[] EMPTY_TABLE = {};
//用于存儲(chǔ)的表杭棵,長度可以調(diào)整婚惫,且必須是2的n次冪
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;

每次新建一個(gè)HashMap時(shí),都會(huì)初始化一個(gè)table數(shù)組魂爪。table數(shù)組的元素為Entry節(jié)點(diǎn)先舷。

其中Entry為HashMap的內(nèi)部類,它包含了鍵key滓侍、值value蒋川、下一個(gè)節(jié)點(diǎn)next,以及hash值撩笆,這是非常重要的捺球,正是由于Entry才構(gòu)成了table數(shù)組的項(xiàng)為鏈表缸浦。

存儲(chǔ)實(shí)現(xiàn):put(key,value)

public V put(K key, V value) {
    //當(dāng)表為空表時(shí),擴(kuò)展表
    if (table == EMPTY_TABLE) {
        inflateTable(threshold);
    }
    //當(dāng)key為null時(shí)氮兵,保存null在table第一個(gè)位置中
    if (key == null)
        return putForNullKey(value);
    //計(jì)算key的hash值
    int hash = hash(key);
    //計(jì)算key hash值在table數(shù)組中的位置
    int i = indexFor(hash, table.length);
    //在i處開始迭代e裂逐,找到key保存的位置
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
        //判斷該條鏈上是否有hash值相同的(key相同),若存在相同的泣栈,則直接覆蓋value卜高,返回舊value
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
        }
    }
    //修改次數(shù)加1
    modCount++;
    //將key,value添加到i位置處
    addEntry(hash, key, value, i);
    return null;
}

通過源碼我們可以清晰看到HashMap保存數(shù)據(jù)的過程為:首先判斷表是否為空南片,為空的話掺涛,先擴(kuò)展表;然后判斷key是否為null疼进,若為null薪缆,則直接調(diào)用putForNullKey方法。若不為空則先計(jì)算key的hash值伞广,然后根據(jù)hash值搜索在table數(shù)組中的索引位置矮燎,如果table數(shù)組在該位置處有元素,則通過比較是否存在相同的key赔癌,若存在則覆蓋原來key的value诞外,否則將該元素保存在鏈頭(最先保存的元素放在鏈尾)。若table在該處沒有元素灾票,則直接保存峡谊。

  1. 迭代:此處迭代原因就是為了防止存在相同的key值,若發(fā)現(xiàn)兩個(gè)hash值(key)相同時(shí)刊苍,HashMap的處理方式是用新value替換舊value既们,這里并沒有處理key,這就解釋了HashMap中沒有兩個(gè)相同的key正什。

  2. int hash = hash(key); hash方法啥纸,計(jì)算key的hash值,代碼如下:

    final int hash(Object k) {
        int h = hashSeed;
        if (0 != h && k instanceof String) {
            return sun.misc.Hashing.stringHash32((String) k);
        }
    
        h ^= k.hashCode();
    
        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }
    
  3. 對(duì)于HashMap的table而言婴氮,數(shù)據(jù)分布需要均勻(最好每項(xiàng)都只有一個(gè)元素斯棒,這樣就可以直接找到),不能太緊也不能太松主经,太緊會(huì)導(dǎo)致查詢速度慢荣暮,太松則浪費(fèi)空間。計(jì)算hash值后罩驻,怎么才能保證table元素分布均與呢穗酥?我們會(huì)想到取模,但是由于取模的消耗較大,HashMap是這樣處理的:調(diào)用indexFor方法砾跃。

    static int indexFor(int h, int length) {
        // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
        return h & (length-1);
    }
    

    HashMap的底層數(shù)組長度總是2的n次方骏啰,在構(gòu)造函數(shù)中存在:capacity <<= 1;這樣做總是能夠保證HashMap的底層數(shù)組長度為2的n次方。當(dāng)length為2的n次方時(shí)抽高,h&(length - 1)就相當(dāng)于對(duì)length取模判耕,而且速度比直接取模快得多厨内,這是HashMap在速度上的一個(gè)優(yōu)化祈秕。

    indexFor方法渺贤,該方法僅有一條語句:h&(length - 1)雏胃,這句話除了上面的取模運(yùn)算外還有一個(gè)非常重要的責(zé)任:均勻分布table數(shù)據(jù)和充分利用空間。
    當(dāng)length = 2^n時(shí)志鞍,不同的hash值發(fā)生碰撞的概率比較小瞭亮,這樣就會(huì)使得數(shù)據(jù)在table數(shù)組中分布較均勻,查詢速度也較快固棚。

這里我們?cè)賮韽?fù)習(xí)put的流程:當(dāng)我們想往一個(gè)HashMap中添加一對(duì)key-value時(shí)统翩,系統(tǒng)首先會(huì)計(jì)算key的hash值,然后根據(jù)hash值確認(rèn)在table中存儲(chǔ)的位置此洲。若該位置沒有元素厂汗,則直接插入。否則迭代該處元素鏈表并依此比較其key的hash值呜师。如果兩個(gè)hash值相等且key值相等(e.hash == hash && ((k = e.key) == key || key.equals(k))),則用新的Entry的value覆蓋原來節(jié)點(diǎn)的value娶桦。如果兩個(gè)hash值相等但key值不等 ,則將該節(jié)點(diǎn)插入該鏈表的鏈頭汁汗。具體的實(shí)現(xiàn)過程見addEntry方法衷畦,如下:

void addEntry(int hash, K key, V value, int bucketIndex) {
    //HashMap元素超過極限,則擴(kuò)容為兩倍
    if ((size >= threshold) && (null != table[bucketIndex])) {
        resize(2 * table.length);
        hash = (null != key) ? hash(key) : 0;
        bucketIndex = indexFor(hash, table.length);
    }
    //創(chuàng)建新的Entry
    createEntry(hash, key, value, bucketIndex);
}
void createEntry(int hash, K key, V value, int bucketIndex) {
    //獲取bucketIndex處的Entry
    Entry<K,V> e = table[bucketIndex];
    //將新創(chuàng)建的 Entry 放入 bucketIndex 索引處知牌,并讓新的 Entry 指向原來的 Entry 
    table[bucketIndex] = new Entry<>(hash, key, value, e);
    size++;
}
  1. 鏈的產(chǎn)生:這是一個(gè)非常優(yōu)雅的設(shè)計(jì)祈争。系統(tǒng)總是將新的Entry對(duì)象添加到bucketIndex處。如果bucketIndex處已經(jīng)有了對(duì)象角寸,那么新添加的Entry對(duì)象將指向原有的Entry對(duì)象菩混,形成一條Entry鏈,但是若bucketIndex處沒有Entry對(duì)象扁藕,也就是e==null,那么新添加的Entry對(duì)象指向null墨吓,也就不會(huì)產(chǎn)生Entry鏈了。
  2. 擴(kuò)容問題:隨著HashMap中元素的數(shù)量越來越多纹磺,發(fā)生碰撞的概率就越來越大帖烘,所產(chǎn)生的鏈表長度就會(huì)越來越長,這樣勢(shì)必會(huì)影響HashMap的速度橄杨,為了保證HashMap的效率秘症,系統(tǒng)必須要在某個(gè)臨界點(diǎn)進(jìn)行擴(kuò)容處理照卦。該臨界點(diǎn)在當(dāng)HashMap中元素的數(shù)量等于table數(shù)組長度*加載因子。但是擴(kuò)容是一個(gè)非常耗時(shí)的過程乡摹,因?yàn)樗枰匦掠?jì)算這些數(shù)據(jù)在新table數(shù)組中的位置并進(jìn)行復(fù)制處理役耕。所以如果我們已經(jīng)預(yù)知HashMap中元素的個(gè)數(shù),那么預(yù)設(shè)元素的個(gè)數(shù)能夠有效的提高HashMap的性能聪廉。

讀取實(shí)現(xiàn):get(key)

通過key的hash值找到在table數(shù)組中的索引處的Entry瞬痘,然后返回該key對(duì)應(yīng)的value即可。

public V get(Object key) {
    //若為null板熊,獲取null對(duì)應(yīng)的value
    if (key == null)
        return getForNullKey();
    //getEntry(key)為真正獲取方法
    Entry<K,V> entry = getEntry(key);

    return null == entry ? null : entry.getValue();
}
final Entry<K,V> getEntry(Object key) {
    if (size == 0) {
        return null;
    }
    //根據(jù)key獲取hash值
    int hash = (key == null) ? 0 : hash(key);
    //取出table數(shù)組中指定索引處的值
    for (Entry<K,V> e = table[indexFor(hash, table.length)];
         e != null;
         e = e.next) {
        Object k;
        //key相同框全,返回對(duì)應(yīng)的value
        if (e.hash == hash &&
            ((k = e.key) == key || (key != null && key.equals(k))))
            return e;
    }
    return null;
}

在這里能夠根據(jù)key快速的取到value除了和HashMap的數(shù)據(jù)結(jié)構(gòu)密不可分外,還和Entry有莫大的關(guān)系干签,在前面就提到過津辩,HashMap在存儲(chǔ)過程中并沒有將key,value分開來存儲(chǔ)容劳,而是當(dāng)做一個(gè)整體key-value來處理的喘沿,這個(gè)整體就是Entry對(duì)象。同時(shí)value也只相當(dāng)于key的附屬而已竭贩。在存儲(chǔ)的過程中蚜印,系統(tǒng)根據(jù)key的hashcode來決定Entry在table數(shù)組中的存儲(chǔ)位置,在取的過程中同樣根據(jù)key的hashcode取出相對(duì)應(yīng)的Entry對(duì)象留量。

源碼分析

jdk1.7.0_71

//默認(rèn)初始化容量
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
//最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;
//系統(tǒng)默認(rèn)負(fù)載因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//空表
static final Entry<?,?>[] EMPTY_TABLE = {};
//用于存儲(chǔ)的表窄赋,長度可以調(diào)整,且必須是2的n次冪
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
//map的size
transient int size;
//下次擴(kuò)充的臨界值 capacity * load factor
int threshold;
//哈希表的負(fù)載因子
final float loadFactor;
//在使用迭代器遍歷的時(shí)候肪获,用來檢查列表中的元素是否發(fā)生結(jié)構(gòu)性變化(列表元素?cái)?shù)量發(fā)生改變的一個(gè)計(jì)數(shù))了寝凌,主要在多線程環(huán)境下需要使用,防止一個(gè)線程正在迭代遍歷孝赫,另一個(gè)線程修改了這個(gè)列表的結(jié)構(gòu)较木。
transient int modCount;
//容量閾值,默認(rèn)大小為Integer.MAX_VALUE
static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;

//計(jì)算哈希值得時(shí)候用
transient int hashSeed = 0;

Holder 靜態(tài)內(nèi)部類,存放一些在虛擬機(jī)啟動(dòng)后才能初始化的值

容量閾值青柄,初始化hashSeed的時(shí)候會(huì)用到該值

static final int ALTERNATIVE_HASHING_THRESHOLD;

static靜態(tài)塊

獲取系統(tǒng)變量jdk.map.althashing.threshold
jdk.map.althashing.threshold系統(tǒng)變量默認(rèn)為-1伐债,如果為-1,則將閾值設(shè)為Integer.MAX_VALUE

HashMap(int initialCapacity, float loadFactor) 指定容量和負(fù)載因子 構(gòu)造

public HashMap(int initialCapacity, float loadFactor) {
    ...
    init();
}

HashMap(int initialCapacity) 指定初始容量的構(gòu)造,負(fù)載因子為默認(rèn)

public HashMap(int initialCapacity) {}

HashMap() 默認(rèn)初始容量和默認(rèn)負(fù)載因子的構(gòu)造

public HashMap(){}

HashMap(Map<? extends K, ? extends V> m) 用map初始化

public HashMap(Map<? extends K, ? extends V> m) {
    //調(diào)用構(gòu)造,初始化空的hashMap
    this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
                      DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
        //擴(kuò)容
        inflateTable(threshold);
        //把元素放入到HashMap中
        putAllForCreate(m);
}

size() key-value映射個(gè)數(shù)

public int size() {
        return size;
    }

isEmpty()是否為空

public boolean isEmpty() {
        return size == 0;
    }

get(Object key) 根據(jù)key獲取value

public V get(Object key) {
    //獲取key為null的
    getForNullKey();
    //獲取其他的key,利用hash值查找
    getEntry(Object key);
}

containsKey(Object key) 是否包含key

public boolean containsKey(Object key) {
        return getEntry(key) != null;
    }

put(K key, V value) 將指定的key value放入HashMap中,若已存在key,就替換舊值

public V put(K key, V value) {}

resize(int newCapacity) 重新設(shè)置大小

void resize(int newCapacity){}

transfer(Entry[] newTable, boolean rehash)現(xiàn)有的table放入新的table

void transfer(Entry[] newTable, boolean rehash) {}

putAll(Map<? extends K, ? extends V> m)

把指定的元素 全部放入HashMap中,已經(jīng)存在的key,會(huì)把舊value覆蓋掉

public void putAll(Map<? extends K, ? extends V> m) {}

remove(Object key) 根據(jù)key刪除

public V remove(Object key) {
    removeEntryForKey(key);
}

clear() 清空

public void clear(){}

containsValue(Object value) 是否包含value

public boolean containsValue(Object value) {}

clone() 淺拷貝

public Object clone() {}

static class Entry<K,V> implements Map.Entry<K,V> 內(nèi)部類

addEntry(int hash, K key, V value, int bucketIndex) 添加一個(gè)鍵值對(duì)

void addEntry(int hash, K key, V value, int bucketIndex) {}

createEntry(int hash, K key, V value, int bucketIndex) 添加一個(gè)鍵值對(duì)

void createEntry(int hash, K key, V value, int bucketIndex) {}

參考

http://www.cnblogs.com/chenpi/p/5280304.html

http://www.cnblogs.com/justany/archive/2013/02/01/2889335.html

http://tangyanbo.iteye.com/blog/1756536

http://www.importnew.com/7099.html

http://www.cnblogs.com/chenssy/p/3521565.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末致开,一起剝皮案震驚了整個(gè)濱河市峰锁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌双戳,老刑警劉巖虹蒋,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡魄衅,警方通過查閱死者的電腦和手機(jī)峭竣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來晃虫,“玉大人皆撩,你說我怎么就攤上這事≌芤” “怎么了扛吞?”我有些...
    開封第一講書人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長荆责。 經(jīng)常有香客問我滥比,道長,這世上最難降的妖魔是什么草巡? 我笑而不...
    開封第一講書人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任守呜,我火速辦了婚禮型酥,結(jié)果婚禮上山憨,老公的妹妹穿的比我還像新娘。我一直安慰自己弥喉,他們只是感情好郁竟,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著由境,像睡著了一般棚亩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上虏杰,一...
    開封第一講書人閱讀 51,562評(píng)論 1 305
  • 那天讥蟆,我揣著相機(jī)與錄音,去河邊找鬼纺阔。 笑死瘸彤,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的笛钝。 我是一名探鬼主播质况,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼玻靡!你這毒婦竟也來了结榄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤囤捻,失蹤者是張志新(化名)和其女友劉穎臼朗,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡视哑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年老厌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片黎炉。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡枝秤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出慷嗜,到底是詐尸還是另有隱情淀弹,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布庆械,位于F島的核電站薇溃,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏缭乘。R本人自食惡果不足惜沐序,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望堕绩。 院中可真熱鬧策幼,春花似錦、人聲如沸奴紧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽黍氮。三九已至唐含,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間沫浆,已是汗流浹背捷枯。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留专执,地道東北人淮捆。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像他炊,于是被迫代替她去往敵國和親争剿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 實(shí)際上痊末,HashSet 和 HashMap 之間有很多相似之處蚕苇,對(duì)于 HashSet 而言,系統(tǒng)采用 Hash 算...
    曹振華閱讀 2,513評(píng)論 1 37
  • 5.1凿叠、對(duì)于HashMap需要掌握以下幾點(diǎn) Map的創(chuàng)建:HashMap() 往Map中添加鍵值對(duì):即put(Ob...
    rochuan閱讀 678評(píng)論 0 0
  • 文|東東 我走過你的河流涩笤,踏破原本的平靜嚼吞,帶起一圈一圈的漣漪,記載著我和你往昔蹬碧,向著四周散去舱禽。踏上岸邊,回頭卻看不...
    郝東東閱讀 287評(píng)論 0 0
  • 晚上總是舍不得睡里伯,朋友圈刷到不再更新,微博內(nèi)容已經(jīng)無數(shù)重復(fù)渤闷,打開視頻軟件已經(jīng)不知道要看什么疾瓮,很晚了,凌晨了,睡吧。...
    徐徐如約閱讀 209評(píng)論 0 0
  • 這個(gè)故事講述了一個(gè)出生于書香門第的女孩浪默,追求自由,努力逾越男尊女卑的差距肩碟,與官兵斗智斗勇,為自己父母報(bào)仇雪恨...
    涼意閱讀 475評(píng)論 0 0