概述
流(Stream):是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
“集合讲的是数据,流讲的是计算”
StreamAPI特点
① Stream 自己不会存储元素。
② Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③ Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
StreamAPI操作步骤
1. 创建Stream
//创建 Stream
@Test
public void test1(){
//1.Collection 提供了两个方法 stream() 与 parallelStream()
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
//2.通过 Arrays 中的 stream() 获取一个数组流
Employee[] employees = new Employee[10];
Stream<Employee> stream2 = Arrays.stream(employees);
//3. 通过 Stream 类中静态方法 of()
List<String> list1 = Arrays.asList("12", "as", "df");
Stream stream3 = Stream.of(list1);
//4. 创建无限流
//迭代
Stream stream4 = Stream.iterate(1, x -> x + 2);
stream4.limit(10).forEach(System.out::println);
//生成
Stream stream5 = Stream.generate(Math::random);
stream5.limit(5).forEach(System.out::println);
}
2. 一系列流水史的中间操作
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”
① 筛选与切片
方法 | 描述 |
filter(Predicate p) | 接收Lambda ,从流中排除某些元素 |
distinct() | 筛选,通过流所生成元素的hashCode() 和equals() 去除重复元素 |
limit(long maxSize) | 截断流,使其元素不超过给定数量 |
skip(long n) | 跳过元素,返回一个扔掉了前n 个元素的流。若流中元素不足n 个,则返回一个空流。与limit(n) 互补 |
//2. 中间操作
List<Employee> employees = Arrays.asList(
new Employee("张三", 20),
new Employee("李四", 50),
new Employee("王五", 40),
new Employee("王五", 40),
new Employee("王五", 40),
new Employee("赵六", 10),
new Employee("王琦", 8),
new Employee("李逵", 4)
);
/**
* 筛选与切片
* filter(Predicate p) :接收Lambda ,从流中排除某些元素。
* distinct() :筛选,通过流所生成元素的hashCode() 和equals() 去除重复元素
* limit(long maxSize):截断流,使其元素不超过给定数量。
* skip(long n):跳过元素,返回一个扔掉了前n 个元素的流。若流中元素不足n 个,则返回一个空流。与limit(n) 互补
*/
//内部迭代:由StreamAPI自己完成
//filter(Predicate p) :接收Lambda ,从流中排除某些元素。
@Test
public void test2() {
/*多个中间操作可以连接起来形成一个流水线,除非流水
线上触发终止操作,否则中间操作不会执行任何的处理!
而在终止操作时一次性全部处理,称为“惰性求值”*/
//中间操作:不会执行任何操作
Stream<Employee> s = employees.stream().filter((e) -> {
System.out.println("Stream API中间操作!");
return e.getAge() > 25;
});
//终止操作:一次性操作全部操作
s.forEach(System.out:: println);
}
//外部迭代
@Test
public void test3() {
Iterator<Employee> it = employees.iterator();
while (it.hasNext()) {
Employee next = it.next();
System.out.println(next);
}
}
//limit(long maxSize):截断流,使其元素不超过给定数量。
@Test
public void test4() {
employees.stream()
.filter(e -> e.getAge() > 30)
.limit(3)
.forEach(System.out:: println);
//运行结果
/*Employee{name='李四', age=50}
Employee{name='王五', age=40}
Employee{name='王五', age=40}*/
}
//skip(long n):跳过元素,返回一个扔掉了前n 个元素的流。若流中元素不足n 个,则返回一个空流。与limit(n) 互补
@Test
public void test5() {
employees.stream()
.filter(e -> e.getAge() > 30)
.skip(1)
.forEach(System.out:: println);
//运行结果
/*Employee{name='王五', age=40}
Employee{name='王五', age=40}
Employee{name='王五', age=40}*/
}
//distinct() :筛选,通过流所生成元素的hashCode() 和equals() 去除重复元素
@Test
public void test6() {
employees.stream()
.filter(e -> e.getAge() > 30)
.distinct()
.forEach(System.out:: println);
//运行结果
/*Employee{name='李四', age=50}
Employee{name='王五', age=40}*/
}
②映射
方法 | 描述 |
---|---|
map(Functionf) | 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素 |
mapToDouble(ToDoubleFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的DoubleStream |
mapToInt(ToIntFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的IntStream |
mapToLong(ToLongFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的LongStream |
flatMap(Function f) | 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流 |
/**
* 映射
* map(Functionf) :接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
* mapToDouble(ToDoubleFunction f) :接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的DoubleStream。
* mapToInt(ToIntFunction f) :接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的IntStream。
* mapToLong(ToLongFunction f) :接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的LongStream。
* flatMap(Function f) :接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
*/
@Test
public void test7() {
//map(Functionf) :接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
List<String> list = Arrays.asList("aaa", "bbb", "ccc");
list.stream()
.map(str -> str.toUpperCase())
.forEach(System.out:: print);//AAABBBCCC
System.out.println("-------------------------");
employees.stream()
.map(Employee:: getName)
.forEach(System.out:: print);//张三李四王五王五王五赵六王琦李逵
System.out.println("-------------------------");
//mapToInt(ToIntFunction f) :接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的IntStream。
IntStream is = employees.stream()
.mapToInt(Employee:: getAge);
is.forEach(System.out::println);
System.out.println("-------------------------");
//flatMap(Function f) :接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
list.stream()
.flatMap(TestStreamAPI1:: 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 comp) | 产生一个新流,其中按比较器顺序排序 |
/**
* 排序
* sorted() 产生一个新流,其中按自然顺序排序(Comparable)
sorted(Comparator comp) 产生一个新流,其中按比较器顺序排序( Comparator)
*/
@Test
public void test8(){
//sorted() 产生一个新流,其中按自然顺序排序(Comparable)
List<String> list = Arrays.asList("aaa", "eee", "ccc","yyy");
list.stream()
.sorted()
.forEach(System.out::print);//aaaccceeeyyy
System.out.println("-----------------");
//sorted(Comparator comp) 产生一个新流,其中按比较器顺序排序( Comparator)
employees.stream()
.sorted((e1,e2)->{
if (e1.getAge() == e2.getAge()){
return e1.getName().compareTo(e2.getName());
}else{
return Integer.compare(e1.getAge(), e2.getAge());
}
}).forEach(System.out::println);
}
3. 终止操作
①查找与匹配
方法 | 描述 |
---|---|
allMatch(Predicate p) | 检查是否匹配所有元素 |
anyMatch(Predicate p) | 检查是否至少匹配一个元素 |
noneMatch(Predicatep) | 检查是否没有匹配所有元素 |
findFirst() | 返回第一个元素 |
findAny() | 返回当前流中的任意元素 |
count() | 返回流中元素总数 |
max(Comparator c) | 返回流中最大值 |
min(Comparator c) | 返回流中最小值 |
forEach(Consumer c) | 内部迭代(使用Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮你把迭代做了) |
/**
* 查找与匹配
* allMatch(Predicate p) 检查是否匹配所有元素
anyMatch(Predicate p) 检查是否至少匹配一个元素
noneMatch(Predicatep) 检查是否没有匹配所有元素
findFirst() 返回第一个元素
findAny() 返回当前流中的任意元素
count() 返回流中元素总数
max(Comparator c) 返回流中最大值
min(Comparator c) 返回流中最小值
forEach(Consumer c) 内部迭代(使用Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮你把迭代做了)
*/
@Test
public void test9(){
boolean b1 = employees.stream()
.allMatch(e -> e.getAge() > 20);
System.out.println(b1);//false
boolean b2 = employees.stream()
.anyMatch(e -> e.getAge() > 20);
System.out.println(b2);//true
boolean b3 = employees.stream()
.noneMatch(e -> e.getAge() > 20);
System.out.println(b3);//false
Optional<Employee> op1 = employees.stream()
.findFirst();
System.out.println(op1.get());//Employee{name='张三', age=20}
//并行流与串行流
/*并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。
Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。
Stream API 可以声明性地通过parallel() 与sequential() 在并行流与顺序流之间进行切换。*/
Optional<Employee> op2 = employees.stream().parallel()
.findAny();
System.out.println(op2.get());//Employee{name='赵六', age=10}
Optional<Employee> op3 = employees.stream().sequential()
.findAny();
System.out.println(op3.get());//Employee{name='张三', age=20}
long num = employees.stream()
.count();
System.out.println(num);//8
Optional<Employee> op4 = employees.stream()
.max(Comparator.comparingInt(Employee:: getAge));
System.out.println(op4.get());//Employee{name='李四', age=50}
Optional<Employee> op5 = employees.stream()
.min(Comparator.comparingInt(Employee:: getAge));
System.out.println(op5.get());//Employee{name='李逵', age=4}
}
②归约
方法 | 描述 |
---|---|
reduce(T iden, BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回T |
reduce(BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回Optional<T> |
/**
* 归约
* reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回T
reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回Optional<T>
*/
@Test
public void test10(){
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
Integer sum = list.stream()
.reduce(0, (x, y) -> x + y);
System.out.println(sum); //21
System.out.println("-----------------------------");
Optional<Integer> op = employees.stream()
.map(Employee:: getAge)
.reduce(Integer:: sum);
System.out.println(op.get());//212
}
③收集
方法 | 描述 |
---|---|
collect(Collector c) | 将流转换为其他形式。接收一个Collector接口的 实现,用于给Stream中元素做汇总的方法 |
Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到List、Set、Map)。但是Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:
方法 | 返回类型 | 作用 |
---|---|---|
toList | List<T> | 把流中元素收集到List |
List<Employee>emps=list.stream().collect(Collectors.toList()); | ||
toSet | Set<T> | 把流中元素收集到Set |
Set<Employee>emps=list.stream().collect(Collectors.toSet()); | ||
toCollection | Collection<T> | 把流中元素收集到创建的集合 |
Collection<Employee>emps=list.stream().collect(Collectors.toCollection(ArrayList::new)); | ||
counting | Long | 计算流中元素的个数 |
long count=list.stream().collect(Collectors.counting()); | ||
summingInt | Integer | 对流中元素的整数属性求和 |
inttotal=list.stream().collect(Collectors.summingInt(Employee::getSalary)); | ||
averagingInt | Double | 计算流中元素Integer属性的平均值 |
doubleavg=list.stream().collect(Collectors.averagingInt(Employee::getSalary)); | ||
summarizingInt | IntSummaryStatistics | 收集流中Integer属性的统计值。如:平均值 |
IntSummaryStatisticsiss=list.stream().collect(Collectors.summarizingInt(Employee::getSalary)); | ||
joining | String | 连接流中每个字符串 |
Stringstr=list.stream().map(Employee::getName).collect(Collectors.joining()); | ||
maxBy | Optional<T> | 根据比较器选择最大值 |
Optional<Emp>max=list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary))); | ||
minBy | Optional<T> | 根据比较器选择最小值 |
Optional<Emp>min=list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary))); | ||
reducing | 归约产生的类型 | 从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值 |
inttotal=list.stream().collect(Collectors.reducing(0,Employee::getSalar,Integer::sum)); | ||
collectingAndThen | 转换函数返回的类型 | 包裹另一个收集器,对其结果转换函数 |
inthow=list.stream().collect(Collectors.collectingAndThen(Collectors.toList(),List::size)); | ||
groupingBy | Map<K,List<T>> | 根据某属性值对流分组,属性为K,结果为V |
Map<Emp.Status, List<Emp>> map= list.stream().collect(Collectors.groupingBy(Employee::getStatus)); | ||
partitioningBy | Map<Boolean,List<T>> | 根据true或false进行分区 |
Map<Boolean,List<Emp>>vd=list.stream().collect(Collectors.partitioningBy(Employee::getManage)); |
/**
* 收集
* collect(Collector c) 将流转换为其他形式。接收一个Collector接口实现,用于给Stream中元素做汇总的方法
*/
@Test
public void test11(){
List<String> list = employees.stream()
.map(Employee:: getName)
.collect(Collectors.toList());
list.forEach(System.out::print);//张三李四王五王五王五赵六王琦李逵
System.out.println("-----------------------");
Set<String> set = employees.stream()
.map(Employee:: getName)
.collect(Collectors.toSet());
set.forEach(System.out::print);//李四张三王琦王五赵六李逵
System.out.println("-----------------------");
HashSet hset = employees.stream()
.map(Employee:: getName)
.collect(Collectors.toCollection(HashSet::new));
hset.forEach(System.out::print);//李四张三王琦王五赵六李逵
}
@Test
public void test12(){
//总数
long count = employees.stream()
.collect(Collectors.counting());
System.out.println(count);//8
//平均值
Double avg = employees.stream()
.collect(Collectors.averagingInt(Employee::getAge));
System.out.println(avg);//26.5
//总和
int sum = employees.stream()
.collect(Collectors.summingInt(Employee:: getAge));
System.out.println(sum);//212
//最大值
Optional<Employee> max = employees.stream()
.collect(Collectors.maxBy((e1, e2) -> Integer.compare(e1.getAge(), e2.getAge())));
System.out.println(max.get());//Employee{name='李四', age=50}
//最小值
Optional<Integer> min = employees.stream()
.map(Employee:: getAge)
.collect(Collectors.minBy(Integer:: compare));
System.out.println(min.get());//4
}
//分组
@Test
public void test13(){
Map<String,List<Employee>> map = employees.stream()
.collect(Collectors.groupingBy(Employee:: getName));
System.out.println(map.toString());
/*{李四=[Employee{name='李四', age=50}],
张三=[Employee{name='张三', age=20}],
王琦=[Employee{name='王琦', age=8}],
王五=[Employee{name='王五', age=40}, Employee{name='王五', age=40}, Employee{name='王五', age=40}],
赵六=[Employee{name='赵六', age=10}],
李逵=[Employee{name='李逵', age=4}]}*/
}
//分区
@Test
public void test14() {
Map<Boolean, List<Employee>> map = employees.stream()
.collect(Collectors.partitioningBy((e) ->e.getAge() > 30));
System.out.println(map);
/*{
false=[Employee{name='张三', age=20},
Employee{name='赵六', age=10},
Employee{name='王琦', age=8},
Employee{name='李逵', age=4}],
true=[Employee{name='李四', age=50},
Employee{name='王五', age=40},
Employee{name='王五', age=40},
Employee{name='王五', age=50}]}*/
}
//summarizingInt IntSummaryStatistics 收集流中Integer属性的统计值。如:平均值
@Test
public void test15(){
DoubleSummaryStatistics ds = employees.stream()
.collect(Collectors.summarizingDouble(Employee:: getAge));
System.out.println(ds.getAverage());//27.75
System.out.println(ds.getCount());//8
System.out.println(ds.getMax());//50.0
System.out.println(ds.getMin());//4.0
System.out.println(ds.getSum());//222.0
}
//joining连接流中每个字符串
@Test
public void test16(){
String str = employees.stream()
.map(Employee:: getName)
.collect(Collectors.joining(",", "---", "==="));
System.out.println(str);//---张三,李四,王五,王五,王五,赵六,王琦,李逵===
}