目錄
一. 什么是流(Stream)
1.1 流的定義
1.2 流的特點
1.3?操作流
1.4 創(chuàng)建流
二. 流的中間操作
2.1 流的篩選與切片
2.1.1 filter
2.1.2 limit
2.1.3 skip
2.1.4 distinct
2.2 流的映射
2.2.1 map
2.2.2 flatMap
2.3 流的排序
2.3.1 sort
三. 流的終止操作
3.1 流的查找與匹配
3.1.1 allMatch
3.1.2 anyMatch
3.1.3 noneMatch
3.1.3 findFirst與findAny
3.1.4 count
3.1.5 max與min
3.2 歸約與收集
3.2.1 歸約(reduce)
3.2.2 收集(collect
??個人主頁:爪哇斗羅
??博主介紹:一名打工人
??簽名:圣人之道,為而不爭。
??一起交流,一起進(jìn)步,一起互動。
?
一. 什么是流(Stream)
1.1 流的定義
將原來的數(shù)組或者集合(數(shù)據(jù)源)通過一系列流水線的中間操作方式產(chǎn)生一個新的流,相當(dāng)于一個數(shù)據(jù)渠道。??
?
1.2 流的特點
?流的特點主要有兩條記住即可:
1. 集合是數(shù)據(jù),流是計算;
2. 流不改變數(shù)據(jù)源,也不做任何存儲。
1.3?操作流
操作流只需這三步:創(chuàng)建流,中間操作流,終止操作?。也就是對應(yīng)上圖中的解釋。
具體來說就是一個數(shù)據(jù)源獲取一個流,然后通過一個中間操作,對數(shù)據(jù)進(jìn)行處理后,最后一個終止操作產(chǎn)生一個結(jié)果。
1.4 創(chuàng)建流
創(chuàng)建流主要三種方式:了解即可,具體如何操作在后面會說到。
@Test
public void t3()
{
// 1.創(chuàng)建流 通過集合提供的stream()創(chuàng)建
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream(); ·
// 2.通過Arrays中的靜態(tài)方法stream()創(chuàng)建流
Integer[] integers = new Integer[10];
Stream<Integer> stream1 = Arrays.stream(integers);
// 3.Stream.of()創(chuàng)建
Stream<Integer> stream2 = Stream.of(1, 2, 4);
}
二. 流的中間操作
開始前,請準(zhǔn)備一個集合,集合可以自己隨便創(chuàng)建。
// 案例數(shù)據(jù) 姓名 年齡 薪水
List<Employee> employees = Arrays.asList(
new Employee("張三", 20, 9999.99),
new Employee("李四", 21, 5555.55),
new Employee("王五", 45, 4568.88),
new Employee("趙六", 56, 6666.66),
new Employee("田七",34 , 8888.88),
new Employee("周一", 19, 9999.99),
new Employee("吳用", 22, 6666.89),
new Employee("孫十",22 , 7878.56));
2.1 流的篩選與切片
2.1.1 filter
通過接收一個lambda表達(dá)式,根據(jù)定義中間過濾條件過濾流中的一些數(shù)據(jù)。
比如,過濾年齡大于21的員工。
public void filter()
{
List<Employee> employees = emp();
// 調(diào)用stream()方法創(chuàng)建一個流
// 通過中間操作filter去過濾年齡大于21的員工
// 并收集一個新的集合后打印。
List<Employee> collect = employees.stream().filter(e -> e.age > 21).collect(Collectors.toList());
collect.forEach(System.out::println);
}
?
練習(xí)(自己測試)
1. 過濾年齡大于21的并且薪水大于7000的員工。
2. 過濾年齡大于21的或者薪水大于7000的員工。
2.1.2 limit
流的截斷操作,使最后獲取的數(shù)據(jù)不超過所指定數(shù)量。
比如,先過濾年齡大于21的并且薪水大于7000的員工,只取前兩條數(shù)據(jù)。
public void limit(){
List<Employee> employees = emp();
// 調(diào)用stream()方法創(chuàng)建一個流
// 先過濾年齡大于21的并且薪水大于7000的員工,只取前兩條數(shù)據(jù)。
// 并收集一個新的集合后打印。
List<Employee> collect = employees.stream()
.filter(e -> e.age > 21 && e.salary>7000)
.limit(2)
.collect(Collectors.toList());
collect.forEach(System.out::println);
}
?
只要取到對應(yīng)數(shù)量的數(shù)據(jù),就會發(fā)生短路,之后的數(shù)據(jù)便不會獲取。
練習(xí)(自己測試)
1. 過濾年齡大于21的并且薪水大于7000的員工只獲取前0條數(shù)據(jù)。
2. 過濾年齡大于21的并且薪水大于7000的員工只獲取前10條數(shù)據(jù)。
2.1.3 skip
跳過前面n個數(shù)據(jù),獲取后面所有的數(shù)據(jù),與limit剛好互補。
比如,過濾年齡大于21的員工。并跳過前2個數(shù)據(jù)獲取后面的所有數(shù)據(jù)。
public void skip(){
List<Employee> employees = emp();
// 調(diào)用stream()方法創(chuàng)建一個流
// 過濾年齡大于21的員工。并跳過前2個數(shù)據(jù)獲取后面的所有數(shù)據(jù)。
// 并收集一個新的集合后打印。
List<Employee> collect = employees.stream()
.filter(e -> e.age > 21)
.skip(2)
.collect(Collectors.toList());
collect.forEach(System.out::println);
}
?
練習(xí)(自己測試)
1. 過濾年齡大于21的并且薪水大于7000的員工跳過前0條數(shù)據(jù)。
2. 過濾年齡大于21的并且薪水大于7000的員工跳過前10條數(shù)據(jù)。
2.1.4 distinct
對重復(fù)的對象元素進(jìn)行去重,它的原理是根據(jù)重寫對象的hashCode方法與equals方法進(jìn)行去重。
將之前的數(shù)據(jù)添加幾個重復(fù)的數(shù)據(jù):
// 案例數(shù)據(jù)
List<Employee> employees = Arrays.asList(
new Employee("張三", 20, 9999.99),
new Employee("李四", 21, 5555.55),
new Employee("王五", 45, 4568.88),
new Employee("趙六", 56, 6666.66),
new Employee("田七",34 , 8888.88),
new Employee("周一", 19, 9999.99),
new Employee("吳用", 22, 6666.89),
new Employee("吳用", 22, 6666.89),
new Employee("吳用", 22, 6666.89),
new Employee("吳用", 22, 6666.89),
new Employee("孫十",22 , 7878.56));
然后重寫員工的hashCode方法與equals方法。
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return age == employee.age && Double.compare(employee.salary, salary) == 0 && Objects.equals(name, employee.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age, salary);
}
使用distinct方法進(jìn)行元素去重:
public void distinct(){
List<Employee> employees = emp();
// 調(diào)用stream()方法創(chuàng)建一個流
// 過濾重復(fù)的元素。
// 并收集一個新的集合后打印。
List<Employee> collect = employees.stream()
.distinct()
.collect(Collectors.toList());
collect.forEach(System.out::println);
}
?
這是進(jìn)行對整個對象進(jìn)行去重的,若對某個字段進(jìn)行去重使用filter方法加上自定義方法。
// 自定義方法
<T> Predicate<T> distinctBykey(Function<? super T,?> key){
Map<Object,Boolean> booleanMap = new ConcurrentHashMap<>();
return t-> booleanMap.putIfAbsent(key.apply(t),Boolean.TRUE)==null;
}
public void distinct(){
List<Employee> employees = emp();
// 調(diào)用stream()方法創(chuàng)建一個流
// 過濾重復(fù)的薪水。
// 并收集一個新的集合后打印。
List<Employee> collect = employees.stream()
.filter(distinctBykey(s->s.getSalary()))
.collect(Collectors.toList());
collect.forEach(System.out::println);
}
?
2.2 流的映射
2.2.1 map
接收一個lambda表達(dá)式,將對象中的元素轉(zhuǎn)換成其它形式或者提取對象中的元素。
然后,接收一個函數(shù)作為參數(shù),該函數(shù)會應(yīng)用到每一個對象元素上,并映射成為新的對象元素。
比如,將對象中的所有英文元素轉(zhuǎn)為大寫。
@Test
public void test01(){
// 構(gòu)建一個集合
List<String> strings = Arrays.asList("aaa","bbb","ccc","ddd");
// 將集合中的元素進(jìn)行大寫
List<String> collects = strings.stream().map(str -> str.toUpperCase()).collect(Collectors.toList());
// 輸出
collects.forEach(System.out::println);
}
?
比如,將員工對象中的年齡抽取出來并去重。
@Test
public void test02(){
List<Employee> employees = emp();
// 將年齡提取出來,并去重。
employees.stream().map(Employee::getAge)
.distinct()
.forEach(System.out::println);
}
?
2.2.2 flatMap
流的扁平化處理,接收一個函數(shù)作為參數(shù),將流中的每一個值都換成另一個流,然后將所有的流連接成一個流。
首先我們看看之前的map操作:
@Test
public void t4()
{
// 構(gòu)建集合
List<String> list = Arrays.asList("hello","world");
// map 將一個個流加入流
List<String[]> collect = list.stream().map(s -> s.split("")).collect(Collectors.toList());
collect.forEach(System.out::println);
}
?
看代碼可以看出它返回的是一個集合,集合里面是一個數(shù)組,map操作是針對于單層式的操作,也就是說只能對外層進(jìn)行處理。
所以,它輸出的就是兩個數(shù)組對象是這種格式的【[h,e,l,l,o],[w,o,r,l,d]】。
[h,e,l,l,o]作為一個流,[w,o,r,l,d]作為一個流,然后將兩者按照流的方式逐個放入一個新流里。
如果要輸出成這種格式的[h,e,l,l,o,w,o,r,l,d]就需要使用flatMap進(jìn)行雙層式的處理。
這種方式就是先將各個流拆成最小元素,然后將元素分別放入一個流中。
@Test
public void t4()
{
List<String> list = Arrays.asList("hello","world");
// 使用flapMap將流中的每一個值都換成另一個流,然后將所有的流連接成一個流。
Stream<String> stringStream = list.stream().map(s -> s.split("")).flatMap(Arrays::stream);
stringStream.forEach(System.out::println);
}
?
2.3 流的排序
2.3.1 sort
流的排序分為以下兩種:
自然排序: 默認(rèn)的字典方式的排序
@Test
public void t5()
{
List<String> list = Arrays.asList("ccc","aaa","eee","ddd");
// 默認(rèn)字典排序
list.stream().sorted().forEach(System.out::println);
}
?
自定義排序: 根據(jù)自己定義的規(guī)則排序
比如,若年齡一樣,按照薪水排序,否則按照年齡排序。
@Test
public void t5()
{
List<Employee> employees = emp();
// 自定義排序
employees.stream().sorted((e1,e2)->{
// 若年齡一樣,按照薪水排序
if (e1.getAge()==e2.getAge()){
return (int) (e1.getSalary()-(e2.getSalary()));
// 否則按照年齡排序
}else{
return e1.getAge()-e2.getAge();
}
}).forEach(System.out::println);
}
?
三. 流的終止操作
3.1 流的查找與匹配
3.1.1 allMatch
用于檢查是否匹配所有的元素,全部匹配成功返回true。
現(xiàn)在將員工信息修改如下:
// 案例數(shù)據(jù)
List<Employee> employees = Arrays.asList(
new Employee("張三", 22, 9999.99),
new Employee("李四", 22, 5555.55),
new Employee("王五", 22, 4568.88),
new Employee("趙六", 22, 6666.66),
new Employee("田七",22, 8888.88),
new Employee("周一", 22, 9999.99),
new Employee("吳用", 22, 6666.89),
new Employee("吳用", 22, 6666.89),
new Employee("吳用", 22, 6666.89),
new Employee("吳用", 22, 6666.89),
new Employee("孫十",22, 7878.56));
return employees;
匹配年齡是否全部為22。
@Test
public void t7()
{
List<Employee> employees = emp();
// 匹配年齡是否全部為22
boolean allMatch = employees.stream().allMatch(e -> e.age == 22);
// 全部匹配到返回true
System.out.println(allMatch);
}
?
3.1.2 anyMatch
用于檢查元素是否匹配到流中的任意元素,匹配到一個就成功返回true。
查找員工薪水是否有超過9000的。
@Test
public void t8()
{
List<Employee> employees = emp();
// 查找員工薪水是否有超過9000的
boolean anyMatch = employees.stream().anyMatch(e -> e.salary > 9000);
// 部分匹配到則返回true
System.out.println(anyMatch);
}
?
3.1.3 noneMatch
全部沒有匹配到元素返回true。
查找員工薪水是否有超過10000的。
@Test
public void t10()
{
List<Employee> employees = emp();
// 查找員工薪水是否有超過10000的
boolean noneMatch = employees.stream().noneMatch(e -> e.salary > 10000);
// 沒有匹配到則返回true
System.out.println(noneMatch);
}
?
3.1.3 findFirst與findAny
findFirst用于查找流中的第一個元素,對于元素處理的流是串行流。
findAny用于查找流中的任意一個元素,對于元素處理的流是并行流。
例如:查找員工薪水大于6000的并取第一個元素與查找員工薪水大于6000的并取任意一個元素
@Test
public void t11()
{
List<Employee> employees = emp();
// 查找員工薪水大于6000的并取第一個元素
System.out.println(employees.stream().filter(e->e.getSalary()>6000).findFirst().get());
// 查找員工薪水大于6000的并取任意一個元素
System.out.println(employees.parallelStream().filter(e->e.getSalary()>6000).findAny().get());
}
?
3.1.4 count
統(tǒng)計流中的元素總個數(shù)。
例如,統(tǒng)計所有員工。
@Test
public void t12()
{
List<Employee> employees = emp();
// 統(tǒng)計流中的元素總個數(shù)
long count = employees.stream().count();
System.out.println(count);
}
?
3.1.5 max與min
max與min分別是獲取流中的最大值與最小值。
@Test
public void t14()
{
List<Employee> employees = emp();
// 統(tǒng)計員工薪水最高的
Optional<Employee> max = employees.stream().max(Comparator.comparingDouble(Employee::getSalary));
System.out.println(max.get());
// 統(tǒng)計員工薪水最低的
Optional<Employee> min = employees.stream().min(Comparator.comparingDouble(Employee::getSalary));
System.out.println(min.get());
}
?
3.2 歸約與收集
3.2.1 歸約(reduce)
可以將流中的元素反復(fù)結(jié)合起來,結(jié)成一個值。
例如,構(gòu)建一個集合,求出它們的和。
@Test
public void t15()
{
// 構(gòu)建一個集合
List<Integer> integers = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
// 進(jìn)行求和
Integer reduce = integers.stream().reduce(0, (x, y) -> x + y);
System.out.println(reduce);
}
?
3.2.2 收集(collect)
集合收集
將流轉(zhuǎn)換為其他形式,接收一個Collector接口的實現(xiàn),用于給Stream中元素做匯總的方法。
可以將最終的結(jié)果收集成一個List集合,Set集合等,也可以將得到的結(jié)果進(jìn)行計算。
看下面這個例子:利用Collectors.toSet()進(jìn)行去重。
@Test
public void test01(){
// 構(gòu)建一個集合
List<String> strings = Arrays.asList("aaa","bbb","ccc","ddd","ddd");
// 將集合中的元素進(jìn)行大寫
Set<String> collects = strings.stream().map(str -> str.toUpperCase()).collect(Collectors.toSet());
// 輸出
collects.forEach(System.out::println);
}
?
分組操作
接下來著重介紹一下 ,使用Collectors.groupingBy進(jìn)行分組操作。
先準(zhǔn)備一組數(shù)據(jù):使用其分組操作將年齡進(jìn)行分組。文章來源:http://www.zghlxwxcb.cn/news/detail-821971.html
List<Employee> emp() {
// 案例數(shù)據(jù)
List<Employee> employees = Arrays.asList(
new Employee("張三", 22, 9999.99),
new Employee("李四", 22, 5555.55),
new Employee("王五", 23, 4568.88),
new Employee("趙六", 23, 6666.66),
new Employee("田七",24, 8888.88),
new Employee("周一", 24, 9999.99),
new Employee("吳用", 25, 6666.89),
new Employee("吳用1", 25, 6666.89),
new Employee("吳用2", 21, 6666.89),
new Employee("吳用3", 21, 6666.89),
new Employee("孫十",21, 7878.56));
return employees;
}
@Test
public void t17()
{
List<Employee> employees = emp();
// 返回一個map集合
Map<Integer, List<Employee>> collect = employees.stream().collect(Collectors.groupingBy(Employee::getAge));
System.out.println(collect);
}
?文章來源地址http://www.zghlxwxcb.cn/news/detail-821971.html
到了這里,關(guān)于帶你走進(jìn)Java8新特性Stream流的小世界的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!