Java8新特性 - Stream API

Stream是Java8中處理集合的關鍵抽象概念,它能夠指定你但願對集合進行的操做,能夠執行很是複雜的查找、過濾和映射數據等操做。使用Stream API對集合進行操做,就相似與使用SQL執行的數據庫操做。也可使用Stream API來並行執行操做。簡而言之,Stream API 提供了一種高效且易於使用的處理數據的方式。數據庫

什麼是Stream

是數據渠道,用於操做數據源(集合、數組等)所生成的元素序列
集合講的是數據,流講的是計算數組

PS:dom

  • Stream本身不會存儲元素
  • Stream不會改變源對象,相反,他們會返回一個持有結果的新Stream
  • Stream操做是延遲執行的,這意味着他們會等到須要結果的時候才執行

下面的圖能夠比較直觀的反映這一過程:
ide

  • 建立Stream
    一個數據源(數組、集合等),獲取一個流
  • 中間操做
    一箇中間操做鏈,對數據源的數據進行處理
  • 終止操做(終端操做)
    一個終止操做,執行中間操做鏈,併產生結果

建立Stream

經過Collection系列集合提供的stream()或parallelStream()

Java8中的Collection接口被擴展,提供了兩個獲取流的方法:函數

  • default Stream stream():返回一個順序流
  • default Stream parallelStream():返回一個並行流

示例代碼:測試

List<Integer> list = new ArrayList<>();
Stream<Integer> stream1 = list.stream();
Stream<Integer> stream2 = list.parallelStream();

由數組建立流

Java8的Arrays的靜態方法stream()能夠獲取數據流this

  • static Stream stream(T[] arrays):返回一個流

示例代碼:code

Integer[] integers = new Integer[10];
Stream<Integer> stream = Arrays.stream(integers);

由值建立流

經過Stream類中的靜態方法of(),經過顯示值建立一個流,能夠接收任意數量的參數對象

  • public static Stream of(T ... values):返回一個流

示例代碼:blog

Stream<Integer> stream = Stream.of(1, 2, 3);

建立無限流

使用靜態方法Stream.iterate()和Stream.generate(),建立無限流

  • 迭代:public static Stream iterate(final T seed, final UnaryOperator f)
  • 生成:public static Stream generate(Supplier s)

示例代碼:

// 迭代
Stream stream1 = Stream.iterate(0, (x) ->  x + 2);
// 生成
Stream stream2 = Stream.generate(() -> Math.random());

中間操做

多個中間操做能夠鏈接起來造成一個流水線,除非流水線上觸發終止操做,不然中間操做不會執行任何的處理!而在終止操做時一次性所有處理,稱之爲「惰性求值」

篩選與切片

  • filter:結合搜lambda,從流中排除元素
  • limit:截斷流,使其元素不超過給定數量
  • skip(n):跳過元素,返回一個刪除了前n個元素的流;若流中元素不足n個,則返回一個空流;與limit(n)互補
  • distinct:篩選,經過流所生成的元素的hashCode()和equals()去除重複元素

示例代碼:

public class TestStreamApi {
    private static List<Demo> demoList = Arrays.asList(
            new Demo(1, "哈哈哈"),
            new Demo(2, "嘿嘿嘿嘿"),
            new Demo(3, "呵呵呵"),
            new Demo(4, "恩恩恩恩"),
            new Demo(5, "哼哼哼"),
            new Demo(6, "嘖嘖嘖"),
            new Demo(5, "哼哼哼"),
            new Demo(8, "哼")
    );

    public static void main(String[] args) {
        // 中間操做不會執行任何操做
        Stream<Demo> demoStream = demoList.stream()
                .filter((x) -> x.getRemark().length() == 3)
                .limit(4)
                .skip(1)
                .distinct();

        // 終止操做一次性執行所有內容
        // 內部迭代:迭代操做由Stream API完成
        demoStream.forEach(System.out::println);
    }
}

運行結果:
3-呵呵呵
5-哼哼哼
6-嘖嘖嘖

注意:distinct篩選經過流所生成的元素的hashCode()和equals()去除重複元素,因此須要重寫Demo的hashCode()和equals()方法。

映射

  • map:接收Lambda,將元素轉換成其它形式或提取信息;接收一個函數做爲參數,該函數會被應用到每一個元素上,並將其映射成一個新的元素
  • flatMap:接收一個函數做爲參數,將流中的每一個值都換成另外一個流,而後把全部的流鏈接成一個流

