精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

小小 Stream,一篇文章拿捏它

開發 前端
在之前的 Java 中的 Lambda 文章中,我簡要提到了 Stream 的使用。在這篇文章中將深入探討它。

在之前的 Java 中的 Lambda文章中,我簡要提到了 Stream 的使用。在這篇文章中將深入探討它。首先,我們以一個熟悉的Student類為例。假設有一組學生:

public class Student {
    private String name;
    private Integer age;

    public Student(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public Integer getAge() {
        return age;
    }
    // toString 方法
    @Override
    public String toString() {
        return"Student{" + "name='" + name + '\'' + ", age=" + age + '}';
    }
}
List<Student> students = new ArrayList<>();
students.add(new Student("Bob", 18));
students.add(new Student("Ted", 17));
students.add(new Student("Zeka", 19));

現在有這樣一個需求:從給定的學生列表中返回年齡大于等于 18 歲的學生,按年齡降序排列,最多返回 2 個。

在Java7 及更早的代碼中,我們會這樣實現:

public static List<Student> getTwoOldestStudents(List<Student> students) { 
    List<Student> result = new ArrayList<>(); 
    // 1. 遍歷學生列表,篩選出符合年齡條件的學生
    for (Student student : students) { 
        if (student.getAge() >= 18) { 
            result.add(student); 
        } 
    } 
    // 2. 對符合條件的學生按年齡排序
    result.sort((s1, s2) -> s2.getAge() - s1.getAge()); 
    // 3. 如果結果大于 2 個,截取前兩個數據并返回
    if (result.size() > 2) { 
        result = result.subList(0, 2); 
    } 
    return result; 
}

在Java8 及以后的版本中,借助 Stream,我們可以更優雅地寫出以下代碼:

public static List<Student> getTwoOldestStudentsByStream(List<Student> students) {
    return students.stream()
        .filter(s -> s.getAge() >= 18)
        .sorted((s1, s2) -> s2.getAge() - s1.getAge())
        .limit(2)
        .collect(Collectors.toList());
}

兩種方法的區別:

  • 從功能角度來看,過程式代碼實現將集合元素、循環迭代和各種邏輯判斷耦合在一起,暴露了太多細節。隨著需求的變化和復雜化,過程式代碼將變得難以理解和維護。
  • 函數式解決方案將代碼細節和業務邏輯解耦。類似于 SQL 語句,它表達的是“做什么”而不是“怎么做”,讓程序員更專注于業務邏輯,寫出更簡潔、易理解和維護的代碼。

基于我日常項目的實踐經驗,我對 Stream 的核心點、易混淆的用法、典型使用場景等做了詳細總結。希望能幫助大家更全面地理解 Stream,并在項目開發中更高效地應用它。

一、初識 Stream

Java 8 新增了 Stream 特性,它使用戶能夠以函數式且更簡單的方式操作 List、Collection 等數據結構,并在用戶無感知的情況下實現并行計算。

簡而言之,Stream 操作被組合成一個 Stream 管道。Stream 管道由以下三部分組成:

  • 創建 Stream(從源數據創建,源數據可以是數組、集合、生成器函數、I/O 通道等);
  • 中間操作(可能有零個或多個,它們將一個 Stream 轉換為另一個 Stream,例如filter(Predicate));
  • 終止操作(產生結果從而終止 Stream,例如count()或forEach(Consumer))。

下圖展示了這些過程:

每個階段里的 Stream 操作都包含多個方法。我們先來簡單了解下每個方法的功能。

1. 創建 Stream

主要負責直接創建一個新的 Stream,或基于現有的數組、List、Set、Map 等集合類型對象創建新的 Stream。

API

解釋

stream()

創建一個新的串行流對象

parallelStream()

創建一個可以并行執行的流對象

Stream.of()

從給定的元素序列創建一個新的串行流對象

除了Stream,還有IntStream、LongStream和DoubleStream等基本類型的流,它們都稱為“流”。

2. 中間操作

這一步負責處理 Stream 并返回一個新的 Stream 對象。中間操作可以疊加。

API

解釋

filter()

過濾符合條件的元素并返回一個新的流

sorted()

按指定規則對所有元素排序并返回一個新的流

skip()

跳過集合前面的指定數量的元素并返回一個新的流

distinct()

去重并返回一個新的流

limit()

只保留集合前面的指定數量的元素并返回一個新的流

concat()

將兩個流的數據合并為一個新的流并返回

peek()

遍歷并處理流中的每個元素并返回處理后的流

map()

將現有元素轉換為另一種對象類型(一對一)并返回一個新的流

flatMap()

將現有元素轉換為另一種對象類型(一對多),即一個原始元素對象可能轉換為一個或多個新類型的元素,然后返回一個新的流

3. 終止操作

顧名思義,終止操作后 Stream 將結束,最后可能會執行一些邏輯處理,或根據需求返回一些執行結果。

API

解釋

findFirst()

找到第一個符合條件的元素時終止流處理

findAny()

找到任意一個符合條件的元素時終止流處理

anyMatch()

返回布爾值,類似于isContains(),用于判斷是否有符合條件的元素

allMatch()

返回布爾值,用于判斷是否所有元素都符合條件

noneMatch()

返回布爾值,用于判斷是否所有元素都不符合條件

min()

返回流處理后的最小值

max()

返回流處理后的最大值

count()

返回流處理后的元素數量

collect()

將流轉換為指定類型,通過Collectors指定

toArray()

將流轉換為數組

iterator()

將流轉換為迭代器對象

forEach()

無返回值,遍歷元素并執行給定的處理邏輯

二、代碼實戰

1. 創建 Stream

// Stream.of, IntStream.of...
Stream<String> nameStream = Stream.of("Bob", "Ted", "Zeka");
IntStream ageStream = IntStream.of(18, 17, 19);

// stream, parallelStream
Stream<Student> studentStream = students.stream();
Stream<Student> studentParallelStream = students.parallelStream();

在大多數情況下,我們基于現有的集合創建 Stream。

2. 中間操作

(1) map

map和flatMap都用于將現有元素轉換為其他類型。區別在于:

  • map必須是一對一的,即每個元素只能轉換為一個新元素;
  • flatMap可以是一對多的,即每個元素可以轉換為一個或多個新元素。

我們先來看map方法。當前需求如下:將之前的學生對象列表轉換為學生姓名列表并輸出:

public static List<String> objectToString(List<Student> students) {
    return students.stream()
        .map(Student::getName)
        .collect(Collectors.toList());
}

輸出:

[Bob, Ted, Zeka]

可以看到,輸入中有三個學生,輸出也是三個學生姓名。

(2) flatMap

學校要求每個學生加入一個團隊。假設 Bob、Ted 和 Zeka 加入了籃球隊,Alan、Anne 和 Davis 加入了足球隊。

public class Team {
    private String type;
    private List<Student> students;

    public Team(String type, List<Student> students) {
        this.type = type;
        this.students = students;
    }

    public String getType() {
        return type;
    }

    public List<Student> getStudents() {
        return students;
    }
@Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("Team{");
        sb.append("type='").append(type).append('\'');
        sb.append(", students=[");
        for (int i = 0; i < students.size(); i++) {
            Student student = students.get(i);
            sb.append("{name='").append(student.getName()).append("', age=").append(student.getAge()).append('}');
            if (i < students.size() - 1) {
                sb.append(", ");
            }
        }
        sb.append("]}");
        return sb.toString();
    }
}
List<Student> basketballStudents = new ArrayList<>();
basketballStudents.add(new Student("Bob", 18));
basketballStudents.add(new Student("Ted", 17));
basketballStudents.add(new Student("Zeka", 19));

List<Student> footballStudents = new ArrayList<>();
footballStudents.add(new Student("Alan", 19));
footballStudents.add(new Student("Anne", 21));
footballStudents.add(new Student("Davis", 21));

Team basketballTeam = new Team("basketball", basketballStudents);
Team footballTeam = new Team("football", footballStudents);

List<Team> teams = new ArrayList<>();
teams.add(basketballTeam);
teams.add(footballTeam);

現在我們需要統計所有團隊中的學生,并將他們合并到一個列表中。你會如何實現這個需求?

在 Java7 及更早的版本中可以通過以下方式解決:

List<Student> allStudents = new ArrayList<>();
for (Team team : teams) {
    for (Student student : team.getStudents()) {
        allStudents.add(student);
    }
}

但這段代碼有兩個嵌套的 for 循環,不夠優雅。面對這個需求,flatMap可以派上用場。

List<Student> allStudents = teams.stream()
    .flatMap(t -> t.getStudents().stream())
    .collect(Collectors.toList());

一行代碼就搞定了。flatMap方法接受一個 lambda 表達式函數,函數的返回值必須是一個 Stream 類型。flatMap方法最終會將所有返回的 Stream 合并生成一個新的 Stream,而map方法無法做到。

下圖清晰地展示了flatMap的處理邏輯:

(3) filter, distinct, sorted, limit

關于剛才所有團隊中的學生列表,我們現在需要知道這些學生中第二和第三大的年齡。他們必須至少 18 歲。此外,如果有重復的年齡,只能算一個。

List<Integer> topTwoAges = allStudents.stream()
    .map(Student::getAge) // [18, 17, 19, 19, 21, 21]
    .filter(a -> a >= 18) // [18, 19, 19, 21, 21]
    .distinct() // [18, 19, 21]
    .sorted((a1, a2) -> a2 - a1) // [21, 19, 18]
    .skip(1) // [19, 18]
    .limit(2) // [19, 18]
    .collect(Collectors.toList());
System.out.println(topTwoAges);

輸出:

[19, 18]

注意:由于在skip方法操作后只剩下兩個元素,limit步驟實際上可以省略。

(4) peek, foreach

peek方法和foreach方法都可以用于遍歷元素并逐個處理,因此我們將它們放在一起進行比較和講解。但值得注意的是,peek是一個中間操作方法,而foreach是一個終止操作方法。

中間操作只能作為 Stream 管道中間的處理步驟,不能直接執行以獲取結果,必須與終止操作配合執行。而foreach作為一個沒有返回值的終止方法,可以直接執行相應的操作。

比如,我們分別使用peek和foreach對籃球隊的每個學生說“Hello, xxx…”。

// peek
System.out.println("---start peek---");
basketballTeam.getStudents().stream().peek(s -> System.out.println("Hello, " + s.getName()));
System.out.println("---end peek---");

// foreach
System.out.println("---start foreach---");
basketballTeam.getStudents().stream().forEach(s -> System.out.println("Hello, " + s.getName()));
System.out.println("---end foreach---");

從輸出中可以看出,peek在單獨調用時不會執行,而foreach可以直接執行:

---start peek---
---end peek---
---start foreach---
Hello, Bob
Hello, Ted
Hello, Zeka
---end foreach---

如果在peek后面加上終止操作,它就可以執行。

System.out.println("---start peek---");
basketballTeam.getStudents().stream().peek(s -> System.out.println("Hello, " + s.getName())).count();
System.out.println("---end peek---");

// 輸出
---start peek---
Hello, Bob
Hello, Ted
Hello, Zeka
---end peek---

peek應謹慎用于業務處理邏輯。因為peek方法是否執行在各個版本并不一致。

例如,在 Java8 版本中,剛才的peek方法會正常執行,但在 Java17 中,它會被自動優化,peek中的邏輯不會執行。至于原因,你可以查看 JDK17 的官方 API 文檔。

三、終止操作

根據終止操作返回的結果類型大概分為兩類。

一類返回的是簡單類型,主要包括max、min、count、findAny、findFirst、anyMatch、allMatch等方法。

另一類是返回的是集合類型。大多數場景是獲取集合類的結果對象,如 List、Set 或 HashMap 等,主要通過collect方法實現。

1. 簡單結果類型

(1) max, min

max()和min()主要用于返回流處理后元素的最大值/最小值。返回結果由Optional包裝。關于Optional的使用,請參考之前的Java 中如何優雅地處理 null 值文章 。

我們直接看例子:

找到足球隊中年齡最大和最小的是誰?

// max
footballTeam.getStudents().stream()
    .map(Student::getAge)
    .max(Comparator.comparing(a -> a))
    .ifPresent(a -> System.out.println("足球隊中最大的年齡是:" + a));

