HashMap一文通

1.什么是HashMap

  • 基于哈希表的Map接口的非同步實現(xiàn)
  • 此實現(xiàn)提供所有可選的映射操作,并允許使用null值和null鍵
  • 此類不保證映射順序
  • 此實現(xiàn)假定哈希函數(shù)將元素適當分布在各桶之間趴荸,為讀取操作提供穩(wěn)定性能
  • 迭代時間與實例容量(桶的數(shù)量)及其大小(鍵-值映射關(guān)系數(shù))成正比
  • 本版本為JDK1.7, 此鏈接為JDK1.8版本(加班趕點中)

2.HashMap的數(shù)據(jù)結(jié)構(gòu)

  • 類定義
public class HashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable
  • 重要全局變量 - 鏈表散列的數(shù)據(jù)結(jié)構(gòu)(數(shù)組+鏈表【沖突解決方案-封閉尋址方法】)
備注:建議直接看英文注釋齐遵,更加清晰明了
//The default initial capacity - MUST be a power of two.
static final int DEFAULT_INITIAL_CAPACITY = 16;
//The maximum capacity - MUST be a power of two <= 1<<30.
static final int MAXIMUM_CAPACITY = 1 << 30;
//The load factor used when none specified in constructor.
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//The table, resized as necessary. Length MUST Always be a power of two.
transient Entry<K,V>[] table;
//The number of key-value mappings contained in this map.
transient int size;
//The next size value at which to resize (capacity * load factor).
int threshold;
//The load factor for the hash table.
final float loadFactor;
/**
  * The number of times this HashMap has been structurally modified
  * Structural modifications are those that change the number of mappings in
  * the HashMap or otherwise modify its internal structure (e.g.,
  * rehash).  This field is used to make iterators on Collection-views of
  * the HashMap fail-fast.  (See ConcurrentModificationException).
  */
transient int modCount;
  • 構(gòu)造器
public HashMap(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal load factor: " + loadFactor);
    // Find a power of 2 >= initialCapacity
        int capacity = 1;
        while (capacity < initialCapacity)
            capacity <<= 1;
        this.loadFactor = loadFactor;
        //閾值為容量*負載因子和最大容量+1之間的最小值 以此值作為容量翻倍的依據(jù)(不能超過最大容量)
        threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
        //初始化一個2次冪的Entry類型數(shù)組 一個桶對應(yīng)一個Entry對象
        table = new Entry[capacity];
        useAltHashing = sun.misc.VM.isBooted() && 
        (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
        init();
}
  • 數(shù)組內(nèi)元素的鏈表結(jié)構(gòu)
//靜態(tài)類 默認實現(xiàn)內(nèi)部Entry接口 (接口中可定義內(nèi)部接口-Map.Entry接口為Map的內(nèi)部接口)
//PS:JDK8中引入default歌粥,作用為在接口中定義默認方法實現(xiàn)
static class Entry<K,V> implements Map.Entry<K,V> {
    final K key;//key具有引用不可變特性
    V value;
    Entry<K,V> next;//next指向下一個:單向鏈表陨溅,頭插入
    final int hash;
    ……
}

3.HashMap的存儲

  • put方法解析
  • @return key不存在返回null针余,否則返回舊值
public V put(K key, V value) {
    //其允許存放null的key和null的value
    //當其key為null時脚祟,調(diào)用putForNullKey方法谬以,放入到table[0]的這個位置(null鍵只有一個)
    if (key == null)
        return putForNullKey(value);
    //通過調(diào)用hash方法對key進行哈希,得到哈希之后的數(shù)值
    //其目的是為了盡可能的讓鍵值對可以分不到不同的桶中
    int hash = hash(key);
    //根據(jù)上一步驟中求出的hash得到在數(shù)組中是索引i
    int i = indexFor(hash, table.length);
    //如果i處的Entry不為null由桌,則通過其next指針不斷遍歷e元素的下一個元素为黎。
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;//使用臨時變量k主要用于e.key的賦值邮丰,意義有限
        //hash一致 && (key引用相同 或 key字符串比較相同)
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            //值變更
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;//已存在則選擇直接返回舊值
        }
    }
    modCount++;
    addEntry(hash, key, value, i);//新增
    return null;//若key不存在則返回null
}
  • hash方法解析
