jdk1.8新特性之Stream使用詳解

1、Stream簡(jiǎn)介

Stream作為Java 8的一大亮點(diǎn)留量,是對(duì)集合(Collection)、數(shù)組對(duì)象功能的增強(qiáng)哟冬,它專注于對(duì)集合對(duì)象進(jìn)行各種非常便利楼熄、高效的聚合操作,或者大批量數(shù)據(jù)操作浩峡。Stream API借助于同樣新出現(xiàn)的Lambda表達(dá)式孝赫,極大的提高編程效率和程序可讀性。同時(shí)它提供串行和并行兩種模式進(jìn)行匯聚操作红符,并發(fā)模式能夠充分利用多核處理器的優(yōu)勢(shì)青柄,使用fork/join并行方式來(lái)拆分任務(wù)和加速處理過(guò)程伐债。通常編寫并行代碼很難且容易出錯(cuò),但使用Stream API無(wú)需編寫一行多線程的代碼致开,就可以方便的寫出高性能的并發(fā)程序峰锁。所以說(shuō),Java 8中首次出現(xiàn)的java.util.stream是一個(gè)函數(shù)式語(yǔ)言+多核時(shí)代綜合影響的產(chǎn)物双戳。

Stream是一組用來(lái)處理數(shù)組虹蒋、集合的API

Java 8之所以費(fèi)這么大功夫引入函數(shù)式編程,原因有二:

  • 代碼簡(jiǎn)潔:函數(shù)式編程寫出的代碼簡(jiǎn)潔且意圖明確飒货,使用Stream接口讓你從此告別for循環(huán)魄衅。
  • 多核友好:Java函數(shù)式編程使得編寫并行程序從未如此簡(jiǎn)單,你需要的全部就是調(diào)用一個(gè)parallel()方法塘辅。

2晃虫、Stream特性

  • 不是數(shù)據(jù)結(jié)構(gòu),沒(méi)有內(nèi)部存儲(chǔ)
  • 不支持索引訪問(wèn)
  • 延遲計(jì)算
  • 支持并行
  • 很容易生成數(shù)組或集合(List扣墩,Set)
  • 支持過(guò)濾哲银,查找,轉(zhuǎn)換呻惕,匯總荆责,聚合等操作

3、Stream運(yùn)行機(jī)制

Stream分為源Source亚脆、中間操作做院、終止操作。

  • 流的源可以是一個(gè)數(shù)組濒持、一個(gè)集合键耕、一個(gè)生成器方法、一個(gè)I/O通道等等弥喉。
  • 一個(gè)流可以有零個(gè)或者多個(gè)中間操作郁竟,每一個(gè)中間操作都會(huì)返回一個(gè)新的流玛迄,供下一個(gè)操作使用由境。
  • 一個(gè)流只會(huì)有一個(gè)終止操作。
  • Stream只有遇到終止操作蓖议,它的源才開(kāi)始執(zhí)行遍歷操作虏杰。

4、Stream的創(chuàng)建

  • 通過(guò)數(shù)組
Arrays.stream(T array)
Stream.of(T array)
    /**
     * 方式一: 通過(guò)數(shù)組生成
     */
    @Test
    public void testBuildStream1() {
        String[] strArr = {"a", "b", "c"};
        //static <T> Stream<T> of(T... values)  返回其元素是指定值的順序排序流勒虾。
        Stream<String> stream1 = Stream.of(strArr);
        stream1.forEach(System.out::println);
        //static <T> Stream<T> of(T t) 返回包含單個(gè)元素的順序 Stream 纺阔。
        Stream<String> stream2 = Stream.of("d");
        stream2.forEach(System.out::println);
    
        Stream<String> stream3 = Arrays.stream(strArr);
        stream3.forEach(System.out::println);
    }
  • 通過(guò)集合
    Collection.stream()
    Collection.parallelStream():獲取的是并行操作的流
    /**
     * 方式二: 通過(guò)集合來(lái)生成
     */
    @Test
    public void testBuildStream2() {
        List<String> list = Arrays.asList("a", "b", "c", "d");
        Stream<String> stream1 = list.stream();
        stream1.forEach(System.out::println);
    
        // parallelStream(): 獲取的是并行操作的流
        Stream<String> stream2 = list.parallelStream();
        stream2.forEach(System.out::println);
    }
  • 通過(guò)Stream.generate方法來(lái)創(chuàng)建
    /**
     * 方式三: 通過(guò)Stream.generate來(lái)生成
     */
    @Test
    public void testBuildStream3() {
        // Stream.generate : 返回?zé)o限順序無(wú)序流,其中每個(gè)元素由提供的 Supplier 修然。 使用該方法的時(shí)候注意要結(jié)合limit來(lái)使用, 否則Stream中元素的個(gè)數(shù)是無(wú)限的
        Stream<Integer> generate = Stream.generate(() -> 1);
        generate.limit(10).forEach(System.out::println);
    }
  • 通過(guò)Stream.iterate方法來(lái)創(chuàng)建
    /**
     * 方式四: 通過(guò)Stream.iterate來(lái)生成
     */
    @Test
    public void testBuildStream4() {
        // Stream.iterate(T seed, UnaryOperator<T> f)  : 返回有序無(wú)限連續(xù) Stream由函數(shù)的迭代應(yīng)用產(chǎn)生 f至初始元素 seed 笛钝,產(chǎn)生 Stream包括 seed 质况, f(seed) , f(f(seed)) 玻靡,等
        Stream<Integer> iterate = Stream.iterate(1, x -> x + 1);
        iterate.limit(10).forEach(System.out::println);
    }
  • 其它API創(chuàng)建

