好久沒寫博客了,^_^。
目錄
1.基本概念。
jdk1.8 有着兩個重要的東西,那就是 lambda 與 stream,這兩樣對現在代碼編程還是有很大的影響。雖然 接口 增加了 default等一些新特性,但都沒有這兩樣重要的多。
stream 是個什麼鬼呢》? 從字面意思便可以理解爲 流,不過這個流 和 以前的 io、buffer流還是不一樣的,這個流是針對於 集合(collection)來使用的。它的使用就很好理解了,流嘛,都有一個開始、中間、結束(終端)。如同雪川融化經過長江流入大海一樣一樣的。
lambda 又是個什麼鬼呢》?從字面意思理解爲匿名函數,它也確實沒有辜負這個名字,匿名簡寫。這個東東是爲了簡化代碼操作。比如聲明一個方法體,需要new Runnable()啊,它可以簡寫爲 ()->{},極大的簡化了語法。
2. Stream 常用接口與操作
常用函數式接口
Supplier<T>
,主要方法:T get()
,這是一個生產者,可以提供一個T對象。Consumer<T>
,主要方法:void accept(T)
,這是一個消費者,默認方法:andthen()
,稍後執行。Predicate<T>
,主要方法:boolean test(T t)
,這是一個判斷者,默認方法:and()
:且,or()
:或,negate()
:非。Function<T,R>
,主要方法:R apply(T t)
,這是一個修改者,默認方法:compose()
:優先執行,andThen()
,稍後執行,identity()
:直接傳自身。
Stream 常用 api 操作
操作 |
類型 |
返回類型 |
函數式接口 |
函數描述符 |
filter |
中間 |
Stream<T> |
Predicate<T> |
T->Boolean |
distinct |
中間-有狀態 |
Stream<T> |
|
|
Skip |
中間-有狀態 |
Stream<T> |
Long |
|
Limit |
中間-有狀態 |
Stream<T> |
Long |
|
Map |
中間 |
Stream<T> |
Function<T,R> |
T->R |
Flatmap |
中間 |
Stream<T> |
Function<T,Stream<R>> |
T->Stream<R> |
Sorted |
中間-有狀態 |
Stream<T> |
Compartor<T> |
(T,T)->int |
anyMatch |
終端 |
Boolean |
Predicate<T> |
T->Boolean |
noneMatch |
終端 |
Boolean |
Predicate<T> |
T->Boolean |
allMatch |
終端 |
Boolean |
Predicate<T> |
T->Boolean |
findAny |
終端 |
Optional<T> |
|
|
findFirst |
終端 |
Optional<T> |
|
|
forEach |
終端 |
Void |
Consumer<T> |
T->void |
Collect |
終端 |
R |
Collector<T,A,R> |
|
Reduce |
終端-有狀態 |
Optional<T> |
BinaryOperator<T> |
(T,T)->T |
Count |
終端 |
Long |
|
|
想要知道更多騷操作?看看 java.util.stream 下面的 Stream 接口即可。
Stream<T> filter(Predicate<? super T> var1);
<R> Stream<R> map(Function<? super T, ? extends R> var1);
IntStream mapToInt(ToIntFunction<? super T> var1);
LongStream mapToLong(ToLongFunction<? super T> var1);
DoubleStream mapToDouble(ToDoubleFunction<? super T> var1);
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> var1);
IntStream flatMapToInt(Function<? super T, ? extends IntStream> var1);
LongStream flatMapToLong(Function<? super T, ? extends LongStream> var1);
DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> var1);
Stream<T> distinct();
Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> var1);
Stream<T> peek(Consumer<? super T> var1);
Stream<T> limit(long var1);
Stream<T> skip(long var1);
long count();
boolean anyMatch(Predicate<? super T> var1);
boolean allMatch(Predicate<? super T> var1);
boolean noneMatch(Predicate<? super T> var1);
Optional<T> findFirst();
Optional<T> findAny();
static <T> Stream.Builder<T> builder() {
return new StreamBuilderImpl();
}
3.舉個慄
3.1 首先準備一個數據源。
創建一個用戶類。
package com.example.demo.lambda;
/**
* 用戶
*
* @author wbw
* @date 2020/4/4 19:19
*/
public class User {
/**
* 姓名
*/
private String name;
/**
* id
*/
private Integer id;
/**
* 年齡
*/
private Integer age;
/**
* 地址
*/
private String address;
/**
* 手機號
*/
private String phone;
public User() {
}
public User(String name, Integer id, Integer age, String address, String phone) {
this.name = name;
this.id = id;
this.age = age;
this.address = address;
this.phone = phone;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", id=" + id +
", age='" + age + '\'' +
", address='" + address + '\'' +
", phone='" + phone + '\'' +
'}';
}
}
準備基礎數據
package com.example.demo.lambda;
import java.io.UnsupportedEncodingException;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* lambda
*
* @author wbw
* @date 2020/4/4 19:21
*/
public class LambdaTest {
public static void main(String[] args) {
// 初始化數據
List<User> userList = loadInfo();
System.out.println(userList);
}
/**
* 加載十條用戶信息
*
* @return userList
*/
private static List<User> loadInfo() {
List<User> userList = new LinkedList<>();
Random random = new Random();
for (int i = 0; i < 10; i++) {
userList.add(new User(names.get(i), i, random.nextInt(),
getRandomAddress(random.nextInt(10)), String.valueOf(random.nextLong())));
}
return userList;
}
/**
* 姓名
*/
private static final List<String> names = Stream.of("張三", "李四", "王五", "張可汗", "李白", "扁鵲", "馬可波羅", "上官婉兒", "太乙真人", "東皇太一").collect(Collectors.toList());
/**
* 自動生成地址(中文)
*
* @param len 長度
* @return name
*/
public static String getRandomAddress(int len) {
Random random = new Random();
StringBuilder ret = new StringBuilder();
for (int i = 0; i < len; i++) {
String str = "";
// 定義高低位
int height = (176 + Math.abs(random.nextInt(39))); // 獲取高位值
int lowPos = (161 + Math.abs(random.nextInt(93))); // 獲取低位值
byte[] b = {Integer.valueOf(height).byteValue(), Integer.valueOf(lowPos).byteValue()};
try {
str = new String(b, "GBK"); // 轉成中文
} catch (UnsupportedEncodingException ex) {
ex.printStackTrace();
}
ret.append(str);
}
return ret.toString();
}
}
ok,開始正式騷操作。
3.2 如何準備一個流呢?
// 初始化數據
List<User> userList = loadInfo();
// 得到一個流
// 1. 直接調用集合自帶方法
Stream<User> stream = userList.stream();
// 2. 使用 stream 初始化
Stream<Object> objectStream = Stream.of(userList.toArray());
Stream.Builder<User> builder = Stream.builder();
userList.forEach(e->builder.add(e));
// 等價於 當明確調用一個帶匿名函數方法時可以簡寫
userList.forEach(builder::add);
3.3 對流進行操作
// 初始化數據
List<User> userList = loadInfo();
// 統計數量
long count = userList.stream().count();
// 轉換成 map ,key爲 id,(k1, k2) -> k1) 是指 key 的主鍵重複時,使用新的還是舊的主鍵,這裏使用舊的
Map<Integer, User> userMap = userList.stream().collect(Collectors.toMap(user -> user.getId(), user -> user, (k1, k2) -> k1));
// lambda 簡寫 User::getId 等於 user -> user.getId()
userMap = userList.stream().collect(Collectors.toMap(User::getId, user -> user, (k1, k2) -> k1));
// 再把 map 轉換成 list
Collection<User> users = Optional.of(userMap).map(Map::values).get();
// 獲取前8個用戶
List<User> collect = userList.stream().limit(8).collect(Collectors.toList());
// 去重
List<User> collect1 = userList.stream().distinct().collect(Collectors.toList());
// 獲取任意一個
User user = userList.stream().findAny().get();
// 獲取第一個
User user1 = userList.stream().findFirst().get();
// 匹配所有
boolean b = userList.stream().allMatch(e -> e.getId() == 1);
// 匹配任意一個
boolean b1 = userList.stream().anyMatch(e -> e.getId() == 1);
// 獲取 姓名 包含張的 用戶
List<User> userList1 = userList.stream().filter(e -> e.getName().contains("張")).collect(Collectors.toList());
// 對用戶名 進行拆分
List<String[]> collect2 = userList.stream().map(e -> e.getName().split("")).collect(Collectors.toList());
// 流扁平化 flatMap 拆分用戶名,去除重複字,得到一個list
List<String> collect3 = userList.stream().map(e -> e.getName().split("")).flatMap(Arrays::stream).distinct().collect(Collectors.toList());
// 獲取用戶名長度爲 2 的用戶,直到長度不爲 2 時結束
long count1 = userList.stream().takeWhile(u -> u.getName().length() == 2).count();
// 和上面相反,刪除 用戶名長度爲 2 的用戶,直到 長度不爲 2 時 獲取。
count1 = userList.stream().dropWhile(u -> u.getName().length() == 2).count();
// 排序
userList.stream().sorted(Comparator.comparingInt(User::getAge)).forEach(e -> System.out.println(e.getAge()));
// 逆序
userList.stream().sorted(Comparator.comparingInt(User::getAge).reversed()).forEach(e -> System.out.println(e.getAge()));
// peek 等於 foreach 返回一個流,繼續操作
userList.stream().peek(us -> us.setId(1)).map(User::getId).forEach(System.out::println);
// 獲取 用戶 年齡 最小的
User user2 = userList.stream().min((o1, o2) -> o1.getAge() > o2.getAge() ? o2.getAge() : o1.getAge()).get();
// 獲取用戶年齡最大的
User user3 = userList.stream().max((o1, o2) -> o1.getAge() < o2.getAge() ? o2.getAge() : o1.getAge()).get();
// reduce 比較 用戶年齡,除此之外 reduce 還可以對 數組集合進行操作計算
User user4 = userList.stream().reduce((a, w) -> a.getAge() > w.getAge() ? a: w).get();
// 跳過 前五個 元素
long count2 = userList.stream().skip(5).count();
// parallel 使用多線程處理
userList.stream().parallel().forEach(System.out::println);
// sequential 串行執行 主要和 上面多線程 結合使用
userList.stream().sequential().forEach(System.out::println);
System.out.println();
// unordered 返回一個無序流
userList.stream().sorted(Comparator.comparing(User::getName)).unordered().forEach(System.out::println);
4. 結束
到這裏,想必也清楚了 流的 基本操作與使用。
返回 void 與 具體的 (int、long、list、boolean、對象)都是終端操作,返回stream 都是中間操作。
完整代碼
package com.example.demo.lambda;
import java.io.UnsupportedEncodingException;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* lambda
*
* @author wbw
* @date 2020/4/4 19:21
*/
public class LambdaTest {
public static void main(String[] args) {
// 初始化數據
List<User> userList = loadInfo();
// 統計數量
long count = userList.stream().count();
// 轉換成 map ,key爲 id,(k1, k2) -> k1) 是指 key 的主鍵重複時,使用新的還是舊的主鍵,這裏使用舊的
Map<Integer, User> userMap = userList.stream().collect(Collectors.toMap(user -> user.getId(), user -> user, (k1, k2) -> k1));
// lambda 簡寫 User::getId 等於 user -> user.getId()
userMap = userList.stream().collect(Collectors.toMap(User::getId, user -> user, (k1, k2) -> k1));
// 再把 map 轉換成 list
Collection<User> users = Optional.of(userMap).map(Map::values).get();
// 獲取前8個用戶
List<User> collect = userList.stream().limit(8).collect(Collectors.toList());
// 去重
List<User> collect1 = userList.stream().distinct().collect(Collectors.toList());
// 獲取任意一個
User user = userList.stream().findAny().get();
// 獲取第一個
User user1 = userList.stream().findFirst().get();
// 匹配所有
boolean b = userList.stream().allMatch(e -> e.getId() == 1);
// 匹配所有
boolean b1 = userList.stream().anyMatch(e -> e.getId() == 1);
// 獲取 姓名 包含張的 用戶
List<User> userList1 = userList.stream().filter(e -> e.getName().contains("張")).collect(Collectors.toList());
// 對用戶名 進行拆分
List<String[]> collect2 = userList.stream().map(e -> e.getName().split("")).collect(Collectors.toList());
// 流扁平化 flatMap 拆分用戶名,去除重複字,得到一個list
List<String> collect3 = userList.stream().map(e -> e.getName().split("")).flatMap(Arrays::stream).distinct().collect(Collectors.toList());
// 獲取用戶名長度爲 2 的用戶,直到長度不爲 2 時結束
long count1 = userList.stream().takeWhile(u -> u.getName().length() == 2).count();
// 和上面相反,刪除 用戶名長度爲 2 的用戶,直到 長度不爲 2 時 獲取。
count1 = userList.stream().dropWhile(u -> u.getName().length() == 2).count();
// 排序
userList.stream().sorted(Comparator.comparingInt(User::getAge)).forEach(e -> System.out.println(e.getAge()));
// 逆序
userList.stream().sorted(Comparator.comparingInt(User::getAge).reversed()).forEach(e -> System.out.println(e.getAge()));
// peek 等於 foreach 返回一個流,繼續操作
userList.stream().peek(us -> us.setId(1)).map(User::getId).forEach(System.out::println);
// 獲取 用戶 年齡 最小的
User user2 = userList.stream().min((o1, o2) -> o1.getAge() > o2.getAge() ? o2.getAge() : o1.getAge()).get();
// 獲取用戶年齡最大的
User user3 = userList.stream().max((o1, o2) -> o1.getAge() < o2.getAge() ? o2.getAge() : o1.getAge()).get();
// reduce 比較 用戶年齡,除此之外 reduce 還可以對 數組集合進行操作計算
User user4 = userList.stream().reduce((a, w) -> a.getAge() > w.getAge() ? a: w).get();
// 跳過 前五個 元素
long count2 = userList.stream().skip(5).count();
// parallel 使用多線程處理
userList.stream().parallel().forEach(System.out::println);
// sequential 串行執行 主要和 上面多線程 結合使用
userList.stream().sequential().forEach(System.out::println);
System.out.println();
// unordered 返回一個無序流
userList.stream().sorted(Comparator.comparing(User::getName)).unordered().forEach(System.out::println);
}
/**
* 加載十條用戶信息
*
* @return userList
*/
private static List<User> loadInfo() {
List<User> userList = new LinkedList<>();
Random random = new Random();
for (int i = 0; i < 10; i++) {
userList.add(new User(names.get(i), i, random.nextInt(),
getRandomAddress(random.nextInt(10)), String.valueOf(random.nextLong())));
}
return userList;
}
/**
* 姓名
*/
private static final List<String> names = Stream.of("張三", "李四", "王五", "張可汗", "李白", "扁鵲", "馬可波羅", "上官婉兒", "太乙真人", "東皇太一").collect(Collectors.toList());
/**
* 自動生成地址(中文)
*
* @param len 長度
* @return name
*/
public static String getRandomAddress(int len) {
Random random = new Random();
StringBuilder ret = new StringBuilder();
for (int i = 0; i < len; i++) {
String str = "";
// 定義高低位
int height = (176 + Math.abs(random.nextInt(39))); // 獲取高位值
int lowPos = (161 + Math.abs(random.nextInt(93))); // 獲取低位值
byte[] b = {Integer.valueOf(height).byteValue(), Integer.valueOf(lowPos).byteValue()};
try {
str = new String(b, "GBK"); // 轉成中文
} catch (UnsupportedEncodingException ex) {
ex.printStackTrace();
}
ret.append(str);
}
return ret.toString();
}
}