//JDK1.7
final int hash(Object k) {
    int h = 0;
    if (useAltHashing) {
        if (k instanceof String) {
            return sun.misc.Hashing.stringHash32((String) k);
        }
        h = hashSeed;
    }
    //異或就是兩個數(shù)的二進制形式,按位對比铭乾,相同取0剪廉,不同取一
    //此算法加入了高位計算,防止低位不變炕檩,高位變化時妈经,造成的 hash 沖突
    h ^= k.hashCode();
    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}
//JDK1.8 擾動函數(shù) -> 散列值優(yōu)化函數(shù)
static final int hash(Object key) {
    int h;
    //把一個數(shù)右移16位即丟棄低16為,就是任何小于2^16的數(shù)捧书,右移16后結(jié)果都為0
    //2的16次方再右移剛好就是1 同時int最大值為32位
    //任何一個數(shù)吹泡,與0按位異或的結(jié)果都是這個數(shù)本身
    //為indexFor做準備
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
  • indexFor方法解析
//@Param h 根據(jù)hash方法得到h
//@Param length 一定是2次冪
//2進制32位帶符號的int表值范圍從-2147483648到2147483648,加起來大概40億空間,內(nèi)存不能直接讀取
//用之前還要先做對數(shù)組的長度取模運算经瓷,得到的余數(shù)才能用來訪問數(shù)組下標
static int indexFor(int h, int length) {
    //2次冪-1 返回的結(jié)果的二進制為永遠是都是1 比如 15 -> 1111 (16 -> 10000)
    //與運算 只有 1 & 1 = 1 正好相當于一個“低位掩碼”
    //如果length-1中某一位為0爆哑,則不論h中對應(yīng)位的數(shù)字為幾,對應(yīng)位結(jié)果都是0舆吮,這樣就讓兩個h取到同一個結(jié)果揭朝,hash沖突
    //同時這個操作可以保證索引不會大于數(shù)組的大小(見開頭的描述)
    return h & (length-1);
}
  • addEntry方法解析
//該方法為包訪問 package java.util(本包私有性高于子類)
void addEntry(int hash, K key, V value, int bucketIndex) {
    //當前容量超過閾值 && 當前坐標數(shù)組非空
    //有個優(yōu)雅的設(shè)計在于,若bucketIndex處沒有Entry對象色冀,那么新添加的entry對象指向null潭袱,從而就不會有鏈了
    if ((size >= threshold) && (null != table[bucketIndex])) {
        resize(2 * table.length);//容量擴容一倍
        hash = (null != key) ? hash(key) : 0;//hash重新計算
        bucketIndex = indexFor(hash, table.length);//index重新計算
    }
    createEntry(hash, key, value, bucketIndex);//新增Entry元素到數(shù)組的制定下標位置
}
//該方法為包訪問 package java.util
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++;
}
  • 關(guān)于indexFor和hash方法的進一步解讀
  • hashCode返回的-2147483648到2147483648的int值,加起來大概40億的映射空間与学。只要哈是函數(shù)映射比較均勻松散彤悔,一般很難出現(xiàn)碰撞key.hashCode()
  • 但問題是40億長度數(shù)組,內(nèi)存放不小索守,該散列值不能直接拿來用晕窑。用之前必須先對數(shù)組長度取模運算,得到的余數(shù)才能來訪問數(shù)組下標indexFor()
  • 長度取2的整次冪卵佛,而length-1時正好相當于一個低位掩碼杨赤。與操作的結(jié)果就是散列的高位全部歸零,只保留低位值截汪,用作下標訪問
     10100101 11000100 00100101
  & 00000000 00000000 00001111
     00000000 00000000 00000101    //高位全部歸零疾牲,只保留末四位
  • 但問題是,無論散列值在松散挫鸽,但只取最后幾位说敏,碰撞也很嚴重。更要命的是如果散列本身做的不好丢郊,分布上成等差數(shù)列盔沫,就會出現(xiàn)規(guī)律性重復(fù)
  • 擾動函數(shù)生效:
    擾動函數(shù)

    右位移16位(32位一半)医咨,讓高半?yún)^(qū)和低半?yún)^(qū)做異或,目的是混合原始哈希碼的高位和低位架诞,以此來加大低位的隨機性拟淮。而且混合后的低位包含高位的部分特征,這樣高位的信息也變相保留下來谴忧。
  • 當長度非2次冪(最后一位永遠是0)很泊,進行與運算(只有都為1得1,否則為0)沾谓,會造成最后一位永遠是0委造,那最后一位就無法使用,導致(1)空間的巨大浪費均驶。同時可使用的位置比原數(shù)組長度小很多昏兆,(2)進一步增加了碰撞的幾率。
  • 歸納
  • 存儲時妇穴,根據(jù)hash算法決定其在數(shù)組中的存儲位置爬虱,再根據(jù)equals方法決定其在該數(shù)組位置上的鏈表中的存儲位置;同時HashMap會根據(jù)當前bucket的占用情況自動調(diào)整容量(超過Load Facotr則resize為原來的2倍)
  • 讀取時,根據(jù)hash算法找到其在數(shù)組中的存儲位置腾它,再根據(jù)equals方法從該位置上的鏈表中取出該Entry跑筝。
    在產(chǎn)生碰撞的情況下,進行g(shù)et時瞒滴,兩步的時間復(fù)雜度是O(1)+O(n)曲梗。1.8使用紅黑樹(O(1)+O(logn))

