Java數據處理利器-Stream流編程入門

什麼是流

Java 8 API添加了一個新的抽象稱爲流Stream,其可以以一種聲明式的方式處理數據。它將基礎操作鏈接起來,能完成複雜的數據處理流水線,並提供了透明的並行處理機制。

Stream流的組成

  • 數據源
  • 中間操作
  • 終端操作

一圖勝千言,從整體上了解下相關的內容

在這裏插入圖片描述
流與集合的區別:

  • 流相對於是時間上的集合,得到的是某一時刻的數據(面向計算)
  • 集合相對於是空間上的集合,需要真實的存儲(面向存儲)
  • 流只能遍歷一次,集合可以反覆操作
  • 流是內部迭代,集合是外部迭代

怎麼使用流編程

下面將對流編程的操作和構建一一進行演示

創建一個實體類User方便我們使用

	class User {
        private Integer userId;
        private String userName;
        private String password;

        public User(Integer userId, String userName, String password) {
            this.userId = userId;
            this.userName = userName;
            this.password = password;
        }

        public Integer getUserId() {
            return userId;
        }

        public String getUserName() {
            return userName;
        }

        public String getPassword() {
            return password;
        }
    }

創建一個集合,作爲我們數據操作的數據

		List<User> list = new ArrayList<User>() {
            {
                add(new User(5,"ckhhh", "123123"));
                add(new User(1,"zhangsan", "1232312"));
                add(new User(3,"lisi", "000000"));
                add(new User(2,"lisi", "4324324"));
                add(new User(4,"wangwu", "99999"));


            }
        };

中間操作

  1. 無狀態的
  • filter使用
		/**
         * filter使用 按照指定條件過濾集合
         */
		  list.stream().
		             filter(user -> user.getUserName().equals("lisi")).
		             forEach(user ->
		                     System.out.println(JSON.toJSONString(user, true)));

打印用戶名爲lisi的用戶的信息,結果如下:

{
	"password":"000000",
	"userId":3,
	"userName":"lisi"
}
{
	"password":"4324324",
	"userId":2,
	"userName":"lisi"
}
  • map使用
		/**
         * map使用 從一種類型轉化爲另一種類型的集合,user->string
         */
        list.stream().
                map(user -> user.getUserName()).
                forEach(user ->
                        System.out.println(JSON.toJSONString(user, true)));

使用map將集合分割,只獲取所有用戶並打印,結果如下:

"ckhhh"
"zhangsan"
"lisi"
"lisi"
"wangwu"
  • flatMap操作
   		/**
         * flatMap操作 接收一個元素,返回一個新的流(將對象轉換爲流操作)
         */
        list.stream()
                .flatMap(user -> Arrays.stream(user.getUserName().split("")))
                .forEach(user ->
                        System.out.println(JSON.toJSONString(user, true)));

使用flatMap將集合變爲新的流:將名字分割成字符,然後打印,結果如下:

"c"
"k"
"h"
"h"
"h"
"z"
"h"
"a"
"n"
"g"
"s"
"a"
"n"
"l"
"i"
"s"
"i"
"l"
"i"
"s"
"i"
"w"
"a"
"n"
"g"
"w"
"u"
  • peek操作
		 /**
         * peek 中間操作 無狀態的 操作完後流還可以使用  foreach使用後就不能用了
         * peek和foreach交替打印
         */
        list.stream()
                .peek(user -> System.out.println(user.getUserName()))
                .forEach(user ->
                        System.out.println(JSON.toJSONString(user, true)));

打印用戶名,因爲是無狀態的,所以會執行完一下peek後執行一下foreach中的打印,結果如下

ckhhh
{
	"password":"123123",
	"userId":5,
	"userName":"ckhhh"
}
zhangsan
{
	"password":"1232312",
	"userId":1,
	"userName":"zhangsan"
}
lisi
{
	"password":"000000",
	"userId":3,
	"userName":"lisi"
}
lisi
{
	"password":"4324324",
	"userId":2,
	"userName":"lisi"
}
wangwu
{
	"password":"99999",
	"userId":4,
	"userName":"wangwu"
}
  1. 有狀態的
  • sort操作
 		/**
         * sort操作,對數據按照規則排序
         * sorted是有狀態的  所以會等peek結束之後再操作
         * peek和foreach就不會再交替打印了
         */
        list.stream()
                .peek(user -> System.out.println(user.getUserName()))
                .sorted(Comparator.comparing(User::getUserId))
                .forEach(user ->
                        System.out.println(JSON.toJSONString(user, true)));

排序後打印,是有狀態的,所以必須所有peek執行完了後才能執行,根據userId升序排列,結果如下:

ckhhh
zhangsan
lisi
lisi
wangwu
{
	"password":"1232312",
	"userId":1,
	"userName":"zhangsan"
}
{
	"password":"4324324",
	"userId":2,
	"userName":"lisi"
}
{
	"password":"000000",
	"userId":3,
	"userName":"lisi"
}
{
	"password":"99999",
	"userId":4,
	"userName":"wangwu"
}
{
	"password":"123123",
	"userId":5,
	"userName":"ckhhh"
}
  • distinct操作
/**
         * distinct操作  去除重複
         */
        list.stream()
                .map(user -> user.getUserName())
                .distinct()
                .forEach(user ->
                        System.out.println(JSON.toJSONString(user, true)));

去除用戶名中的重複項後打印用戶名,結果如下:

"ckhhh"
"zhangsan"
"lisi"
"wangwu"
  • skip操作
		/**
         *  skip過濾掉前三條
         */
        list.stream()
                .sorted(Comparator.comparing(user ->user.getUserId()))
                .skip(3)
                .forEach(user ->
                        System.out.println(JSON.toJSONString(user, true)));

按id升序排序後,跳過前三條,從第四條開始打印,結果如下:

{
	"password":"99999",
	"userId":4,
	"userName":"wangwu"
}
{
	"password":"123123",
	"userId":5,
	"userName":"ckhhh"
}
  • limit操作
		 /**
         * limit操作  獲取前三條
         */
        list.stream()
                .sorted(Comparator.comparing(user ->user.getUserId()))
                .limit(3)
                .forEach(user ->
                        System.out.println(JSON.toJSONString(user, true)));

按id升序排列後,打印前三條,結果如下:

{
	"password":"1232312",
	"userId":1,
	"userName":"zhangsan"
}
{
	"password":"4324324",
	"userId":2,
	"userName":"lisi"
}
{
	"password":"000000",
	"userId":3,
	"userName":"lisi"
}

終端操作

  1. 短路的操作
  • allMatch操作
		/**
         * allMatch 使用:是否全部滿足終端操作,短路的操作
         * 如果發現了不匹配的  就不會繼續執行了,peek中打印的數據打不符合的就停止了
         */
        boolean b = list.stream()
                .peek(user -> System.out.println(user.getUserId()))
                .allMatch(user->user.getUserId()> 2);
        System.out.println(b);

判斷是否所有的用戶id都大於2,一旦發現小於等於2的就返回false,結果如下

5
1
false
  • anyMatch操作
		/**
         * anyMatch使用  :只要有滿足的,就返回true
         * 短路的  找到一個符合的就返回true
         */
        boolean c = list.stream()
                 .peek(user -> System.out.println(user.getUserId()))
                .anyMatch(user->user.getUserId()> 2);
        System.out.println(c);

判斷是否有用戶id大於2,一旦發現大於2的就返回true,結果如下

5
true
  • noneMatch操作
		/**
         * noneMatch使用:所有的都沒匹配上 返回true
         * 短路的,找到一個符合的就返回false
         */
        boolean d = list.stream()
                 .peek(user -> System.out.println(user.getUserId()))
                .noneMatch(user->user.getUserId()> 2);
        System.out.println(d);

判斷是否沒有用戶id大於2,一旦發現大於2的就返回false,結果如下:

5
false
  • findFirst操作
 		/**
         * findFirst:獲取第一個對象
         * 短路的  獲取了就返回
         */
        Optional<User> first = list.stream()
                .findFirst();
        System.out.println(JSON.toJSONString(first.get(), true));

返回第一個對象,結果如下

{
	"password":"123123",
	"userId":5,
	"userName":"ckhhh"
}
  • findAny操作
		 /**
         * findAny找到任何一個就返回
         * 並行操作時findAny會更快,可能會隨機匹配 如果串行一致
         */
        Optional<User> first1 = list.stream()
                .findAny();
        System.out.println(JSON.toJSONString(first1.get(), true));

返回任意一個對象,在串行下和返回第一個對象一樣,在並行下就是隨機的,結果如下:

{
	"password":"123123",
	"userId":5,
	"userName":"ckhhh"
}
  1. 非短路操作
  • max操作
		 /**
         * max操作:獲取最大值
         */
        OptionalDouble max = list.stream()
                .mapToDouble(User::getUserId)
                .max();
        System.out.println(max.getAsDouble());

將用戶id列分割出來,將值轉爲doble,返回最大值,結果如下:

5.0
  • min操作
		/**
         * min操作:獲取最小值
         */
        OptionalDouble min = list.stream()
                .mapToDouble(User::getUserId)
                .min();
        System.out.println(min.getAsDouble());