示例代碼:

public class TestStreamApi {
    private static List<Demo> demoList = Arrays.asList(
            new Demo(1, "哈哈哈"),
            new Demo(2, "嘿嘿嘿嘿")
    );

    public static void main(String[] args) {
        demoList.stream()
                .map(Demo::getRemark)
                .flatMap(TestStreamApi :: filterCharacter)
                .forEach(System.out::println);
    }

    public static Stream<Character> filterCharacter(String str) {
        List<Character> list = new ArrayList<>();
        for (Character c : str.toCharArray()) {
            list.add(c);
        }
        return list.stream();
    }
}

運行結果:






排序

  • sorted():天然排序
  • sorted(Comparator c):定製排序

示例代碼:

public class TestStreamApi {
    private static List<Demo> demoList = Arrays.asList(
            new Demo(5, "哈哈哈"),
            new Demo(2, "嘿嘿嘿嘿"),
            new Demo(3, "呵呵呵"),
            new Demo(2, "哼哼哼"),
            new Demo(5, "嘖嘖嘖")
    );

    public static void main(String[] args) {
        List<String> list = Arrays.asList("aaa", "bbb", "ccc");
        list.stream()
                .sorted()
                .forEach(System.out::println);
        System.out.println("----------");
        demoList.stream()
                .sorted((x, y) -> {
                    if (x.getNum().equals(y.getNum())) {
                        return x.getRemark().compareTo(y.getRemark());
                    } else {
                        return x.getNum().compareTo(y.getNum());
                    }
                })
                .forEach(System.out::println);
    }
}

運行結果:
aaa
bbb
ccc
----------
2-哼哼哼
2-嘿嘿嘿嘿
3-呵呵呵
5-哈哈哈
5-嘖嘖嘖

終止操做

查找與匹配

  • allMatch:檢查是否匹配全部元素
  • anyMatch:檢查是否匹配全部元素
  • noneMatch:檢查是否沒有匹配全部元素
  • findFirst:返回第一個元素
  • findAny:返回當前流中的任意元素
  • count:返回流中元素的總個數
  • max:返回流中的最大值
  • min:返回流中的最小值

示例代碼:

public class TestStreamApi2 {
        private static List<Demo> demoList = Arrays.asList(
            new Demo("張三", 18, 6666.66, Demo.Status.BUSY),
            new Demo("李四", 38, 3333.33, Demo.Status.FREE),
            new Demo("王五", 28, 5555.55, Demo.Status.FREE),
            new Demo("趙六", 48, 7777.77, Demo.Status.BUSY),
            new Demo("王二麻子", 58, 8888.88, Demo.Status.VOCATION)

    );

    public static void main(String[] args) {
        // 是否是全部的對象都處於BUSY狀態
        System.out.println(demoList.stream()
                .allMatch((d) -> d.getStatus().equals(Demo.Status.BUSY)));
        // 是否有對象處於BUSY狀態
        System.out.println(demoList.stream()
                .anyMatch((d) -> d.getStatus().equals(Demo.Status.BUSY)));
        // 是否沒有對象處於BUSY狀態
        System.out.println(demoList.stream()
                .noneMatch((d) -> d.getStatus().equals(Demo.Status.BUSY)));
        // 獲取工資最高的
        Optional<Demo> optionalDemo1 = demoList.stream()
                .sorted((x, y) -> -Double.compare(x.getSalary(), y.getSalary()))
                .findFirst();
        System.out.println(optionalDemo1.get());
        // 獲取隨機一個空閒的
        Optional<Demo> optionalDemo2 = demoList.stream()
                .filter((e) -> e.getStatus().equals(Demo.Status.FREE))
                .findAny();
        System.out.println(optionalDemo2.get());
        // 總數
        System.out.println(demoList.stream().count());
        // 工資最高的
        Optional<Demo> optionalDemo3 = demoList.stream()
                .max((x, y) -> Double.compare(x.getSalary(), y.getSalary()));
        System.out.println(optionalDemo3.get());
        // 最小的工資
        Optional<Double> optionalDemo4 = demoList.stream()
                .map(Demo::getSalary)
                .max(Double::compare);
        System.out.println(optionalDemo4.get());
    }
}

class Demo{
    // 姓名
    String name;
    // 年齡
    Integer age;
    // 工資
    Double salary;
    // 狀態
    Status status;

    public Demo() {}