需要注意的是结榄,對(duì)于基本數(shù)值型,目前有三種對(duì)應(yīng)的包裝類型 Stream:IntStream囤捻、LongStream臼朗、DoubleStream。當(dāng)然我們也可以用 Stream<Integer>蝎土、Stream<Long> >视哑、Stream<Double>,但是 boxing 和 unboxing 會(huì)很耗時(shí)誊涯,所以特別為這三種基本數(shù)值型提供了對(duì)應(yīng)的Stream挡毅。

Java 8 中還沒(méi)有提供其它數(shù)值型Stream,因?yàn)檫@將導(dǎo)致擴(kuò)增的內(nèi)容較多醋拧。而常規(guī)的數(shù)值型聚合運(yùn)算可以通過(guò)上面三種 Stream 進(jìn)行慷嗜。

    /**
     * 方式五: 其它方式創(chuàng)建Stream
     */
    @Test
    public void testBuildStream5() {
        String str = "abcdef";
        IntStream stream = str.chars();
        //stream.forEach(System.out::println);
        stream.forEach(x -> System.out.println((char) x));
    }

5、Stream常用API

5.1丹壕、中間操作

一個(gè)流可以后面跟隨零個(gè)或多個(gè) intermediate 操作庆械。其目的主要是打開(kāi)流,做出某種程度的數(shù)據(jù)映射/過(guò)濾菌赖,然后返回一個(gè)新的流缭乘,交給下一個(gè)操作使用。這類操作都是惰性化的(lazy)琉用,就是說(shuō)堕绩,僅僅調(diào)用到這類方法,并沒(méi)有真正開(kāi)始流的遍歷邑时。

過(guò)濾:filter

    @Test
    public void testFilter() {
        Arrays.asList(1, 2, 3, 4, 5, 6).stream().filter(x -> {
            System.out.println("filter里面的方法被執(zhí)行了");
            return x % 2 == 0;
        }).forEach(System.out::println);
    }

去重:distinct

    // Stream中間操作distinct 去除Stream中的重復(fù)元素 使用的是Object.equals(Object)
    @Test
    public void testDistinct() {
        Arrays.asList(1, 2, 2, 4, 4, 6).stream().distinct().forEach(System.out::println);
    }

排序:sorted

    // Stream中間操作sorted 對(duì)Stream中的元素進(jìn)行排序
    // sorted(): 按照自然順序排序
    // sorted(Comparator<? super T> comparator) : 按照指定的比較器進(jìn)行排序
    @Test
    public void testSorted() {
        Arrays.asList(6, 2, 5, 1, 4, 3).stream().sorted().forEach(System.out::print);
        System.out.println();
        Arrays.asList(6, 2, 5, 1, 4, 3).stream().sorted((a, b) -> a - b).forEach(System.out::print);
        System.out.println();
        Arrays.asList(6, 2, 5, 1, 4, 3).stream().sorted(Comparator.comparingInt(a -> a)).forEach(System.out::print);
        System.out.println();
        Arrays.asList(6, 2, 5, 1, 4, 3).stream().sorted(Integer::compare).forEach(System.out::print);
    }

