1. 程式人生 > >Stream-快速入門Stream編程

Stream-快速入門Stream編程

編程 dem 消息 m-api 方式 aso false 判斷 skip

一、什麽是流

Stream 不是集合元素,它不是數據結構並不保存數據,它是有關算法和計算的,它更像一個高級版本的 Iterator。原始版本的 Iterator,用戶只能顯式地一個一個遍歷元素並對其執行某些操作;高級版本的 Stream,用戶只要給出需要對其包含的元素執行什麽操作,比如 “過濾掉長度大於 10 的字符串”、“獲取每個字符串的首字母”等,Stream 會隱式地在內部進行遍歷,做出相應的數據轉換。

Stream 就如同一個叠代器(Iterator),單向,不可往復,數據只能遍歷一次,遍歷過一次後即用盡了,就好比流水從面前流過,一去不復返。

流看作在時間中分布的一組值。相反,集合則是空間(這裏就是計算機內存)中分布的一組值,在一個時間點上全體存在——你可以使用叠代器來訪問for-each循環中的內部成員。

Java 7 之前的處理:

List<Dish> lowCaloricDishes = new ArrayList<>();
for(Dish d: menu){
if(d.getCalories() < 400){
lowCaloricDishes.add(d);
}
}
Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
public int compare(Dish d1, Dish d2){
return Integer.compare(d1.getCalories(), d2.getCalories());
}
});
List
<String> lowCaloricDishesName = new ArrayList<>(); for(Dish d: lowCaloricDishes){ lowCaloricDishesName.add(d.getName()); }

Java 8 流處理:

import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;
List<String> lowCaloricDishesName =
menu.stream()
.filter(d 
-> d.getCalories() < 400) .sorted(comparing(Dish::getCalories)) .map(Dish::getName) .collect(toList());

Stream Api的特性:

  • 聲明性--更簡潔,更易讀
  • 可復合--更靈活
  • 可並行--性能更好
  • 只能遍歷一遍

二、使用流

簡單說,對 Stream 的使用就是實現一個 filter-map-reduce 過程,產生一個最終結果,或者導致一個副作用(side effect)。例如:

List<String> name = menu.stream()
                .filter(dish -> dish.getCalories() > 300)
                .map(Dish::getName)
                .limit(3)
                .collect(Collectors.toList());
  • filter:從流中排除某些元素
  • map:將元素轉換為其他形式或提取消息
  • limit:截斷流
  • collect:將流轉換為其他形式

接下來,我們將開始學習Stream中的Api 使用技巧,接下來將會使用到實體類 -Dish,以及鏈表:

實體類:Dish

public class Dish {
    private String name;
    private boolean vegetarian;
    private int calories;
    private Type type;

    public enum Type {MEAT, FISH, OTHER}