    public Demo(String name, Integer age, Double salary, Status status) {
        this.name = name;
        this.age = age;
        this.salary = salary;
        this.status = status;
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Double getSalary() {
        return salary;
    }

    public void setSalary(Double salary) {
        this.salary = salary;
    }

    public Status getStatus() {
        return status;
    }

    public void setStatus(Status status) {
        this.status = status;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Demo demo = (Demo) o;
        return name.equals(demo.name) &&
                age.equals(demo.age) &&
                salary.equals(demo.salary) &&
                status == demo.status;
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age, salary, status);
    }

    @Override
    public String toString() {
        return "Demo{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                ", status=" + status +
                '}';
    }

    public enum Status{
        FREE,
        BUSY,
        VOCATION
    }
}

運行結果:
false
true
false
Demo{name='王二麻子', age=58, salary=8888.88, status=VOCATION}
Demo{name='李四', age=38, salary=3333.33, status=FREE}
5
Demo{name='王二麻子', age=58, salary=8888.88, status=VOCATION}
8888.88

歸約

  • reduce(T identify, BinaryOperator) / reduce(BinaryOperator):能夠將流中元素反覆結合起來,獲得一個值

示例代碼:

public class TestStreamApi3 {
    private static List<Demo> demoList = Arrays.asList(
            new Demo("張三", 18, 6666.66, Demo.Status.BUSY),
            new Demo("李四", 38, 3333.33, Demo.Status.FREE),
            new Demo("王五", 28, 5555.55, Demo.Status.FREE),
            new Demo("趙六", 48, 7777.77, Demo.Status.BUSY),
            new Demo("王二麻子", 58, 8888.88, Demo.Status.VOCATION)
    );

    public static void main(String[] args) {
        Optional<Double> optional = demoList.stream()
                .map(Demo::getSalary)
                .reduce(Double::sum);
        System.out.println(optional.get());
    }
}

運行結果:
32222.190000000002

收集

  • collect:將流轉換爲其餘形式。接收一個Collection接口的實現,用於給Stream中元素作彙總的方法

Collectors接口中方法的實現決定了如何對流執行收集操做(如蒐集到List、Set、Map)。

  • toList:把流中元素收集到List
  • toSet:把流中元素收集到Set
  • toCollection:把流中元素收集到建立的集合
  • counting:計算流中元素的個數
  • summingInt:對流中元素的整數屬性求和
  • averagingInt:計算流中元素Integer屬性的平均值
  • summarizingInt:收集流中Integer屬性的統計值
  • jioning:鏈接流中的每一個字符串
  • maxBy:根據比較器選擇最大值
  • minBy:根據比較器選擇最小值
  • reducing:從一個做爲累加器的初始值開始,利用BinaryOperator與流中元素逐個結合,從而規約成單個值
  • collectingAndThen:包裹另外一個收集器,對其結果轉換函數
  • groupingBy:根據某個屬性值對流分組,屬性爲K,結果爲V
  • partitioningBy:根據true、false進行分區

給定一個數組,方便測試:

private static List<Demo> demoList = Arrays.asList(
            new Demo("張三", 18, 6666.66, Demo.Status.BUSY),
            new Demo("李四", 38, 3333.33, Demo.Status.FREE),
            new Demo("王五", 28, 5555.55, Demo.Status.FREE),
            new Demo("趙六", 48, 7777.77, Demo.Status.BUSY),
            new Demo("王二麻子", 58, 8888.88, Demo.Status.VOCATION)
    );

toList

示例代碼:

// 收集 - toList
System.out.println("---------------->toList");
List<String> list = demoList.stream()
            .map(Demo::getName)
            .collect(Collectors.toList());
list.forEach(System.out::println);

運行結果:
張三
李四
王五
趙六
王二麻子

toSet

示例代碼:

// 收集 - toSet
System.out.println("---------------->toSet");
Set<String> set = demoList.stream()
        .map(Demo::getName)
        .collect(Collectors.toSet());
set.forEach(System.out::println);

運行結果:
李四
張三
王二麻子
王五
趙六

toCollection

示例代碼:

// 收集 - toCollection
System.out.println("---------------->toCollection");
HashSet<String> hashSet = demoList.stream()
        .map(Demo::getName)
        .collect(Collectors.toCollection(HashSet::new));
hashSet.forEach(System.out::println);

運行結果:
李四
張三
王二麻子
王五
趙六

counting

示例代碼:

// 收集 - counting 計算總數
System.out.println("---------------->counting");
System.out.println(demoList.stream()
        .collect(Collectors.counting()));