合并流:concat

    // Stream中間操作concat 合并流
    // concat(Stream<? extends T> a, Stream<? extends T> b)
    // 創(chuàng)建一個(gè)懶惰連接的流奴紧,其元素是第一個(gè)流的所有元素,后跟第二個(gè)流的所有元素晶丘。
    @Test
    public void testConcat() {
        Stream.concat(Arrays.asList("a","b","c").stream(), Arrays.asList("d","e","f").stream()).forEach(System.out::println);
    }

截仁虻:limit、skip

    // Stream中間操作limit 截取
    // limit(long maxSize) : 返回由此流的元素組成的流浅浮,截短長(zhǎng)度不能超過(guò) maxSize 沫浆。
    @Test
    public void testLimit() {
        // 截取前2個(gè)原始
        Arrays.asList("a","b","c").stream().limit(2).forEach(System.out::println);
    }
    
    // Stream中間操作skip 截取
    // skip(long n) : 在丟棄流的第一個(gè) n元素后,返回由該流的 n元素組成的流滚秩。
    @Test
    public void testSkip() {
        // 跳過(guò)第一個(gè)原始
        Arrays.asList("a","b","c").stream().skip(1).forEach(System.out::println);
    }

轉(zhuǎn)換:map/flatMap/

    // Stream中間操作map 轉(zhuǎn)換
    // map(Function<? super T,? extends R> mapper)
    @Test
    public void testMap() {
        Arrays.asList("a","b","c").stream().map(x -> x.toUpperCase()).forEach(System.out::println);
    }
    
    // Stream中間操作flatMap 轉(zhuǎn)換
    // flatMap(Function<? super T,? extends Stream<? extends R>> mapper)
    // flatMap 把 input Stream 中的層級(jí)結(jié)構(gòu)扁平化专执,就是將最底層元素抽出來(lái)放到一起,最終 output 的新 Stream 里面已經(jīng)沒(méi)有 List 了郁油,都是直接的數(shù)字本股。
    @Test
    public void testFlatMap() {
        Stream<List<Integer>> inputStream = Stream.of(Arrays.asList(1, 2, 3), Arrays.asList(4), Arrays.asList(5, 6));
        Stream<Integer> stream = inputStream.flatMap(childList -> childList.stream());
        stream.forEach(System.out::println);
    }

其它:peek

    // Stream中間操作peek 查看
    @Test
    public void testPeek() {
        List<User> list = Arrays.asList(new User("zhangsan", 1), new User("lisi", 2));
        List<User> collect = list.stream().peek(x -> x.setName("lili")).collect(Collectors.toList());
        collect.stream().forEach(System.out::println);
    }

5.2攀痊、終止操作

一個(gè)流只能有一個(gè) terminal 操作,當(dāng)這個(gè)操作執(zhí)行后拄显,流就被使用“光”了蚕苇,無(wú)法再被操作。所以這必定是流的最后一個(gè)操作凿叠。Terminal 操作的執(zhí)行涩笤,才會(huì)真正開(kāi)始流的遍歷,并且會(huì)生成一個(gè)結(jié)果盒件。

循環(huán):forEach

計(jì)算:min蹬碧、max、count、average、sum

    // Stream終止操作min 求Stream中的最小值
    // min(Comparator<? super T> comparator): 根據(jù)提供的 Comparator返回此流的最小元素命雀。
    @Test
    public void testMin() {
        Optional<Integer> min = Arrays.asList(6, 2, 5, 1, 4, 3).stream().min((a, b) -> a - b);
        if (min.isPresent()) {
            System.out.println(min.get());
        }
    }
    
    // Stream終止操作max 求Stream中的最大值
    // max(Comparator<? super T> comparator) : 根據(jù)提供的 Comparator返回此流的最大元素。
    @Test
    public void testMax() {
        Optional<Integer> max = Arrays.asList(6, 2, 5, 1, 4, 3).stream().max((a, b) -> a - b);
        if (max.isPresent()) {
            System.out.println(max.get());
        }
    }
    
    // Stream終止操作count 求Stream中的元素個(gè)數(shù)
    // count() : 返回此流中的元素?cái)?shù)罗心。
    @Test
    public void testCount() {
        long count = Arrays.asList(6, 2, 5, 1, 4, 3).stream().count();
        System.out.println(count);
    }
    
    // IntStream終止操作average()  求Stream中的元素的算術(shù)平均值
    @Test
    public void testAverage() {
        OptionalDouble average = Arrays.asList("6", "2", "5", "1", "4", "3").stream().mapToInt(Integer::parseInt).average();
        if (average.isPresent()) {
            System.out.println(average.getAsDouble());
        }
    }
    
    // IntStream終止操作sum()  返回此流中元素的總和。
    @Test
    public void testSum() {
        int sum = Arrays.asList("6", "2", "5", "1", "4", "3").stream().mapToInt(Integer::parseInt).sum();
        System.out.println(sum);
    }