將用戶id列分割出來,將值轉爲doble,返回最小值,結果如下:

1.0
  • count操作
 		/**
         * count操作:獲取元素的個數
         */
        long count = list.stream()
                .count();
        System.out.println(count);

統計元素個數,結果如下:

5
  • collect操作
		/**
         *  集合收集器
         */
        List<User> result = list.stream()
                .filter(user -> user.getUserId() > 2)
                .collect(Collectors.toList());
        System.out.println(JSON.toJSONString(result, true));

將id大於2的元素重新寫入集合,打印結果如下:

[
	{
		"password":"123123",
		"userId":5,
		"userName":"ckhhh"
	},
	{
		"password":"000000",
		"userId":3,
		"userName":"lisi"
	},
	{
		"password":"99999",
		"userId":4,
		"userName":"wangwu"
	}
]

        /**
         *  分組收集器
         */
        Map<String, List<User>> group = list.stream()
                .collect(Collectors.groupingBy(
                        user -> user.userName));
        System.out.println(JSON.toJSONString(group, true));

將元素按用戶名分組後打印,結果如下:

{
	"lisi":[
		{
			"password":"000000",
			"userId":3,
			"userName":"lisi"
		},
		{
			"password":"4324324",
			"userId":2,
			"userName":"lisi"
		}
	],
	"ckhhh":[
		{
			"password":"123123",
			"userId":5,
			"userName":"ckhhh"
		}
	],
	"zhangsan":[
		{
			"password":"1232312",
			"userId":1,
			"userName":"zhangsan"
		}
	],
	"wangwu":[
		{
			"password":"99999",
			"userId":4,
			"userName":"wangwu"
		}
	]
}

        /**
         * 分區收集器
         * 分爲大於2和小於等於2兩部分
         */
        Map<Boolean, List<User>> partition = list.stream()
                .collect(Collectors.partitioningBy(user -> user.getUserId() > 2));

        System.out.println(JSON.toJSONString(partition, true));

將元素按id大於2位標準分爲兩個區後進行打印,結果如下:

{false:[
		{
			"password":"1232312",
			"userId":1,
			"userName":"zhangsan"
		},
		{
			"password":"4324324",
			"userId":2,
			"userName":"lisi"
		}
	],true:[
		{
			"password":"123123",
			"userId":5,
			"userName":"ckhhh"
		},
		{
			"password":"000000",
			"userId":3,
			"userName":"lisi"
		},
		{
			"password":"99999",
			"userId":4,
			"userName":"wangwu"
		}
	]
}

構建流

  • 由數值直接構建流
public void steamFromValue() {
    Stream stream = Stream.of(1, 2, 3, 4, 5);

    stream.forEach(System.out::println);
}

運行結果如下:

1
2
3
4
5
  • 由數組構建流
 public void streamFromArray() {
        int[] num = new int[]{1, 2, 3, 4, 5};
        Arrays.stream(num)
                .forEach(System.out::println);
  }

運行結果如下:

1
2
3
4
5
  • 通過文件構建流
public void streamFromFile() throws IOException {
        Stream<String> stream = Files.lines(Paths.get("F:/QQ下載/FileServer.java"));
        stream.forEach(System.out::println);
}

運行結果如下(部分)

package com.demo.lambda;

import java.io.*;

/**
 * 〈文件服務類〉
 *
 * @author Chkl
 * @create 2020/4/20
 * @since 1.0.0
 */
public class FileServer {

    /**
     * 通過url獲取本地文件內容
     *
     * @param url
     * @param fileConsumer
     */
    public void fileHandler(String url, FileConsumer fileConsumer) throws IOException {
	..............
	...............
  • 通過函數生成流(無限流)
public void streamFromFunction() {
        //參數: 初始值,規則(從0開始生成偶數)
        Stream<Integer> stream = Stream.iterate(0, n -> n + 2);
        stream.limit(100)
                .forEach(System.out::println);
        //隨機生成
        Stream<Double> stream2 = Stream.generate(Math::random);
        stream2.limit(100)
                .forEach(System.out::println);

}

第一種運行結果如下(部分):

0
2
4
6
8
10
12
14
16
18
20
22
24

第二種運行結果如下(部分):

0.28047181698982915
0.3366379505477519
0.6314896823002529
0.4202209521644461
0.8362832087391032
0.7025533309035066
0.49208504805565545
0.18517793464530763
0.37580620856029856
0.8888281509032474
0.8399345540793907
0.6470816912713735
0.9842389606178953
0.5166615554196825
0.035214233629631164

通過以上的學習,可以基本的掌握Stream流常用的一些操作和構建方便了,具體業務需要使用到的方法在使用時再去了解

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章