運行結果:
5

summingInt

示例代碼:

// 收集 - summingInt 計算年齡總和
System.out.println("---------------->summingInt");
System.out.println(demoList.stream()
        .collect(Collectors.summingInt(Demo::getAge)));

運行結果:
190

averagingInt

示例代碼:

// 收集 - averagingInt 平均年齡
System.out.println("---------------->averagingInt");
System.out.println(demoList.stream()
        .collect(Collectors.averagingInt(Demo::getAge)));

運行結果:
38.0

summarizingInt

示例代碼:

// 收集 - summarizingInt
System.out.println("---------------->summarizingInt");
IntSummaryStatistics summaryStatistics = demoList.stream()
        .collect(Collectors.summarizingInt(Demo::getAge));
// 最大值
System.out.println(summaryStatistics.getMax());
// 平均值
System.out.println(summaryStatistics.getAverage());
// 總和
System.out.println(summaryStatistics.getSum());

運行結果:
58
38.0
190

joining

示例代碼:

// 收集 - joining 鏈接姓名
System.out.println("---------------->joining");
String s = demoList.stream()
        .map(Demo::getName)
        .collect(Collectors.joining(",", "開始->", "<-結束"));
System.out.println(s);

運行結果:
開始->張三,李四,王五,趙六,王二麻子<-結束

maxBy

示例代碼:

// 收集 - maxBy 獲取工資最高的人
System.out.println("---------------->maxBy");
Optional<Demo> max = demoList.stream()
        .collect(Collectors.maxBy((x, y) -> Double.compare(x.getSalary(), y.getSalary())));
System.out.println(max.get());

運行結果:
Demo{name='王二麻子', age=58, salary=8888.88, status=VOCATION}

minBy

示例代碼:

// 收集 - minBy 獲取最低的
System.out.println("---------------->minBy");
Optional<Double> min = demoList.stream()
        .map(Demo::getSalary)
        .collect(Collectors.minBy(Double::compare));
System.out.println(min.get());

運行結果:
3333.33

groupingBy

示例代碼:

// 收集 - groupingBy 根據狀態分組
System.out.println("---------------->groupingBy");
Map<Demo.Status, List<Demo>> group = demoList.stream()
        .collect(Collectors.groupingBy(Demo::getStatus));
System.out.println(group);
// 多級分組 先按狀態分組,在按年齡分組
Map<Demo.Status, Map<String, List<Demo>>> group2 = demoList.stream()
        .collect(Collectors.groupingBy(Demo::getStatus, Collectors.groupingBy((x) -> {
            if (x.getAge() <= 30) {
                return "青年";
            } else if (x.getAge() <= 45) {
                return "中年";
            } else {
                return "老年";
            }
        })));
System.out.println(group2);

運行結果:
{VOCATION=[Demo{name='王二麻子', age=58, salary=8888.88, status=VOCATION}], FREE=[Demo{name='李四', age=38, salary=3333.33, status=FREE}, Demo{name='王五', age=28, salary=5555.55, status=FREE}], BUSY=[Demo{name='張三', age=18, salary=6666.66, status=BUSY}, Demo{name='趙六', age=48, salary=7777.77, status=BUSY}]}
{VOCATION={老年=[Demo{name='王二麻子', age=58, salary=8888.88, status=VOCATION}]}, FREE={青年=[Demo{name='王五', age=28, salary=5555.55, status=FREE}], 中年=[Demo{name='李四', age=38, salary=3333.33, status=FREE}]}, BUSY={青年=[Demo{name='張三', age=18, salary=6666.66, status=BUSY}], 老年=[Demo{name='趙六', age=48, salary=7777.77, status=BUSY}]}}

partitioningBy

示例代碼:

// 收集 - partitioningBy 分區
System.out.println("---------------->partitioningBy");
Map<Boolean, List<Demo>> partition = demoList.stream()
        .collect(Collectors.partitioningBy((x) -> x.getAge() > 30));
System.out.println(partition);

運行結果: {false=[Demo{name='張三', age=18, salary=6666.66, status=BUSY}, Demo{name='王五', age=28, salary=5555.55, status=FREE}], true=[Demo{name='李四', age=38, salary=3333.33, status=FREE}, Demo{name='趙六', age=48, salary=7777.77, status=BUSY}, Demo{name='王二麻子', age=58, salary=8888.88, status=VOCATION}]}

相關文章
相關標籤/搜索