匹配:anyMatch城瞎、allMatch渤闷、noneMatch、findFirst脖镀、findAny

    // Stream終止操作allMatch
    // allMatch(Predicate<? super T> predicate) : 返回此流的所有元素是否與提供的謂詞匹配(所有的元素都滿足返回true, 否則返回false)飒箭。
    @Test
    public void testAllMatch() {
        boolean b = Arrays.asList(6, 2, 5, 1, 4, 3).stream().allMatch(x -> x > 0);
        System.out.println(b);
    }
    
    // Stream終止操作anyMatch
    // anyMatch(Predicate<? super T> predicate) : 返回此流的任何元素是否與提供的謂詞匹配(任何一個(gè)元素符合條件則返回true, 否則返回false)。
    @Test
    public void testAnyMatch() {
        boolean b = Arrays.asList(6, 2, 5, 1, 4, 3).stream().anyMatch(x -> x > 5);
        System.out.println(b);
    }
    
    // Stream終止操作noneMatch
    // noneMatch(Predicate<? super T> predicate) : 返回此流的元素是否與提供的謂詞匹配蜒灰。 (所有元素都不匹配則返回true, 否則返回false)弦蹂。
    @Test
    public void testNoneMatch() {
        boolean b = Arrays.asList(6, 2, 5, 1, 4, 3).stream().noneMatch(x -> x > 5);
        System.out.println(b);
    }
    
    // Stream終止操作findFirst
    // findFirst()  : 返回描述此流的第一個(gè)元素的Optional如果流為空,則返回一個(gè)空的Optional 强窖。
    @Test
    public void testFindFirst() {
        Optional<Integer> first = Arrays.asList(6, 2, 5, 1, 4, 3).stream().findFirst();
        if (first.isPresent()) {
            System.out.println(first.get());
        }
    }
    
    // Stream終止操作findAny
    // findAny() : 返回描述流的一些元素的Optional如果流為空凸椿,則返回一個(gè)空的Optional 。
    @Test
    public void testFindAny() {
        Optional<Integer> any = Arrays.asList(6, 2, 5, 1, 4, 3).stream().findAny();
        if (any.isPresent()) {
            System.out.println(any.get());
        }
    }

匯聚:reduce

這個(gè)方法的主要作用是把 Stream 元素組合起來(lái)翅溺。它提供一個(gè)起始值(種子)脑漫,然后依照運(yùn)算規(guī)則(BinaryOperator),和前面 Stream 的第一個(gè)未巫、第二個(gè)窿撬、第 n 個(gè)元素組合启昧。從這個(gè)意義上說(shuō)叙凡,字符串拼接、數(shù)值的 sum密末、min握爷、max跛璧、average 都是特殊的 reduce。例如 Stream 的 sum 就相當(dāng)于Integer sum = integers.reduce(0, (a, b) -> a+b); 或Integer sum = integers.reduce(0, Integer::sum);

也有沒(méi)有起始值的情況新啼,這時(shí)會(huì)把Stream的前面兩個(gè)元素組合起來(lái)追城,返回的是 Optional。

    // Stream終止操作reduce
    @Test
    public void testReduce() {
        // 字符串連接燥撞,concat = "ABCD"
        String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat);
        System.out.println(concat);
        // 求最小值座柱,minValue = -3.0
        double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
        System.out.println(minValue);
        // 求和,sumValue = 10, 有起始值
        int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);
        System.out.println(sumValue);
        // 求和物舒,sumValue = 10, 無(wú)起始值
        sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
        System.out.println(sumValue);
        // 過(guò)濾色洞,字符串連接,concat = "ace"
        concat = Stream.of("a", "B", "c", "D", "e", "F").filter(x -> x.compareTo("Z") > 0).reduce("", String::concat);
        System.out.println(concat);
    }