    public Dish(String name, boolean vegetarian, int calories, Type type) {
        this.name = name;
        this.vegetarian = vegetarian;
        this.calories = calories;
        this.type = type;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean isVegetarian() {
        return vegetarian;
    }

    public void setVegetarian(boolean vegetarian) {
        this.vegetarian = vegetarian;
    }

    public int getCalories() {
        return calories;
    }

    public void setCalories(int calories) {
        this.calories = calories;
    }

    public Type getType() {
        return type;
    }

    public void setType(Type type) {
        this.type = type;
    }
}

使用鏈表:

Arrays.asList(
                new Dish("pork", false, 800, Dish.Type.MEAT),
                new Dish("beef", false, 700, Dish.Type.MEAT),
                new Dish("chicken", false, 400, Dish.Type.MEAT),
                new Dish("french fries", true, 530, Dish.Type.OTHER),
                new Dish("rice", true, 350, Dish.Type.OTHER),
                new Dish("season fruit", true, 120, Dish.Type.OTHER),
                new Dish("pizza", true, 550, Dish.Type.OTHER),
                new Dish("prawns", false, 300, Dish.Type.FISH),
                new Dish("salmon", false, 450, Dish.Type.FISH));

1、篩選 Filter

1.1 謂詞篩選 filter

該操作會接受一個謂詞(一個返回boolean的函數)作為參數,並返回一個包括所有符合謂詞的元素的流。

示例代碼:

List<Dish> vegatarianMenu = menu.stream()
                .filter(Dish::isVegetarian)
                .collect(Collectors.toList());

  

1.2 去重篩選 distinct()

流還支持一個叫作distinct的方法,它會返回一個元素各異(根據流所生成元素的 hashCode和equals方法實現)的流

示例代碼:

List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
        numbers.stream()
                .filter(integer -> integer % 2 == 0)
                .distinct()
                .forEach(System.out::println);

輸出結果為:2,4(去重一個 2)

1.3 截短流 limit(n)

流支持limit(n)方法,該方法會返回一個不超過給定長度的流。所需的長度作為參數傳遞 給limit

List<Dish> vegatarianMenu = menu.stream()
                .filter(Dish::isVegetarian)
                .limit(3)
                .collect(Collectors.toList());

1.4 跳過元素 skip(n)

流還支持skip(n)方法,返回一個扔掉了前n個元素的流。如果流中元素不足n個,則返回一個空流

List<Dish> vegatarianMenu = menu.stream()
                .filter(Dish::isVegetarian)
                .skip(2)
                .collect(Collectors.toList());

2、映射 Map

流支持map方法,它會接受一個函數作為參數。這個函數會被應用到每個元素上,並將其映射成一個新的元素(使用映射一詞,是因為它和轉換類似,但其中的細微差別在於它是“創建一個新版本”而不是去“修改”

2.1 對流中的每一個元素應用

List<String> dishNames = menu.stream()
                .map(Dish::getName)
                .collect(Collectors.toList());

2.2 流的扁平化

對於一張單詞表, 如何返回一張列表, 列出裏面各不相同的字符呢? 例如, 給定單詞列表["Hello","World"],你想要返回列表["H","e","l", "o","W","r","d"]

List<String> uniqueCharacters = words.stream()
                            .map(w -> w.split(""))
                            .flatMap(Arrays::stream)
                            .distinct()
                            .collect(Collectors.toList());

3、匹配查找 Match Find

3.1 AnyMatch

anyMatch方法可以回答“流中是否有一個元素能匹配給定的謂詞”。返回結果為boolean數據類型,如果流中出現匹配項,返回True。

if (list.stream().anyMatch(Dish::isVegetarian)){
            System.out.printf("The menu is (somewhat) vegetarian friendly!!");
        }

3.2 AllMatch

AllMatch 用法和AnyMatch 相似,方法返回結果為“流中所有結果都符合判斷規則”。

boolean flag = list.stream().allMatch(dish -> dish.getCalories() <1000);

3.3 NoneMatch

NoneMatch 與 AllMatch 恰恰相反,返回結果為“流中所有結果都不符合”。

boolean flag = list.stream().allMatch(dish -> dish.getCalories() <1000);

3.4 findAny

findAny方法將返回當前流中的任意元素。它可以與其他流操作結合使用。方法返回結果為 Optional<T>。

list.stream()
    .filter(Dish::isVegetarian)
    .findAny()
    .ifPresent(d -> System.out.println(d.getName()));

3.5 findFirst

有些流有一個出現順序(encounterorder)來指定流中項目出現的邏輯順序(比如由List或排序好的數據列生成的流)。對於這種流,你可能想要找到第一個元素。為此有一個finFirst 方法,它的工作方式類似於findany。

someNumbers.stream()
    .map(x -> x * x)
    .filter(x -> x % 3 == 0)
    .findFirst()
    .ifPresent(System.out::println);

3.6 何時使用findFirst和findAny

你可能會想,為什麽會同時有findFirst和findAny呢?答案是並行。找到第一個元素 在並行上限制更多。如果你不關心返回的元素是哪個,請使用findAny,因為它在使用並行流 時限制較少。

4.歸約 Reduce

需要將流中所有元素反復結合起來,得到一個值,比如一個Integer。這樣的查詢可以被歸類為約操作 (將流歸約成一個值)。

元素求和

//Type 1
    int result = list.stream()
        .reduce(0, Integer::sum);
    System.out.println(result);

//Type 2
    list.stream()
        .reduce(Integer::sum)
        .ifPresent(System.out::println);

最大值和最小值

//Max
    list.stream()
        .reduce(Integer::max)
        .ifPresent(System.out::println);

//Min
    list.stream()
        .reduce(Integer::min)
        .ifPresent(System.out::println);

三、總結

github 地址:https://github.com/jaycekon/StreamDemo

  • 流是“從支持數據處理操作的源生成的一系列元素”
  • 流利用內部叠代:叠代通過filter、map、sorted等操作被抽象掉了。
  • 流操作有兩類:中間操作和終端操作。
  • filter和map等中間操作會返回一個流,並可以鏈接在一起。可以用它們來設置一條流 水線,但並不會生成任何結果。
  • forEach和count等終端操作會返回一個非流的值,並處理流水線以返回結果。
  • 流中的元素是按需計算的。

Stream-快速入門Stream編程