// min
footballTeam.getStudents().stream()
    .map(Student::getAge)
    .min(Comparator.comparing(a -> a))
    .ifPresent(a -> System.out.println("足球隊中最小的年齡是:" + a));

輸出:

足球隊中最大的年齡是:21
足球隊中最小的年齡是:19

(2) findAny, findFirst

findAny()和findFirst()主要用于在找到符合條件的元素。對于串行 Stream,findAny()和findFirst()功能相同;對于并行 Stream,findAny()更高效。

假設籃球隊新增了一個學生 Tom,年齡為 19 歲。

List<Student> basketballStudents = new ArrayList<>();
basketballStudents.add(new Student("Bob", 18));
basketballStudents.add(new Student("Ted", 17));
basketballStudents.add(new Student("Zeka", 19));
basketballStudents.add(new Student("Tom", 19));

現在需要查找到:

  • 籃球隊中第一個年齡為 19 歲的學生姓名;
  • 籃球隊中任意一個年齡為 19 歲的學生姓名。
// findFirst
basketballStudents.stream()
    .filter(s -> s.getAge() == 19)
    .findFirst()
    .map(Student::getName)
    .ifPresent(name -> System.out.println("findFirst: " + name));

// findAny
basketballStudents.stream()
    .filter(s -> s.getAge() == 19)
    .findAny()
    .map(Student::getName)
    .ifPresent(name -> System.out.println("findAny: " + name));

輸出:

findFirst: Zeka
findAny: Zeka

可以看到,在串行 Stream 下,這兩個功能沒有區別。并行處理的區別將在后面介紹。

(3) count

籃球隊新增了一個學生,現在籃球隊有多少學生?

System.out.println("籃球隊的學生人數:" + basketballStudents.stream().count());

輸出:

籃球隊的學生人數:4

(4) anyMatch, allMatch, noneMatch

顧名思義,這三個方法用于判斷元素是否符合條件,并返回布爾值。看以下三個例子:

  • 足球隊中是否有名為 Alan 的學生?
  • 足球隊中的所有學生是否都小于 22 歲?
  • 足球隊中是否沒有年齡超過 20 歲的學生?
// anyMatch
System.out.println("anyMatch: " + footballStudents.stream().anyMatch(s -> s.getName().equals("Alan")));

// allMatch
System.out.println("allMatch: " + footballStudents.stream().allMatch(s -> s.getAge() < 22));

// noneMatch
System.out.println("noneMatch: " + footballStudents.stream().noneMatch(s -> s.getAge() > 20));

輸出:

anyMatch: true
allMatch: true
noneMatch: false

2. 結果集合類型

(1) 生成集合

生成集合應該是collect最常用的場景。除了之前提到的 List,還可以生成 Set、Map 等,如下:

// 獲取籃球隊中學生年齡的分布,不允許重復
Set<Integer> ageSet = basketballStudents.stream()
    .map(Student::getAge)
    .collect(Collectors.toSet());
System.out.println("set: " + ageSet);

// 獲取籃球隊中所有學生的姓名和年齡的 Map
Map<String, Integer> nameAndAgeMap = basketballStudents.stream()
    .collect(Collectors.toMap(Student::getName, Student::getAge));
System.out.println("map: " + nameAndAgeMap);

輸出:

set: [17, 18, 19]
map: {Ted=17, Tom=19, Bob=18, Zeka=19}

(2) 生成字符串

除了生成集合,collect還可以用于拼接字符串。

例如,我們獲取籃球隊中所有學生的姓名后,希望用“,”將所有姓名拼接成一個字符串并返回。

System.out.println(basketballStudents.stream()
    .map(Student::getName)
    .collect(Collectors.joining(",")));

輸出:

Bob,Ted,Zeka,Tom

也許你會說,用String.join()不也能實現這個功能嗎?確實,如果只是單純的字符串拼接,確實沒有必要使用Stream來實現。畢竟,殺雞焉用牛刀!

此外,Collectors.joining()還支持定義前綴和后綴,功能更強大。

System.out.println(basketballStudents.stream()
    .map(Student::getName)
    .collect(Collectors.joining(",", "(", ")")));

輸出:

(Bob,Ted,Zeka)

(3) 生成統計結果