上面代碼例如第一個(gè)示例的 reduce()冠胯,第一個(gè)參數(shù)(空白字符)即為起始值火诸,第二個(gè)參數(shù)(String::concat)為 BinaryOperator。這類有起始值的 reduce() 都返回具體的對(duì)象荠察。而對(duì)于第四個(gè)示例沒(méi)有起始值的 reduce()置蜀,由于可能沒(méi)有足夠的元素,返回的是 Optional悉盆,請(qǐng)留意這個(gè)區(qū)別盯荤。

收集器:toArray、collect

    // Stream終止操作toArray焕盟、collect
    @Test
    public void testToArrayAndCollect() {
        List<Integer> collect = Arrays.asList(6, 2, 5, 1, 4, 3).stream().collect(Collectors.toList());
        System.out.println(collect);
    
        Object[] objects = Arrays.asList(6, 2, 5, 1, 4, 3).stream().toArray();
        Arrays.stream(objects).forEach(System.out::println);
    }

在對(duì)于一個(gè)Stream進(jìn)行多次轉(zhuǎn)換操作 (Intermediate 操作)廷雅,每次都對(duì)Stream的每個(gè)元素進(jìn)行轉(zhuǎn)換,而且是執(zhí)行多次京髓,這樣時(shí)間復(fù)雜度就是N(轉(zhuǎn)換次數(shù))個(gè)for循環(huán)里把所有操作都做掉的總和嗎航缀?其實(shí)不是這樣的,轉(zhuǎn)換操作都是lazy的堰怨,多個(gè)轉(zhuǎn)換操作只會(huì)在 Terminal 操作的時(shí)候融合起來(lái)芥玉,一次循環(huán)完成。我們可以這樣簡(jiǎn)單的理解备图,Stream 里有個(gè)操作函數(shù)的集合灿巧,每次轉(zhuǎn)換操作就是把轉(zhuǎn)換函數(shù)放入這個(gè)集合中,在 Terminal 操作的時(shí)候循環(huán) Stream 對(duì)應(yīng)的集合揽涮,然后對(duì)每個(gè)元素執(zhí)行所有的函數(shù)抠藕。

還有一種操作被稱為short-circuiting。用以指:

對(duì)于一個(gè)intermediate操作蒋困,如果它接受的是一個(gè)無(wú)限大(infinite/unbounded)的Stream盾似,但返回一個(gè)有限的新Stream。

對(duì)于一個(gè) terminal 操作雪标,如果它接受的是一個(gè)無(wú)限大的 Stream零院,但能在有限的時(shí)間計(jì)算出結(jié)果溉跃。

當(dāng)操作一個(gè)無(wú)限大的Stream,而又希望在有限時(shí)間內(nèi)完成操作告抄,則在管道內(nèi)擁有一個(gè) short-circuiting 操作是必要非充分條件撰茎。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市打洼,隨后出現(xiàn)的幾起案子龄糊,更是在濱河造成了極大的恐慌,老刑警劉巖募疮,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绎签,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡酝锅,警方通過(guò)查閱死者的電腦和手機(jī)诡必,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)搔扁,“玉大人爸舒,你說(shuō)我怎么就攤上這事「宥祝” “怎么了扭勉?”我有些...
    開(kāi)封第一講書人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)苛聘。 經(jīng)常有香客問(wèn)我涂炎,道長(zhǎng),這世上最難降的妖魔是什么设哗? 我笑而不...
    開(kāi)封第一講書人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任唱捣,我火速辦了婚禮,結(jié)果婚禮上网梢,老公的妹妹穿的比我還像新娘震缭。我一直安慰自己,他們只是感情好战虏,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布拣宰。 她就那樣靜靜地躺著,像睡著了一般烦感。 火紅的嫁衣襯著肌膚如雪巡社。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,764評(píng)論 1 290
  • 那天手趣,我揣著相機(jī)與錄音晌该,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛气笙,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播怯晕,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼潜圃,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了舟茶?” 一聲冷哼從身側(cè)響起谭期,我...
    開(kāi)封第一講書人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吧凉,沒(méi)想到半個(gè)月后隧出,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡阀捅,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年胀瞪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片饲鄙。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡凄诞,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出忍级,到底是詐尸還是另有隱情帆谍,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布轴咱,位于F島的核電站汛蝙,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏朴肺。R本人自食惡果不足惜窖剑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望戈稿。 院中可真熱鬧苛吱,春花似錦、人聲如沸器瘪。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)橡疼。三九已至援所,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間欣除,已是汗流浹背住拭。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人滔岳。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓杠娱,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親谱煤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子摊求,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

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