[JDK1.7源碼閱讀]ArrayList

來自mrcode:代碼有毒

從結(jié)構(gòu)來看


4個頂層接口來看至少有以下功能


1.Cloneable:

此類實現(xiàn)了 Cloneable 接口,以指示 Object.clone() 方法可以合法地對該類實例進(jìn)行按字段復(fù)制。

2.RandomAccess:

List 實現(xiàn)所使用的標(biāo)記接口,用來表明其支持快速(通常是固定時間)隨機訪問。此接口的主要目的是允許一般的算法更改其行為,從而在將其應(yīng)用到隨機或連續(xù)訪問列表時能提供良好的性能。

3.Serializable:

序列化接口沒有方法或字段,僅用于標(biāo)識可序列化的語義

4.Iterable:

實現(xiàn)這個接口允許對象成為 “foreach” 語句的目標(biāo)。

實現(xiàn)/繼承


1.AbstractList:

此類提供 List 接口的骨干實現(xiàn),以最大限度地減少實現(xiàn)“隨機訪問”數(shù)據(jù)存儲(如數(shù)組)支持的該接口所需的工作。對于連續(xù)的訪問數(shù)據(jù)(如鏈表),應(yīng)優(yōu)先使用 AbstractSequentialList,而不是此類。

2.List:

有序的 collection(也稱為序列)。此接口的用戶可以對列表中每個元素的插入位置進(jìn)行精確地控制。用戶可以根據(jù)元素的整數(shù)索引(在列表中的位置)訪問元素,并搜索列表中的元素。

3.AbstractCollection:

此類提供 Collection 接口的骨干實現(xiàn),以最大限度地減少了實現(xiàn)此接口所需的工作。

4.Collection:Collection

層次結(jié)構(gòu) 中的根接口。Collection 表示一組對象,這些對象也稱為 collection 的元素。一些 collection 允許有重復(fù)的元素,而另一些則不允許。一些 collection 是有序的,而另一些則是無序的。JDK 不提供此接口的任何直接 實現(xiàn):它提供更具體的子接口(如 Set 和 List)實現(xiàn)。此接口通常用來傳遞 collection,并在需要最大普遍性的地方操作這些 collection。

從功能閱讀-簡單函數(shù)開始

一般在使用中最常用的就是,數(shù)據(jù)的存、取、刪、循環(huán)遍歷,就從這幾個點分析源碼。
要使用就得先初始化對象實例。

構(gòu)造函數(shù)

 /**
 * Constructs an empty list with an initial capacity of ten.
 * 構(gòu)建一個初始化容量為10的空列表
 */
 public ArrayList() {
     this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
 }

 // 用指定容量創(chuàng)建列表
 public ArrayList(int initialCapacity) {
     super();
     if (initialCapacity < 0)
       throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
       this.elementData = new Object[initialCapacity];
 }

add - 將指定的元素添加到此列表的尾部。

 //把元素添加到元素末尾
 public boolean add(E e) {
     //確保列表的容量足夠
     ensureCapacityInternal(size + 1); // Increments modCount!!
     elementData[size++] = e;
     return true;
 }

add - 將指定的元素插入此列表中的指定位置。