4.HashMap的Resize

  • 性能參數(shù)
  • initialCapacity 初始容量 默認16
  • loadFactor(負載因子) : 衡量的是一個散列表的空間的使用程度,負載因子越大表示散列表的裝填程度越高逛腿,反之愈小稀并。對于使用鏈表法的散列表來說,查找一個元素的平均時間是 O(1+a)单默,因此如果負載因子越大,對空間的利用更充分忘瓦,然而后果是查找效率的降低搁廓;如果負載因子太小,那么散列表的數(shù)據(jù)將過于稀疏耕皮,對空間造成嚴重浪費境蜕。默認的的負載因子 0.75是對空間和時間效率的一個平衡選擇凌停。當容量超出此最大容量時, resize后的HashMap 容量是容量的兩倍
  • resize方法解析
/**
  * Rehashes the contents of this map into a new array with a
  * larger capacity.  This method is called automatically when the
  * number of keys in this map reaches its threshold.
  */
//目的:通過增加內(nèi)部數(shù)組的長度的方式罚拟,從而保證鏈表中只保留很少的Entry對象完箩,從而降低put(),remove()和get()方法的執(zhí)行時間
//注意:如果兩個Entry對象的鍵的哈希值不一樣,但它們之前在同一個桶上拉队,那么在調(diào)整以后弊知,并不能保證它們依然在同一個桶上
void resize(int newCapacity) {
    Entry[] oldTable = table;//使用臨時拷貝粱快,保證當前數(shù)據(jù)時效性(參見JAVA的`觀察者`模式實現(xiàn))
    int oldCapacity = oldTable.length;
    if (oldCapacity == MAXIMUM_CAPACITY) {
        threshold = Integer.MAX_VALUE;
        return;
    }
    //實例化一個newCapacity容量的新數(shù)組
    Entry[] newTable = new Entry[newCapacity];
    boolean oldAltHashing = useAltHashing;
    useAltHashing |= sun.misc.VM.isBooted() &&
            (newCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
    boolean rehash = oldAltHashing ^ useAltHashing;
    transfer(newTable, rehash);//遍歷舊數(shù)組對新數(shù)組賦值
    table = newTable;//引用替換
    threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);//重新計算閾值
}
  • transfer方法解析