還有一個在實際中可能很少用到的場景,就是使用collect生成數字數據的統計結果。我們簡單看一下。

// 計算平均年齡
System.out.println("平均年齡:" + basketballStudents.stream()
    .map(Student::getAge)
    .collect(Collectors.averagingInt(a -> a)));

// 統計匯總
IntSummaryStatistics summary = basketballStudents.stream()
    .map(Student::getAge)
    .collect(Collectors.summarizingInt(a -> a));
System.out.println("summary: " + summary);

在上面的例子中,使用collect對年齡進行了一些數學運算,結果如下:

平均年齡:18.0
summary: IntSummaryStatistics{count=3, sum=54, min=17, average=18.000000, max=19}

四、并行 Stream

使用并行流可以有效利用計算機性能,提高執行速度。并行 Stream 將整個流分成多個片段,然后并行處理每個片段的流,最后將每個片段的執行結果匯總成一個完整的 Stream。

如下圖所示,篩選出大于等于 18 的數字:

將原始任務拆分為多個任務。

[7, 18, 18]

每個任務并行執行操作。

stream.filter(a -> a >= 18)

單個任務處理并匯總為單個結果。

[18, 18]

高效使用 findAny()

如上所述,findAny()在并行 Stream 中更高效,從 API 文檔中可以看出,每次執行該方法的結果可能不同。

使用parallelStream執行findAny()10 次,以找出任何滿足條件(名字是 Bob、Tom 或 Zeka)的學生名字。

for (int i = 0; i < 10; i++) {
    basketballStudents.parallelStream()
        .filter(s -> s.getAge() >= 18)
        .findAny()
        .map(Student::getName)
        .ifPresent(name -> System.out.println("并行流中的 findAny: " + name));
}

輸出:

并行流中的findAny: Zeka
并行流中的findAny: Zeka
并行流中的findAny: Tom
并行流中的findAny: Zeka
并行流中的findAny: Zeka
并行流中的findAny: Bob
并行流中的findAny: Zeka
并行流中的findAny: Zeka
并行流中的findAny: Zeka

這個輸出證實了findAny()的不穩定性。

關于并行流的更多知識,我將在后續文章中進一步分析和討論。

五、注意事項

1. 延遲執行

Stream 是惰性的;只有在啟動終止操作時才會對源數據執行計算,并且只在需要時才會消耗源元素。前面提到的peek方法就是一個很好的例子。

2. 避免執行兩次終止操作

一旦 Stream 被終止,就不能再用于執行其他操作,否則會報錯。看下面的例子:

Stream<Student> stream = students.stream();
stream.filter(s -> s.getAge() >= 18).count();
stream.filter(s -> s.getAge() >= 18).forEach(System.out::println); // 這里會報錯

輸出:

java.lang.IllegalStateException: stream has already been operated upon or closed

因為一旦 Stream 被終止,就不能再重復使用。

責任編輯:趙寧寧 來源: 程序猿技術充電站
相關推薦

2020-10-09 08:15:11

JsBridge

2018-09-26 16:04:04

NVMe主機控制器

2021-04-09 08:40:51

網絡保險網絡安全網絡風險

2022-02-21 09:44:45

Git開源分布式

2021-06-30 00:20:12

Hangfire.NET平臺

2019-04-17 15:16:00

Sparkshuffle算法

2024-06-25 08:18:55

2023-05-12 08:19:12

Netty程序框架

2017-09-05 08:52:37

Git程序員命令

2021-05-15 09:18:04

Python進程

2021-07-01 10:01:16

JavaLinkedList集合

2020-10-22 08:25:22

JavaScript運作原理

2021-02-02 18:39:05

JavaScript

2018-10-22 12:50:20

CDN網絡內容發布網絡

2021-01-29 18:41:16

JavaScript函數語法

2013-04-15 10:59:08

iOS開發ARC版本說明

2022-12-14 08:03:27

CSS變量前端

2021-06-04 09:56:01

JavaScript 前端switch

2019-01-09 10:04:16

2020-02-28 11:29:00

ElasticSear概念類比
點贊
收藏

51CTO技術棧公眾號