總結(jié):

  • 下標(biāo)不能越界
  • 每次操作都需要移動元素的個數(shù):頻繁使用該方法將會影響性能
 // 將指定的元素插入次列表中的指定位置
 //將指定位置元素開始的下標(biāo)都往后+1(往后移動一個位置)
 public void add(int index, E element) {
     //檢查指定下標(biāo)是否越界
     rangeCheckForAdd(index);
     //調(diào)用核心函數(shù)1 確保足夠的容量
     ensureCapacityInternal(size + 1); // Increments modCount!!/
     // 移動元素
     System.arraycopy(elementData, index, elementData, index + 1, size - index);
     elementData[index] = element;
     size++;
 }

 /**
 * A version of rangeCheck used by add and addAll.
 * 檢查指定下標(biāo)是否越界,該方法供add 和 addAll使用
 */
 private void rangeCheckForAdd(int index) {
     if (index > size || index < 0)
       throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

核心函數(shù)1:確保足夠的列表容量

總結(jié):

  • 支持最大容量:Integer.MAX_VALUE(約21億條數(shù)據(jù))
  • 每次擴容都在原有基礎(chǔ)上增加一半的容量;所以適當(dāng)?shù)墓浪愠跏既萘渴呛苤匾模J(rèn)初始容量 10)
 //確保列表的容量足夠:1. 入口
 private void ensureCapacityInternal(int minCapacity) {
     if (elementData == EMPTY_ELEMENTDATA) {
       minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
     }
     ensureExplicitCapacity(minCapacity);
 }

 //1.1. 修改次數(shù)+1,是否擴容
 private void ensureExplicitCapacity(int minCapacity) {
     modCount++;
     // overflow-conscious code
     if (minCapacity - elementData.length > 0)
       grow(minCapacity);
 }

 /**
 * Increases the capacity to ensure that it can hold at least the
 * number of elements specified by the minimum capacity argument. *
 * @param minCapacity the desired minimum capacity
 * 擴容;至少能容納下minCapacity所需要的容量
 */
 private void grow(int minCapacity) {
     // overflow-conscious code
     int oldCapacity = elementData.length;
     // 新的容量= 以前容量 + 以前容量的一半
     int newCapacity = oldCapacity + (oldCapacity >> 1);
     //確保計算出來的新容量至少能裝得下所需要的容量
     if (newCapacity - minCapacity < 0)
         newCapacity = minCapacity;
     // 如果新的容量 比 內(nèi)置的最大容量限制還要大
     if (newCapacity - MAX_ARRAY_SIZE > 0)
         // 進(jìn)行重新計算確定新的容量:結(jié)果:最大Integer.MAX_VALUE
         // 所以這里的結(jié)果就是:一個list最多支持21億多條數(shù)據(jù),一般也不會用到這么大的,完全夠用
          newCapacity = hugeCapacity(minCapacity);
     // minCapacity is usually close to size, so this is a win:
     // 把原來的數(shù)據(jù)拷貝到新容量數(shù)組中去
     elementData = Arrays.copyOf(elementData, newCapacity);
 }

remove - 按索引移除元素

總結(jié):

  • 移除索引不能大于最大元素數(shù)量
  • 每次操作都需要對移除后的數(shù)據(jù)進(jìn)行移動操作:在刪除較多的場景里面使用該方法影響性能
 //按索引移除該元素,并返回被移除的元素值
 public E remove(int index) {
     // 檢查索引的有效性,索引大于size則數(shù)組越界
     rangeCheck(index);
     modCount++; //快速失敗思想,增加修改次數(shù)
     E oldValue = elementData(index);
     //計算需要移動的元素個數(shù)
     int numMoved = size - index - 1;
     if (numMoved > 0)
         //把源數(shù)組的刪除索引之后的元素都往前copy覆蓋
         System.arraycopy(elementData, index+1, elementData, index,numMoved);
     //置空最后一個元素,并把size減少1
     //這里置空元素,只是為了讓gc盡快的回收掉移除的元素,小技巧用得很對
     elementData[--size] = null; // clear to let GC do its work
     return oldValue;
 }

 // 檢查指定下標(biāo)是否在范圍內(nèi)
 private void rangeCheck(int index) {
     //這里可以看到只檢查了是否大于等于siez,如果為負(fù)數(shù)的話,則沒有檢查,那么在插入的時候Native Method會拋出數(shù)據(jù)越界的
     if (index >= size)
         throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
 }

remove - 移除指定的元素

