面对某些表结构字段或者数据,没有达到上万条的情况下,对数据查询的处理,可以利用持久化框架(jdbc,mybatis,Hibernate)来处理数据返回到前端使用。
但是如果某些返回的结合利用到的查询条件很复杂,或者条件很零碎,这样会使得后端的处理数据的方法的数量上达到冗余或者重复利用率不高,所以这样在考虑程序执行速度上,我们可以在一个统一的方法中返回list集合,在此基础上就行对集合中所有数据的数据筛选。
stream()优点
无存储。stream不是一种数据结构,它只是某种数据源的一个视图,数据源可以是一个数组,Java容器或I/O channel等。
为函数式编程而生。对stream的任何修改都不会修改背后的数据源,比如对stream执行过滤操作并不会删除被过滤的元素,而是会产生一个不包含被过滤元素的新stream。
惰式执行。stream上的操作并不会立即执行,只有等到用户真正需要结果的时候才会执行。
映射
map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
List<String> list = Arrays.asList("a,b,c", "1,2,3");
//将每个元素转成一个新的且不带逗号的元素
Stream<String> s1 = list.stream().map(s -> s.replaceAll(",", ""));
s1.forEach(System.out::println); // abc 123
Stream<String> s3 = list.stream().flatMap(s -> {
//将每个元素转换成一个stream
String[] split = s.split(",");
Stream<String> s2 = Arrays.stream(split);
return s2;
});
数组所有元素,按某种规律计算:
List num = Arrays.asList(1,2,3,4,5);
List collect1 = num.stream().map(n -> n * 2).collect(Collectors.toList());
System.out.println(collect1); //[2, 4, 6, 8, 10]
去重
distinct
IntStream stream = IntStream.of(2, 3, 3, 5, 6, 6, 8);
stream.distinct().forEach(System.out::println);}}
输出:2 3 5 6 8
计数
count
IntStream stream = IntStream.of(2, 3, 3, 5, 6, 6, 8);
long total = stream.distinct().count();
System.out.println(total);
}}
输出:5
过滤
filter
Stream<String> stream = Stream.of("张三", "张五", "赵四", "王五", "张六");
//对Stream流中的元素进行过滤,只要姓张的人
Stream<String> stream2 = stream.filter((String name)->{return name.startsWith("张");});
stream2.forEach(name-> System.out.println(name));输出: 张三 张四 张五
减少
reduce()简介
Reduce
原意:减少,缩小- 根据指定的计算模型将Stream中的值计算得到一个最终结果
解释:reduce 操作可以实现从Stream中生成一个值,其生成的值不是随意的,而是根据指定的计算模型。比如,之前提到count、min和max方法,因为常用而被纳入标准库中。事实上,这些方法都是reduce操作。
reduce三个override的方法
reduce方法有三个override的方法:
Optional reduce(BinaryOperator accumulator);
T reduce(T identity, BinaryOperator accumulator);
<U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner);
公共集合
测试代码中的所有集合,都是该集合。
List<Person> javaProgrammers = new ArrayList<Person>() {
{
add(new Person("Elsdon", "Jaycob", "Java programmer", "male", 2000, 18));
add(new Person("Tamsen", "Brittany", "Java programmer", "female", 2371, 55));
add(new Person("Floyd", "Donny", "Java programmer", "male", 3322, 25));
add(new Person("Sindy", "Jonie", "Java programmer", "female", 35020, 15));
add(new Person("Vere", "Hervey", "Java programmer", "male", 2272, 25));
add(new Person("Maude", "Jaimie", "Java programmer", "female", 2057, 87));
add(new Person("Shawn", "Randall", "Java programmer", "male", 3120, 99));
add(new Person("Jayden", "Corrina", "Java programmer", "female", 345, 25));
add(new Person("Palmer", "Dene", "Java programmer", "male", 3375, 14));
add(new Person("Addison", "Pam", "Java programmer", "female", 3426, 20));
}
};
方式一reduce(BinaryOperator accumulator)
Optional<T> reduce(BinaryOperator<T> accumulator)
reduce(BinaryOperator<T> accumulator)方法需要一个函数式接口参数
,该函数式接口需要两个参数
,返回一个结果
(reduce中返回的结果会作为下次累加器计算的第一个参数),也就是累加器
,最终得到一个Optional对象
测试示例代码:
@Test
public void Test() {
int asInt = javaProgrammers.stream()
.mapToInt(Person::getSalary)//返回数值流,减少拆箱封箱操作,避免占用内存 IntStream
.reduce((x, y) -> x += y)// int
.getAsInt(); //return int
System.out.printf("方式一 reduce(BinaryOperator<T> accumulator) 求薪资测试结果:"+asInt);
/*解析:
1. reduce(BinaryOperator<T> accumulator) reduce方法接受一个函数,这个函数有两个参数
2. 第一个参数是上次函数执行的返回值(也称为中间结果),第二个参数是stream中的元素,这个函数把这两个值相加,得到的和会被赋值给下次执行这个函数的第一个参数
*注意:
1.第一次执行的时候第一个参数的值是Stream的第一个元素,第二个参数是Stream的第二个元素
2.方法返回值类型是Optional
*/
}
方式二reduce(T identity, BinaryOperator accumulator)
T reduce(T identity, BinaryOperator<T> accumulator);
与第一种变形相同的是都会接受一个BinaryOperator
函数接口,不同的是其会接受一个identity参数
,identity参数与Stream中数据同类型,相当于一个的初始值
,通过累加器accumulator迭代计算Stream中的数据
,得到一个跟Stream中数据相同类型的最终结果。
测试示例代码:
@Test
public void test1(){
int reduce = javaProgrammers.stream().mapToInt(Person::getSalary).reduce(10000, (x, y) -> x += y);
System.out.printf("方式二 reduce(T identity, BinaryOperator<T> accumulator) 求薪资测试结果:"+reduce);
/*注意:
* 1.与方式一相比设置了累加器的初始值,参数一(x)则不再是Stream中的第一个数据而是设置的初始值(10000)其他相同
*/
}
打印结果:
方式一 reduce(BinaryOperator<T> accumulator) 求薪资测试结果:57308
方式二 reduce(T identity, BinaryOperator<T> accumulator) 求薪资测试结果:67308 //初始值10000
方式三 reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner)
\<U\> U reduce(U identity,BiFunction\<U, ? super T, U\> accumulator,BinaryOperator\<U\> combiner);
我们先观察分析再次被改变的参数列表:
1. 第一个参数:返回实例u,传递你要返回的U类型对象的初始化实例u
2. 第二个参数:累加器accumulator,可以使用lambda表达式,声明你在u上累加你的数据来源t的逻辑,例如(u,t)->u.sum(t),此时lambda表达式的行参列表是返回实例u和遍历的集合元素t,函数体是在u上累加t
3. 第三个参数:参数组合器combiner,接受lambda表达式。
根据参数我们一步一步分析代码示例:
@Test
public void test2() {
ArrayList<Integer> accResult_ = Stream.of(1, 2, 3, 4)
//第一个参数,初始值为ArrayList
.reduce(new ArrayList<Integer>(),
//第二个参数,实现了BiFunction函数式接口中apply方法,并且打印BiFunction
new BiFunction<ArrayList<Integer>, Integer, ArrayList<Integer>>() {
@Override
public ArrayList<Integer> apply(ArrayList<Integer> acc, Integer item) {
acc.add(item);
System.out.println("item: " + item);
System.out.println("acc+ : " + acc);
System.out.println("BiFunction");
return acc;
}
//第三个参数---参数的数据类型必须为返回数据类型,改参数主要用于合并多个线程的result值
// (Stream是支持并发操作的,为了避免竞争,对于reduce线程都会有独立的result)
}, new BinaryOperator<ArrayList<Integer>>() {
@Override
public ArrayList<Integer> apply(ArrayList<Integer> acc, ArrayList<Integer> item) {
System.out.println("BinaryOperator");
acc.addAll(item);
System.out.println("item: " + item);
System.out.println("acc+ : " + acc);
System.out.println("--------");
return acc;
}
});
System.out.println("accResult_: " + accResult_);
System.out.println("------------------lambda优化代码-----------------");
ArrayList<Integer> newList = new ArrayList<>();
ArrayList<Integer> accResult_s = Stream.of(1,2,3,4)
.reduce(newList,
(acc, item) -> {
acc.add(item);
System.out.println("item: " + item);
System.out.println("acc+ : " + acc);
System.out.println("BiFunction");
return acc;
}, (acc, item) -> null);
System.out.println("accResult_s: " + accResult_s);
}
示例代码中,第一个参数是ArrayList
,在第二个函数参数中打印了“BiFunction”,而在第三个参数接口中打印了函数接口中打印了”BinaryOperator“.看下面的打印结果,只打印了“BiFunction”,而没有打印”BinaryOperator“,也就是说第三个函数参数并没有执行。分析参数时我们知道了该变形可以返回任意类型的数据
。
对于第三个函数参数,为什么没有执行,而且其参数必须为返回的数据类型?这是因为Stream是支持并发操作的,为了避免竞争,对于reduce线程都会有独立的result,combiner的作用在于合并每个线程的result得到最终结果。这也说明了了第三个函数参数的数据类型必须为返回数据类型了。
打印结果:
item: 1
acc+ : [1]
BiFunction
item: 2
acc+ : [1, 2]
BiFunction
item: 3
acc+ : [1, 2, 3]
BiFunction
item: 4
acc+ : [1, 2, 3, 4]
BiFunction
排序
sorted():它使用自然顺序对流中的元素进行排序。元素类必须实现Comparable接口。
sorted(Comparator<? super T> comparator):这里我们使用lambda表达式创建一个Comparator实例。我们可以按升序和降序对流元素进行排序。
(原文链接:https://blog.youkuaiyun.com/qq_31635851/article/details/111352643)
List<String> list = Arrays.asList("aa", "ff", "dd");
//String 类自身已实现Compareable接口
list.stream().sorted().forEach(System.out::println);// aa dd ff
Student s1 = new Student("aa", 10);
Student s2 = new Student("bb", 20);
Student s3 = new Student("aa", 30);
Student s4 = new Student("dd", 40);
List<Student> studentList = Arrays.asList(s1, s2, s3, s4);
//自定义排序:先按姓名升序,姓名相同则按年龄升序
studentList.stream().sorted(
(o1, o2) -> {
if (o1.getName().equals(o2.getName())) {
return o1.getAge() - o2.getAge();
} else {
return o1.getName().compareTo(o2.getName());
}
}
).forEach(System.out::println);
要反转自然顺序,Comparator提供reverseOrder()方法。
list.stream().sorted(Comparator.reverseOrder())
下面的代码行使用Comparator对列表进行排序。list.stream().sorted(Comparator.comparing(Student::getAge))
为了颠倒顺序,Comparator提供reversed()方法。
list.stream().sorted(Comparator.comparing(Student::getAge).reversed())
Comparator.comparing的应用
/** * Comparator.comparing 方法的使用 *
* comparing 方法接收一个 Function 函数式接口 ,通过一个 lambda 表达式传入
* */
employees.sort(Comparator.comparing(e -> e.getName()));
/** * 该方法引用 Employee::getName 可以代替 lambda表达式 */
employees.sort(Comparator.comparing(Employee::getName));
使用 Comparator.reversed 进行排序
返回相反的排序规则,
/**
* 相反的排序规则
*/
Collections.sort(employees, Comparator.comparing(Employee::getName).reversed());
employees.forEach(System.out::println);
输出,
Employee{name='Keith', age=35, salary=4000.0, mobile=3924401}
Employee{name='John', age=25, salary=3000.0, mobile=9922001}
Employee{name='Ace', age=22, salary=2000.0, mobile=5924001}
使用 Comparator.nullsFirst进行排序
当集合中存在null元素时,可以使用针对null友好的比较器,null元素排在集合的最前面
employees.add(null); //插入一个null元素
Collections.sort(employees, Comparator.nullsFirst(Comparator.comparing(Employee::getName)));
employees.forEach(System.out::println);
Collections.sort(employees, Comparator.nullsLast(Comparator.comparing(Employee::getName)));
employees.forEach(System.out::println);
使用 Comparator.thenComparing 排序
首先使用 name 排序,紧接着在使用ege 排序,来看下使用效果
Collections.sort(employees, Comparator.comparing(Employee::getAge).thenComparing(Employee::getName));
employees.forEach(System.out::println);
收集操作
原文链接:https://blog.youkuaiyun.com/y_k_y/article/details/84633001
collect:接收一个Collector实例,将流中元素收集成另外一个数据结构。
Collector<T, A, R> 是一个接口,有以下5个抽象方法:
Supplier<A> supplier():创建一个结果容器A
BiConsumer<A, T> accumulator():消费型接口,第一个参数为容器A,第二个参数为流中元素T。
BinaryOperator<A> combiner():函数接口,该参数的作用跟上一个方法(reduce)中的combiner参数一样,将并行流中各 个子进程的运行结果(accumulator函数操作后的容器A)进行合并。
Function<A, R> finisher():函数式接口,参数为:容器A,返回类型为:collect方法最终想要的结果R。
Set<Characteristics> characteristics():返回一个不可变的Set集合,用来表明该Collector的特征。有以下三个特征:
CONCURRENT:表示此收集器支持并发。(官方文档还有其他描述,暂时没去探索,故不作过多翻译)
UNORDERED:表示该收集操作不会保留流中元素原有的顺序。
IDENTITY_FINISH:表示finisher参数只是标识而已,可忽略。
Student s1 = new Student("aa", 10,1);
Student s2 = new Student("bb", 20,2);
Student s3 = new Student("cc", 10,3);
List<Student> list = Arrays.asList(s1, s2, s3);
//装成list
List<Integer> ageList = list.stream().map(Student::getAge).collect(Collectors.toList()); // [10, 20, 10]
//转成set
Set<Integer> ageSet = list.stream().map(Student::getAge).collect(Collectors.toSet()); // [20, 10]
//转成map,注:key不能相同,否则报错
Map<String, Integer> studentMap = list.stream().collect(Collectors.toMap(Student::getName, Student::getAge)); // {cc=10, bb=20, aa=10}
//字符串分隔符连接
String joinName = list.stream().map(Student::getName).collect(Collectors.joining(",", "(", ")")); // (aa,bb,cc)
//聚合操作
//1.学生总数
Long count = list.stream().collect(Collectors.counting()); // 3
//2.最大年龄 (最小的minBy同理)
Integer maxAge = list.stream().map(Student::getAge).collect(Collectors.maxBy(Integer::compare)).get(); // 20
//3.所有人的年龄
Integer sumAge = list.stream().collect(Collectors.summingInt(Student::getAge)); // 40
//4.平均年龄
Double averageAge = list.stream().collect(Collectors.averagingDouble(Student::getAge)); // 13.333333333333334
// 带上以上所有方法
DoubleSummaryStatistics statistics = list.stream().collect(Collectors.summarizingDouble(Student::getAge));
System.out.println("count:" + statistics.getCount() + ",max:" + statistics.getMax() + ",sum:" + statistics.getSum() + ",average:" + statistics.getAverage());
//分组
Map<Integer, List<Student>> ageMap = list.stream().collect(Collectors.groupingBy(Student::getAge));
//多重分组,先根据类型分再根据年龄分
Map<Integer, Map<Integer, List<Student>>> typeAgeMap = list.stream().collect(Collectors.groupingBy(Student::getType, Collectors.groupingBy(Student::getAge)));
//分区
//分成两部分,一部分大于10岁,一部分小于等于10岁
Map<Boolean, List<Student>> partMap = list.stream().collect(Collectors.partitioningBy(v -> v.getAge() > 10));
//规约
Integer allAge = list.stream().map(Student::getAge).collect(Collectors.reducing(Integer::sum)).get(); //40