国产精品美女xx| 亚洲天堂第一页| 日韩在线视频在线| 成人免费视频国产| 国产精品日韩| 国产亚洲视频在线| 国产在线观看中文字幕| 爱看av在线入口| 久久色在线视频| 国产日韩欧美日韩| 四虎永久在线精品| 精品一区不卡| 欧美成人国产一区二区| 久久精品99国产| 成人免费视屏| 久久精品视频在线看| 亚洲精品日韩av| 免费的毛片视频| 欧美激情四色| 日韩电影大全免费观看2023年上| 国产一级特黄a大片免费| 污污视频在线| 国产精品丝袜一区| 国产乱码精品一区二区三区卡| 波多野结衣网站| 今天的高清视频免费播放成人| 亚洲一级黄色av| 久久久久久久无码| 国产日韩在线观看视频| 一本久道中文字幕精品亚洲嫩| 性欧美18一19内谢| 欧美老女人性开放| 国产91高潮流白浆在线麻豆| 国产欧美精品一区二区三区介绍| 国产一区二区99| 激情婷婷久久| 久久精品2019中文字幕| 人妻aⅴ无码一区二区三区 | 日韩av免费看| 青娱乐国产在线| 日韩国产一区| 亚洲午夜精品久久久久久性色| 黄色国产在线视频| 国产视频网站一区二区三区| 欧美日韩亚洲综合在线| 成人在线观看a| 不卡福利视频| 天天亚洲美女在线视频| 成人免费网站入口| 污影院在线观看| 亚洲乱码国产乱码精品精98午夜| 色涩成人影视在线播放| 九九九伊在人线综合| aaa国产一区| 激情小说综合网| 日本美女一级片| 成人午夜视频免费看| 国产99在线播放| 超碰在线播放97| 国产成人精品www牛牛影视| 亚洲japanese制服美女| 国产精品一区二区av白丝下载 | 久久久久久女乱国产| 99国产精品视频免费观看| 国产精品一区二区欧美黑人喷潮水| 国产精品久久免费| 国产精品一区二区视频| 91亚洲精品一区二区| 91久久国语露脸精品国产高跟| 久久精品999| 成人午夜黄色影院| 国产福利资源在线| 国产成人av一区二区| wwwxx欧美| 五月婷婷深深爱| 久久免费视频色| 亚洲综合五月天| 在线看一级片| 偷拍亚洲欧洲综合| 老司机午夜av| 4438五月综合| 欧美精品一区二区三区蜜桃视频| 捆绑裸体绳奴bdsm亚洲| 久久av免费| 北条麻妃久久精品| 久草成人在线视频| 亚洲一区二区三区高清不卡| 国产精品久久久久久久久免费看| 一区二区三区免费在线视频| 国产成人免费在线观看不卡| 精品日本一区二区| 国内三级在线观看| 国产精品国产三级国产aⅴ入口 | av影院在线免费观看| 色婷婷av一区二区三区大白胸 | 色先锋资源久久综合5566| 中文字幕美女视频| 影音先锋在线一区| 国产精品成人v| 亚洲国产一二三区| 国产亚洲欧美在线| 黑人巨茎大战欧美白妇| www.成人影院| 日韩一区二区三区电影在线观看 | 熟妇女人妻丰满少妇中文字幕| 99久久免费精品国产72精品九九| 亚洲日韩中文字幕在线播放| 国产精品三区在线观看| 亚洲视频1区| 成人妇女免费播放久久久| 亚洲三级中文字幕| 亚洲猫色日本管| 老熟妇仑乱视频一区二区| 日韩在线观看一区二区三区| 在线观看欧美视频| 日韩免费黄色片| 国产一区二区久久| 日韩精品欧美专区| 91高清视频在线观看| 欧美精品第1页| 天堂久久精品忘忧草| 在线日韩电影| 亚洲一区二区三区成人在线视频精品| 欧美少妇另类| 亚洲成人激情av| 亚洲一区二区三区四区精品| 欧美日韩在线二区| 青青久久av北条麻妃海外网| 理论片中文字幕| 亚洲男人的天堂在线观看| 在线观看免费成人av| 欧亚精品一区| 欧美激情视频在线| 国产绳艺sm调教室论坛| 国产精品美女久久久久久 | 天堂中文在线播放| 欧美va亚洲va| 四虎永久免费在线| 麻豆国产一区二区| 色999五月色| 日韩电影免费观| 日韩大片免费观看视频播放| 日本三级片在线观看| 国产成人精品综合在线观看 | 丝袜美腿亚洲一区二区图片| 极品尤物一区二区三区| 国产99re66在线视频| 欧美哺乳videos| 久久精品一区二区三| 国产福利91精品| 激情视频小说图片| 免费观看亚洲天堂| 色综合久久中文字幕综合网小说| 国产深喉视频一区二区| 亚洲精品视频免费观看| 五月天婷婷在线观看视频| 亚洲精品午夜av福利久久蜜桃| 成人精品久久久| 国产1区在线| 欧美一级夜夜爽| 国产极品国产极品| 大尺度一区二区| 老太脱裤子让老头玩xxxxx| 国产精品videossex| 久久久噜噜噜久久久| 天天综合天天综合| 欧美性精品220| 亚洲黄色免费视频| 久久精品二区亚洲w码| 红桃一区二区三区| 高清精品视频| 青青草精品毛片| 日p在线观看| 欧美xingq一区二区| 国产福利拍拍拍| 久久久91精品国产一区二区精品 | 久久久久久影视| 日韩视频免费在线播放| 欧美超碰在线| 国产精品一区二区三区观看 | 91麻豆国产精品| 日韩影视在线| 亚洲男人7777| 国产孕妇孕交大片孕| 亚洲国产精品综合小说图片区| 日本一区二区三区网站| 美女免费视频一区| 欧美中日韩在线| 你懂的一区二区三区| 成人午夜在线观看| 成人观看网址| 在线观看精品国产视频| 亚洲精品久久久久久无码色欲四季 | 噜噜噜噜噜久久久久久91| 日韩欧美2区| 久久久久久久久久久91| 国产免费av高清在线| 正在播放一区二区| 好看的av在线| 亚洲免费观看高清在线观看| 粉嫩av蜜桃av蜜臀av| 国产一区二区三区不卡在线观看| 国产视频九色蝌蚪| 图片区亚洲欧美小说区| 久久久久久九九九九| 大胆国模一区二区三区| 91精品国产色综合| 91蜜桃在线视频| 国产一区二区激情| 蜜臀av中文字幕| 欧美日韩mp4| 日韩手机在线视频| 一区二区三区四区不卡视频| 日本不卡一区视频| 91麻豆国产在线观看| 久久艹这里只有精品| 日韩1区2区日韩1区2区| 国产手机免费视频| 91高清一区| 亚洲激情一区二区三区| 爽爽窝窝午夜精品一区二区| 福利视频久久| 亚洲在线资源| 国产精品视频免费在线| 欧美电影免费观看高清完整| 久久久久久18| 色呦呦在线免费观看| 精品国产一区二区三区四区在线观看 | 国产精品麻豆一区二区| 日本一卡二卡在线| 国产福利一区二区三区| 极品粉嫩美女露脸啪啪| 日本不卡中文字幕| 波多野结衣乳巨码无在线| 欧美疯狂party性派对| 日韩美女一区| 欧美人与物videos另类xxxxx| 国产日产精品一区二区三区四区| 看亚洲a级一级毛片| 91精品国产综合久久香蕉922| 亚洲a∨精品一区二区三区导航| 国产91精品黑色丝袜高跟鞋| 99色在线观看| 韩国欧美亚洲国产| a'aaa级片在线观看| 久久免费在线观看| 爱看av在线入口| 久久久亚洲国产| 国产经典三级在线| 久久久噜噜噜久久中文字免| 成人黄色动漫| 青青在线视频一区二区三区| jk漫画禁漫成人入口| 国产成人欧美在线观看| 四虎影视4hu4虎成人| 国产精品揄拍500视频| 欧美97人人模人人爽人人喊视频| 国产精品亚洲视频在线观看| 日韩久久99| 91久久精品www人人做人人爽| 日本在线一区二区三区| 高清视频一区二区三区| 国产精品自在| 久久手机视频| 成人毛片免费看| 久久久一二三四| 亚洲一级特黄| 久久久久久久久久久久久国产精品 | 亚洲欧美激情插| 久久国产在线视频| 欧美日韩国产一中文字不卡 | 日韩在线卡一卡二| 视频在线观看免费高清| 国产一区二区不卡老阿姨| 色悠悠在线视频| 久久综合九色欧美综合狠狠| 日韩福利在线视频| 亚洲免费av高清| 日韩精品手机在线| 欧美日韩美少妇| 成人午夜免费福利| 亚洲美女性生活视频| 日韩在线观看www| 欧美激情精品久久久久久黑人| 美女露胸视频在线观看| 国产精品国内视频| 日本一区二区三区播放| 欧美日韩日本网| 在线精品视频在线观看高清| 免费看的黄色大片| 精品在线视频一区| 日本黄色免费观看| 国产精品二区一区二区aⅴ污介绍| 久久久综合久久| 日本久久一区二区三区| 国产白浆在线观看| 国产午夜精品视频免费不卡69堂| 99久久精品免费观看国产| 2019国产精品自在线拍国产不卡| 日日夜夜综合| 久久99精品久久久久子伦| 999视频精品| www黄色av| 粉嫩av一区二区三区粉嫩| 69精品无码成人久久久久久| 亚洲第一综合色| 国产精品久久欧美久久一区| 亚洲欧洲激情在线| 日韩三级电影视频| 国产精品美女视频网站| 高清精品视频| 18视频在线观看娇喘| 日韩电影免费一区| 亚洲中文字幕一区| 亚洲啪啪综合av一区二区三区| 五月天婷婷激情| 精品久久人人做人人爽| 精品自拍一区| 国产精品99久久久久久人| 成人av资源网址| 成人在线观看www| 免费高清不卡av| 成人性生交大免费看| 亚洲丶国产丶欧美一区二区三区| 国产精品乱码一区二区| 这里精品视频免费| 英国三级经典在线观看| 国产精品v欧美精品v日韩| 亚洲欧美综合久久久| 91欧美视频在线| 国产欧美精品日韩区二区麻豆天美| 日韩欧美中文字幕一区二区| 欧美成人福利视频| 超碰最新在线| 亚洲va欧美va国产综合剧情 | 久久综合久色欧美综合狠狠| 麻豆成人在线视频| 欧美一区日韩一区| 好了av在线| 成人信息集中地欧美| 久久中文字幕av| www.超碰97.com| 国产精品理伦片| 中文字幕乱码视频| 尤物yw午夜国产精品视频| 欧美性片在线观看| 亚洲激情电影在线| 久久精品国产一区二区三区免费看| 在线观看国产精品一区| 欧美影视一区在线| 成人在线免费看| 国产精品久久久久秋霞鲁丝| jiujiure精品视频播放| 中文字幕天天干| 国产精品久久久久久久岛一牛影视 | 亚洲黄页一区| 免费成人蒂法网站| 一本久久a久久精品亚洲| 国产精品秘入口| 成人动漫网站在线观看| 欧美一区不卡| 极品白嫩少妇无套内谢| 香港成人在线视频| 水莓100在线视频| 国产ts人妖一区二区三区| 欧美三级情趣内衣| 九九热视频免费| 亚洲成av人片一区二区| 日本啊v在线| 国产精品一区二区三区免费视频| 99tv成人| 午夜不卡久久精品无码免费| 色综合久久久久网| 欧美三级黄网| 国产精品国产亚洲精品看不卡15| 在线综合亚洲| 国产毛片欧美毛片久久久| 欧美久久久一区| av老司机在线观看| 欧美日韩国产三区| 久久se精品一区二区| 久久影院一区二区| 亚洲美腿欧美激情另类| 四虎影视成人精品国库在线观看 | 精品国产一区二区三区久久久蜜月| 欧美a级在线观看| 亚洲精品8mav| av影院午夜一区| 中文字幕久久网| 韩国三级电影久久久久久| 日韩精品一区二区三区免费观看| 一起草最新网址| 色婷婷综合视频在线观看| 黄色免费在线观看网站| 精品久久精品久久| 久久精品国产久精国产| 日本少妇毛茸茸高潮| 伊人精品在线观看| 成人线上播放| 国产又黄又猛的视频|