總結(jié):

  • 按value刪除元素,只會刪除第一次出現(xiàn)的元素
  • 按value刪除元素,在內(nèi)部會進(jìn)行for循環(huán)匹配元素,并且要移動受影響的元素:所以對于刪除來說,list性能稍低
 // 移除指定的元素,如果這個元素存在的話,并且移除的是第一次出現(xiàn)的元素
 // 移除成功或則返回true,未搜索找元素則返回false
 public boolean remove(Object o) {
     //因為list支持 null 元素,所以對null進(jìn)行特殊處理
     if (o == null) {
         for (int index = 0; index < size; index++)
             if (elementData[index] == null) {
                 fastRemove(index);
                 return true;
             } 
     } else {
         //使用元素下標(biāo)進(jìn)行遍歷判斷
         for (int index = 0; index < size; index++)
              if (o.equals(elementData[index])) {
             fastRemove(index);
             return true;
         }
     }
     return false;
 }

 /*
 * Private remove method that skips bounds checking and does not
 * return the value removed.
 * 私有的刪除元素方法,不進(jìn)行邊界檢查和是否存在的檢查,直接按照指定的索引進(jìn)行刪除
 * (貌似又學(xué)到一個規(guī)則:公開的的方法必須校驗邊界規(guī)則什么的,私有的方法就不用了,方便公用 * 嗎?)
 * 該方法的代碼和public remove(index) 中的代碼部分主要刪除邏輯一致
 */
 private void fastRemove(int index) {
     modCount++;
     int numMoved = size - index - 1;
     if (numMoved > 0) 
     System.arraycopy(elementData, index+1, elementData, index,numMoved);
     elementData[--size] = null; // clear to let GC do its work
 }

set - 用指定的元素替代此列表中指定位置上的元素。

總結(jié):

  • 直接根據(jù)下標(biāo)替換掉原有元素
 //用指定的元素替代此列表中指定位置上的元素。
 public E set(int index, E element) {
     //在remove中也使用了該函數(shù),檢查 index>=siez
     rangeCheck(index);
     //記錄替換之前的元素
     E oldValue = elementData(index);
     //直接更新
     elementData[index] = element;
     return oldValue;
 }

 //獲得指定下標(biāo)的 元素
 @SuppressWarnings("unchecked")
 E elementData(int index) {
     return (E) elementData[index];
 }

get - 返回此列表中指定位置上的元素。

 public E get(int index) {
     //檢查下標(biāo)是否越界size
     rangeCheck(index);
     //直接通過下標(biāo)拿到了元素
     return elementData(index);
 }

Iterator - 遍歷

總結(jié)一

要讓一個對象擁有foreach的目標(biāo),則需要有以下兩個要求:
??1.目標(biāo)對象實現(xiàn) Iterable 接口
??2.實現(xiàn)Iterator,對數(shù)據(jù)進(jìn)行常用操作訪問實現(xiàn)接口的方法會返回一個 Iterator 類型的對象,所以得自己實現(xiàn)對自己數(shù)據(jù)結(jié)構(gòu)的訪問等操作

foreach 是調(diào)用了 Iterator 的方法實現(xiàn)的。
所以問題就來了

for(i=0;i<size;i++) 這種語法是不是和迭代器無關(guān)了,從這個看來,for(;;)只是對i進(jìn)行了
一個自增的操作,和具體的對象不是綁定狀態(tài),但是由于ArrayList能使用下標(biāo)訪問數(shù)據(jù),所以
就能多一種遍歷方式了

總結(jié)二