/**
  * Transfers all entries from current table to newTable.
  */
void transfer(Entry[] newTable, boolean rehash) {
    int newCapacity = newTable.length;
    //注意:如果在新表的數(shù)組索引位置相同,則鏈表元素會倒置事哭,也就是先插入最近的
    for (Entry<K,V> e : table) {
        while(null != e) {
            Entry<K,V> next = e.next;
            if (rehash) {
                e.hash = null == e.key ? 0 : hash(e.key);//重新計算hash
            }
            int i = indexFor(e.hash, newCapacity);
            //注意:多線程環(huán)境可能由于執(zhí)行次序非有序造成next引用變更賦值出錯導致環(huán)形鏈接出現(xiàn)漫雷,從而造成死循環(huán)
            e.next = newTable[i];
            newTable[i] = e;
            e = next;
        }
    }
}

5.Fail-Fast機制

  • 錯誤機制
  • 當使用迭代器的過程中有其他線程修改了map,將拋出ConcurrentModificationException
  • 源碼實現(xiàn)
transient int modCount;//修改計數(shù) put鳍咱、remove或clear時mount++ clear時清空
HashIterator() {
    expectedModCount = modCount;
    if (size > 0) {
    Entry[] t = table;
    while (index < t.length && (next = t[index++]) == null)  
        ;
    }
}
final Entry<K,V> nextEntry() {
    //期望變更數(shù)量不匹配
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
  • HashMap的remove方法實現(xiàn)
public V remove(Object key) {
    Entry<K,V> e = removeEntryForKey(key);
    return (e == null ? null : e.value);
}
  • HashMap.KeySet的remove方法實現(xiàn)
public boolean remove(Object o) {
    return HashMap.this.removeEntryForKey(o) != null;
}
  • HashMap.HashIterator的remove方法實現(xiàn)
public void remove() {
    if (current == null)
        throw new IllegalStateException();
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
    Object k = current.key;
    current = null;
    HashMap.this.removeEntryForKey(k);
    //迭代器中刪除時同步了expectedModCount值與modCount相同
    expectedModCount = modCount;
}
  • remove方法解析
/**
  * Removes and returns the entry associated with the specified key
  * in the HashMap.  Returns null if the HashMap contains no mapping
  * for this key.
  */
final Entry<K,V> removeEntryForKey(Object key) {
    int hash = (key == null) ? 0 : hash(key);
    int i = indexFor(hash, table.length);
    Entry<K,V> prev = table[i];//用于記錄該key的前一個元素(默認先從隊首開始)
    Entry<K,V> e = prev;//從隊首開始往隊尾遍歷
    //遍歷key所在鏈表
    while (e != null) {
        Entry<K,V> next = e.next;
        Object k;
        if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) {
            modCount++;//remove屬于結(jié)構(gòu)性改造流炕,modCount計數(shù)+1
            size--;//當前Map的有效元素數(shù)量-1
            if (prev == e)
                table[i] = next;//若當前key正好位于隊首澎现,則隊首指向next
            else 
                prev.next = next;//若當前key不位于隊首每辟,則該key之前的元素的next指向該key的下一個元素
            e.recordRemoval(this);//鉤子方法
            return e;
        }
        //繼續(xù)往隊尾找
        prev = e;//指向當前循環(huán)元素的上一個元素
        e = next;//指向下一次循環(huán)元素
    }
    return e;
}
  • 迭代推薦方式
Map map = new HashMap();
Iterator iter = map.entrySet().iterator();
while (iter.hasNext()) {
 Map.Entry entry = (Map.Entry) iter.next();
 Object key = entry.getKey();
 Object val = entry.getValue();
}

6.常見面試題

1.什么時候會使用HashMap?他有什么特點渠欺?

  • 基于Map接口實現(xiàn)的Key-Value容器,允許null值挠将,同時非有序,非同步舔稀。

2.你知道HashMap的工作原理嗎?

  • <i class="icon-exclamation-sign">參見歸納 </i>
  • 在Java 8中产园,如果一個bucket中碰撞沖突的元素超過某個限制(默認是8),則使用紅黑樹來替換鏈表什燕,從而提高速度

3.你知道get和put的原理嗎竞端?equals()和hashCode()的都有什么作用屎即?

  • 通過對key的hashCode()進行hashing,并計算下標( n-1 & hash)技俐,從而獲得buckets的位置。如果產(chǎn)生碰撞暂刘,則利用key.equals()方法去鏈表或樹中去查找對應(yīng)的節(jié)點

4.你知道hash的實現(xiàn)嗎?為什么要這樣實現(xiàn)谣拣?

  • 在Java 1.8的實現(xiàn)中族展,是通過hashCode()的高16位異或低16位實現(xiàn)的:(h = k.hashCode()) ^ (h >>> 16)森缠,主要是從速度、功效仪缸、質(zhì)量來考慮的贵涵,這么做可以在bucket的n比較小的時候,也能保證考慮到高低bit都參與到hash的計算中恰画,同時不會有太大的開銷宾茂。
  • 使用hash還有一個好處就是 盡可能確保每個鏈表中的長度一致

5. 如果HashMap的大小超過了負載因子(load factor)定義的容量,怎么辦拴还?

  • 如果超過了負載因子(默認0.75)跨晴,則會重新resize一個原來長度兩倍的HashMap,并且重新調(diào)用hash方法片林;同時此時很可能出現(xiàn)一系列問題:<i class="icon-exclamation-sign">參見問題6 </i>

6. 你了解重新調(diào)整HashMap大小存在什么問題嗎?

  • 1.當數(shù)據(jù)過多時焕妙,很可能出現(xiàn)性能瓶頸(包括rehash時間)
    <i class="icon-female"> 使用HashMap時一定保證數(shù)量有限</i>
    2.多線程情況下可能產(chǎn)生條件競競爭從而造成死循環(huán)(具體表現(xiàn)在CPU接近100%)弓摘。多線程同時試著調(diào)整大小焚鹊,可能導致存儲在鏈表中的元素的次序顛倒衣盾,因為移動到新的bucket位置的時候,HashMap并不會將元素放在鏈表的尾部,而是放在頭部蓝撇,這是為了避免尾部遍歷。具體死循環(huán)代碼參見transfer(newTable)
    <i class="icon-female">多線程環(huán)境下推薦使用ConcurrentHashMap </i>

7. 為什么String, Interger這樣的wrapper類適合作為鍵虽抄?

  • 1.class具有final屬性,同時重寫equals()和hashCode()
    2.hashCode變動會導致讀取失效
    3.final同時保證線程安全
    <i class="icon-female">對象推薦重寫equals和hashCode方法迈窟,主要用于Map存取時的對比,同時有利于減少碰撞 </i>

8.我們可以使用自定義的對象作為鍵嗎曲稼?

  • 這是前一個問題的延伸。當然你可能使用任何對象作為鍵贫悄,只要它遵守了equals()和hashCode()方法的定義規(guī)則娘摔,并且當對象插入到Map中之后將不會再改變了。如果這個自定義對象時不可變的凳寺,那么它已經(jīng)滿足了作為鍵的條件,因為當它創(chuàng)建之后就已經(jīng)不能改變了逆趋。

9.如何對HashMap進行排序怜瞒?

  • 轉(zhuǎn)換:Map -> Set -> LinkedList(存key)
  • 排序:LinkedList自行sort
  • 存儲:存入有序LinkedHashMap

10.HashMap的remove陷阱?

  • 通過Iterator方式可正確遍歷完成remove操作
  • 直接調(diào)用list的remove方法就會拋異常