1.遍歷獲取元素 是通過下標(biāo)直接獲取
??2.但是刪除元素的話是委托了 原來的刪除方式,很原來的刪除原理一致,但是保證了當(dāng)前元素下標(biāo)的正確性,彌補了for(;;)刪除導(dǎo)致容器size變化和元素下標(biāo)位置發(fā)生變化 從而無法獲取正確的元素的 問題
??3.Iterator 可以使用while來遍歷

   // 返回一個在一組 T 類型的元素上進(jìn)行迭代的迭代器。
   // 這個跌迭代器是 快速失敗的,也就是會檢查modCount,發(fā)現(xiàn)不對就拋出異常
   public Iterator<E> iterator() {
       return new Itr();
   }
   /**
    * An optimized version of AbstractList.Itr
    * 對父類的優(yōu)化。- 這個抽象層次真好
    */
   private class Itr implements Iterator<E> {
       // 返回下一步元素的索引
       int cursor;       // index of next element to return
       //返回最后一次操作的元素索引,-1 表示沒有操作過元素,用于刪除操作使用
       int lastRet = -1; // index of last element returned; -1 if no such
       //快速失敗檢查,保留一個當(dāng)前迭代器所持有的數(shù)據(jù)版本;
       int expectedModCount = modCount;
       //是否有下一個元素
       public boolean hasNext() {
           // 當(dāng)前游標(biāo)不等于size的話,就表示還有下一個元素
           return cursor != size;
       }
       @SuppressWarnings("unchecked")
       public E next() {
           checkForComodification();
           int i = cursor; //在開頭獲取下一步的值,且以0開始,則i表示當(dāng)前操作的元素下標(biāo)
           if (i >= size) /當(dāng)前元素索引如果大于了size
               throw new NoSuchElementException();
           Object[] elementData = ArrayList.this.elementData;
           // 判斷 當(dāng)前下標(biāo)是否大于了數(shù)組的長度(什么情況下會出現(xiàn)此問題呢?)
           if (i >= elementData.length)
               throw new ConcurrentModificationException();
           cursor = i + 1; //記錄下一步要操作的元素
           return (E) elementData[lastRet = i];
       }
       public void remove() {
           // 如果還沒有操作過元素,比如,沒有調(diào)用next方法,就調(diào)用該方法就表示狀態(tài)不正確
           if (lastRet < 0)
               throw new IllegalStateException();
           checkForComodification(); //每次操作都需要檢查是否快速失敗
           try {
               //調(diào)用原來的移除方法進(jìn)行刪除元素,當(dāng)前也會使modCount次數(shù)增加
               ArrayList.this.remove(lastRet);
               cursor = lastRet; //因為刪除了元素,刪除下標(biāo)后面的元素都會往前移動
               lastRet = -1; //變成-1 狀態(tài),如果連續(xù)調(diào)用該方法則拋出異常(這里設(shè)計得真的好巧妙,保證了刪除操作)
               //更改當(dāng)前迭代器所持有的數(shù)據(jù)版本,否則就會導(dǎo)致快速失敗異常了
               expectedModCount = modCount;
           } catch (IndexOutOfBoundsException ex) {
               //對于數(shù)據(jù)越界操作,都定義為 當(dāng)方法檢測到對象的并發(fā)修改,但不允許這種修改時,拋出此異常
               throw new ConcurrentModificationException();
           }
       }
       //檢查當(dāng)前迭代器的版本是否與 容器列表中的數(shù)據(jù)版本是否一致,如果不一致,那么當(dāng)前迭代數(shù)據(jù)就會發(fā)生下標(biāo)越界等異常,所以需要拋出異常(比如在多線程中,或則在迭代中使用list.remove() 或則add等方法 都會導(dǎo)致拋出異常)
       final void checkForComodification() {
           if (modCount != expectedModCount)
               throw new ConcurrentModificationException();
       }
   }

contains - 如果包含指定的元素返回true

總結(jié):
??1.查找是否包含同樣是從頭到尾的循環(huán)遍歷匹配
??2.是否匹配 使用equals方法比對,所以自己可以覆蓋equals方法來查找引用型自定義對象

public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }
    // 在數(shù)組中循環(huán)遍歷查找指定的元素,如果存在則返回該元素下標(biāo),是返回首次出現(xiàn)的元素哦
    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                //使用的是equals方法
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

小結(jié)總結(jié)

其實從上面已經(jīng)就可以看出來了,list的一些常用總結(jié)特性:底層使用數(shù)組實現(xiàn)
優(yōu)點:
??1.隨機訪問比較快,因為直接通過下標(biāo)獲取元素
??2.覆蓋更新元素也比較快,也是直接通過下標(biāo)覆蓋

缺點
??1.在remove多的場景下性能稍微低下(針對其他的集合來說)
??2.在按value remove的情況下會循環(huán)遍歷整個數(shù)組,性能稍低
??3.隨機插入操作性能較低,因為需要把插入下標(biāo)后面所有的元素都移動位置
??4.當(dāng)前容量不夠的時候會觸發(fā)擴容操作,每次擴容都需要把源數(shù)據(jù)拷貝到新的擴容數(shù)組中去,不合理估算初始容量的話,性能較低

源碼分析的測試用列:
因為是工具類,直接對使用的方法進(jìn)行分析;

public class ArrayListStudyTest {
    private ArrayList<Integer> data;
    @Before
    public void createTestData(){
        data = new ArrayList<>(3);
        data.add(0);
        data.add(1);
        data.add(2);
    }
    @Test
    public void add(){
        data.add(1,2);
    }
    @Test
    public void remove(){
        data.remove(1);
        data.remove(Integer.valueOf(2));
    }
    @Test
    public void set(){
        data.set(-1,3);
    }
    @Test
    public void get(){
        Integer integer = data.get(0);
    }
    @Test
    public void foreach(){
        Iterator<Integer> iterator = data.iterator();
        while (iterator.hasNext()){
            Integer next = iterator.next();
        }
        for (Integer num : data) {
            System.out.println(num);
        }
    }
}