10.為什么只允許通過iterator進行remove操作吴汪?

  • HashMap和keySet的remove方法都可以通過傳遞key參數(shù)刪除任意的元素
  • 而iterator只能刪除當前元素(current),一旦刪除的元素是iterator對象中next所正在引用的杆融,如果沒有通過modCount霜运、 expectedModCount的比較實現(xiàn)快速失敗拋出異常脾歇,下次循環(huán)該元素將成為current指向淘捡,此時iterator就遍歷了一個已移除的過期數(shù)據(jù)
  • 之所以推薦迭代器remove的根本原因在于只有迭代器的remove方法中實現(xiàn)了變更時于modCount的同步工作
    expectedModCount = modCount;

10.如果是遍歷過程中增加或修改數(shù)據(jù)呢?

  • 增加或修改數(shù)據(jù)只能通過Map的put方法實現(xiàn)激况,在遍歷過程中修改數(shù)據(jù)可以,但如果增加新key就會在下次循環(huán)時拋異常乌逐,因為在添加新key時modCount也會自增(迭代器只實現(xiàn)了remove方法也是原因之一)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末浙踢,一起剝皮案震驚了整個濱河市绢慢,隨后出現(xiàn)的幾起案子洛波,更是在濱河造成了極大的恐慌,老刑警劉巖思瘟,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件闻伶,死亡現(xiàn)場離奇詭異,居然都是意外死亡蓝翰,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門诞帐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來爆雹,“玉大人,你說我怎么就攤上這事钙态。” “怎么了蚓挤?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵驻子,是天一觀的道長。 經(jīng)常有香客問我崇呵,道長,這世上最難降的妖魔是什么鞭执? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任芒粹,我火速辦了婚禮,結(jié)果婚禮上化漆,老公的妹妹穿的比我還像新娘。我一直安慰自己座云,他們只是感情好,可當我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布圃阳。 她就那樣靜靜地躺著璧帝,像睡著了一般。 火紅的嫁衣襯著肌膚如雪睬隶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天银萍,我揣著相機與錄音恤左,去河邊找鬼。 笑死飞袋,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的授嘀。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼览闰,長吁一口氣:“原來是場噩夢啊……” “哼巷折!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起锻拘,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎婉宰,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體心包,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年痕惋,在試婚紗的時候發(fā)現(xiàn)自己被綠了娃殖。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡堕虹,死狀恐怖叶洞,靈堂內(nèi)的尸體忽然破棺而出鲫凶,到底是詐尸還是另有隱情衩辟,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布昼钻,位于F島的核電站封寞,受9級特大地震影響然评,放射性物質(zhì)發(fā)生泄漏狈究。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一亿眠、第九天 我趴在偏房一處隱蔽的房頂上張望磅废。 院中可真熱鬧,春花似錦拯勉、人聲如沸憔购。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽山卦。三九已至,卻和暖如春账蓉,著一層夾襖步出監(jiān)牢的瞬間逾一,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工箱玷, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留陌宿,地道東北人。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓壳坪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親沐批。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,779評論 2 354

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

  • 一、基本數(shù)據(jù)類型 注釋 單行注釋:// 區(qū)域注釋:/* */ 文檔注釋:/** */ 數(shù)值 對于byte類型而言...
    龍貓小爺閱讀 4,261評論 0 16
  • 實際上宪拥,HashSet 和 HashMap 之間有很多相似之處,對于 HashSet 而言江解,系統(tǒng)采用 Hash 算...
    曹振華閱讀 2,513評論 1 37
  • 5.1徙歼、對于HashMap需要掌握以下幾點 Map的創(chuàng)建:HashMap() 往Map中添加鍵值對:即put(Ob...
    rochuan閱讀 678評論 0 0
  • 巷子里的人還在憨憨大睡 我們只路過青石階上歪脖子阿婆的眼 老樹迎著春天樂此不疲 誰偷穿了孫兒的綠袍衫 陽光撒下一地...
    斑鳩y閱讀 328評論 0 1
  • 朋友說,乖乖女都喜歡壞壞的男生桨螺。可能每一個乖乖女心里灭翔,都有一顆向往自由的心吧。 高中是我人生的一大轉(zhuǎn)折肝箱,偏離了我原...
    咸魚有夢y閱讀 204評論 0 0