編寫自己的Arraylist

簡單編寫了下 基本功能的arraylist,編寫過程中發(fā)現(xiàn),要費不少時間,特別是你還要考慮到各種暴露方法調(diào)用會出現(xiàn)的bug,現(xiàn)在才感覺一個工具類真的可能要經(jīng)過多次迭代和認(rèn)真考慮各個使用環(huán)境下,才能編寫出好的工具類

/**
 * @author zhuqiang
 * @version V1.0
 * @date 2016/7/22 0022 9:44
 */
public class MyArrayList<E> implements Iterable<E>{
    private Object[] elementData; //底層實現(xiàn)用數(shù)組
    private final static Object[] EMPTY_ELEMENTDATA = {};
    private final static int DEFAULT_CAPACITY = 10; //默認(rèn)初始容量
    private int size; //容器大小
    private int modCount; //快速失敗 版本
    public MyArrayList() {
        elementData = EMPTY_ELEMENTDATA;
    }
    public MyArrayList(int initialCapacity) {
        if(initialCapacity < 0){
            throw new IllegalArgumentException("初始化容量不能小于0");
        }
        this.elementData = new Object[initialCapacity];
    }
    /**
     * 把元素添加到列表末尾
     * @param e
     * @return
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);
        elementData[size++] = e;
        return true;
    }
    // 確保底層容量能存儲數(shù)據(jù)
    private void ensureCapacityInternal(int minCapacity) {
        if(EMPTY_ELEMENTDATA == elementData){
            this.elementData = new Object[DEFAULT_CAPACITY];
        }
        if(minCapacity - this.elementData.length > 0){
            ensureCapacity(minCapacity);
        }
    }
    // 擴容操作
    private void ensureCapacity(int minCapacity) {
        int oldCapacity = this.elementData.length;
        int newCapacity = oldCapacity + (oldCapacity>>1); //新的容量計算
        elementData = Arrays.copyOf(elementData,newCapacity); //把原數(shù)據(jù)拷貝到新的容量數(shù)組中
    }
    /**
     * 移除指定下標(biāo)的元素
     * @param index
     * @return
     */
    public E remove(int index){
        E oldValue = elementData(index);
        //計算需要移動的元素個數(shù)
        int numMoved = size - index -1; // 減掉被移除的自己
        System.arraycopy(elementData,index+1,elementData,index,numMoved);
        elementData[--size] = null;
        return oldValue;
    }
    /**
     * 替換指定下標(biāo)的元素
     * @param index
     * @param element 返回被替換掉的元素的值
     * @return
     */
    public E set(int index, E element){
        E oldValue = elementData(index);
        this.elementData[index] = element;
        return oldValue;
    }
    public E get(int index){
        return elementData(index);
    }
    @SuppressWarnings("unchecked")
    E elementData(int index) {
        return (E) elementData[index];
    }
    public int size(){
        return size;
    }
    @Override
    public Iterator iterator() {
        return new Itr();
    }
    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;
        @Override
        public boolean hasNext() {
            return cursor != size;
        }
        @Override
        public E next() {
            int i = cursor;
            E e = MyArrayList.this.elementData(i);
            cursor++;
            return e;
        }
        @Override
        public void remove() {
        }
    }
    public static void main(String[] args) {
        MyArrayList<Integer> data = new MyArrayList<>();
        data.add(0);
        data.add(1);
        data.add(2);
        data.add(3);
        for (Integer i : data) {
            System.out.println(i);
        }
        System.out.println("==========");
        for (int i = 0; i < data.size(); i++) {
            System.out.println(data.get(i));
        }
        System.out.println("==========");
        data.remove(1);
        for (int i = 0; i < data.size(); i++) {
            System.out.println(data.get(i));
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,460評論 6 538
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,067評論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,467評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,468評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,184評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,582評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,616評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,794評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,343評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,096評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,291評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,863評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,513評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,941評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,190評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,026評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,253評論 2 375

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