目录
- 谷粒商城知识点补充
- 前言
- 1 java8新特性之lambda表达式
- 2 Stream API
-
- 2.1 概述
- 2.2 创建
- 2.3 中间操作
- 2.4 终止操作
-
- 2.4.1 匹配与查找
- 2.4.2 归约
- 2.4.3 收集
-
- 1)、Collectors.toList()
- 2)、Collectors.toSet()
- 3)、Collectors.toCollection(HashSet::new)
- 4)、Collectors.maxBy()
- 5)、Collectors.minBy()
- 6)、Collectors.summingDouble()
- 7)、Collectors.averagingDouble()
- 8)、Collectors.counting()
- 9)、Collectors.summarizingDouble()
- 10)、Collectors.groupingBy()
- 11)、Collectors.groupingBy()
- 12)、Collectors.partitioningBy()
- 13)、Collectors.joining()
- 3 异步和线程池
- 4 消息中间件 RabbitMQ
- 5 性能压测
- 6 接口幂等性
- 7 ElasticSearch
- 8 分布式事务
- 9 SpringCloud Alibaba-Sentinel
谷粒商城知识点补充
前言
在学习谷粒商城项目中,会遇到一些知识已经学过但是没有使用就忘记的,或者是没有学过的,这篇文章就是将谷粒商城中所有不懂的知识点进行一个补充和完善的。
1 java8新特性之lambda表达式
1.1 为什么使用
Lambda是一个 匿名函数 ,我们可以把 Lambda 表达式理解为是 一段可以传递的代码 (将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使 Java 的语言表达能力得到了提升。
1.2 从匿名类到Lambda的转换
1.3 语法
- -> :lambda操作符 或 箭头操作符
- -> 左边: lambda形参列表(其实就是接口中的抽象方法的形参)
- -> 右边: lambda体(其实就是重写的抽象方法的方法体)
总结
-> 左边: lambda形参列表的参数类型可以省略(类型推断),如果形参列表只有一个参数,其一对()也可以省略
-> 右边: lambda体应该使用一对{}包裹;如果lambda体只执行一条语句(可能是return语句),可以省略这一对{}和return关键字
2 Stream API
2.1 概述
2.2 创建
2.2.1 通过集合
//创建 Stream方式一:通过集合
@Test
public void test1(){
List<Employee> employees = EmployeeData.getEmployees();
// default Stream<E> stream() : 返回一个顺序流
Stream<Employee> stream = employees.stream();
// default Stream<E> parallelStream() : 返回一个并行流
Stream<Employee> parallelStream = employees.parallelStream();
}
2.2.2 通过数组
//创建 Stream方式二:通过数组
@Test
public void test2(){
int[] arr = new int[]{
1,2,3,4,5,6};
//调用Arrays类的static <T> Stream<T> stream(T[] array): 返回一个流
IntStream stream = Arrays.stream(arr);
Employee e1 = new Employee(1001,"Tom");
Employee e2 = new Employee(1002,"Jerry");
Employee[] arr1 = new Employee[]{
e1,e2};
Stream<Employee> stream1 = Arrays.stream(arr1);
}
2.2.3 通过Stream的of()
//创建 Stream方式三:通过Stream的of()
@Test
public void test3(){
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
}
2.2.4 创建无限流
//创建 Stream方式四:创建无限流
@Test
public void test4(){
// 迭代
// public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
//遍历前10个偶数
Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);
// 生成
// public static<T> Stream<T> generate(Supplier<T> s)
Stream.generate(Math::random).limit(10).forEach(System.out::println);
}
2.3 中间操作
多个中间操作 可以连接起来形成一个 流水线 ,除非流水线上触发终止操作,否则 中间操作不会执行任何的处理 !而在 终止操作时一次性全部处理,称为“惰性求值”。
2.3.1 筛选与切片
1)、filter
接收 Lambda,从流中排除某些元素
// (1)、filter——接收 Lambda , 从流中排除某些元素。
@Test
public void testFilter() {
//这里加入了终止操作 ,不然中间操作一系列不会执行
//中间操作只有在碰到终止操作才会执行
emps.stream()
.filter((e)->e.getAge()>18)
.forEach(System.out::println);//终止操作
}
注意:这里filter主要是过滤一些条件,这里的话就是把年龄小于18岁的Employee对象给过滤掉,然后用forEach给遍历一下,因为中间操作只有碰到终止操作才会执行,不然的话,看不到过滤效果。以下的操作都大部分都forEach了一下,为方便看到效果。filter用的还是很多的.
2)、limit
截断流,使其元素不超过给定数量
// (2)、limit——截断流,使其元素不超过给定数量。
@Test
public void testLimit() {
emps.stream()
.filter((e)->e.getAge()>8)
.limit(4)//跟数据库中的limit有异曲同工之妙
.forEach(System.out::println);//终止操作
}
注意:这里用了上面的filter跟limit,代码意思是:过滤掉年龄小于8的,只要4条数据。这种".“式操作很有意思,就是中间操作都可以一直”.",直到得到你想要的要求。
3)、skip(n)
跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n) 互补
// (3)、skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
@Test
public void testSkip() {
emps.stream()
.filter((e)->e.getAge()>8)
.skip(2)//这里可以查找filter过滤后的数据,前两个不要,要后面的,与limit相反
.forEach(System.out::println);//终止操作
}
注意:这里同样使用了filter中间操作,也可以不用,代码意思是:过滤掉年龄小于8岁的employee对象,然后前两个对象不要,只要后面的对象。跟limit意思相反。
4)、distinct
筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
// (4)、distinct——筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
@Test
public void testDistinct() {
emps.stream()
.distinct()//去除重复的元素,因为通过流所生成元素的 hashCode() 和 equals() 去除重复元素,所以对象要重写hashCode跟equals方法
.forEach(System.out::println);//终止操作
}
注意:distinct,去除重复的元素,因为通过流所生成元素的 hashCode() 和 equals() 去除重复元素,所以对象要重写hashCode跟equals方法,我在Employee对象中重写了这两个方法。
2.3.2 映 射
1)、map
接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
2)、flatMap
接收一个函数作为参数,将流中的每个值都转换成另一个流,然后把所有流连接成一个流。
// map-接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
@Test
public void testMapAndflatMap() {
List<String> list=Arrays.asList("aaa","bbb","ccc","ddd");
list.stream()
.map((str)->str.toUpperCase())//里面是Function
.forEach(System.out::println);
System.out.println("----------------------------------");
//这里是只打印名字,map映射,根据Employee::getName返回一个name,映射成新的及结果name
emps.stream()
.map(Employee::getName)
.forEach(System.out::println);
System.out.println("======================================");
//流中流
Stream<Stream<Character>> stream = list.stream()
.map(StreamAPI::filterCharacter);
//{
{a,a,a},{b,b,b}}
//map是一个个流(这个流中有元素)加入流中
stream.forEach(sm->{
sm.forEach(System.out::println);
});
System.out.println("=============引进flatMap=============");
// 只有一个流
Stream<Character> flatMap = list.stream()
.flatMap(StreamAPI::filterCharacter);
//flatMap是将一个个流中的元素加入流中
//{a,a,a,b,b,b}
flatMap.forEach(System.out::println);
}
/**
* 测试map跟flatMap的区别
* 有点跟集合中的add跟addAll方法类似
* add是将无论是元素还是集合,整体加到其中一个集合中去[1,2,3.[2,3]]
* addAll是将无论是元素还是集合,都是将元素加到另一个集合中去。[1,2,3,2,3]
* @param str
* @return
*/
public static Stream<Character> filterCharacter(String str){
List<Character> list=new ArrayList<>();
for (Character character : str.toCharArray()) {
list.add(character);
}
return list.stream();
}
注意:map跟flatMap还是有区别的,map是一个个流(这个流中有元素)加入流中,flatMap是将一个个流中的元素加入流中.
2.3.2 排序
1)、sorted()
自然排序(Comparable)
2)、sorted(Comparator com)
定制排序(Comparator)
@Test
public void testSorted() {
//自然排序
List<String> list=Arrays.asList("ccc","aaa","bbb","ddd","eee");
list.stream()
.sorted()
.forEach(System.out::println);
System.out.println("=======定制排序=========");
//定制排序
emps.stream()
.sorted((x, y) -> {
if(x.getAge() == y.getAge()){
return x.getName().compareTo(y.getName());
}else{
return Integer.compare(x.getAge(), y.getAge());
}
}).forEach(System.out::println);
}
2.4 终止操作
- 终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如: List 、 Integer ,甚至是 void 。
- 流进行了终止操作后,不能再次使用。
2.4.1 匹配与查找
1)、allMatch
检查是否匹配所有元素
System.out.println("==========allMatch==============");
boolean allMatch = emps.stream()
.allMatch((e)->e.getStatus().equals(Status.BUSY));
System.out.println(allMatch);
2)、anyMatch
检查是否至少匹配一个元素
System.out.println("==========anyMatch==============");
boolean anyMatch = emps.stream()
.anyMatch((e)->e.getAge()>10);
System.out.println(anyMatch);
3)、noneMatch
检查是否没有匹配的元素
System.out.println("==========noneMatch==============");
boolean noneMatch = emps.stream()
.noneMatch((e)->e.getStatus().equals(Status.BUSY));
System.out.println(noneMatch);
4)、findFirst
返回第一个元素
System.out.println("==========findFirst==============");
Optional<Employee2> findFirst = emps.stream()
.sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))//按照工资排序并输出第一个
.findFirst();
System.out.println(findFirst);
5)、findAny
返回当前流中的任意元素
System.out.println("==========findAny==============");
Optional<Employee2> findAny = emps.stream()
.filter((e)->e.getStatus().equals(Status.BUSY))
.findAny();
System.out.println(findAny);
6)、count
返回流中元素的总个数
System.out.println("==========count==============");
long count = emps.stream()
.count();
System.out.println(count);
7)、max
返回流中最大值
System.out.println("==========max==============");
Optional<Double> max = emps.stream()
.map(Employee2::getSalary)
.max(Double::compare);
System.out.println(max);
8)、min
返回流中最小值
System.out.println("==========min==============");
Optional<Employee2> min = emps.stream()
.min((e1,e2)->Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(min);
9)、forEach
在中间操作代码处,已经多次使用了,这里不再赘述。
2.4.2 归约
1)、reduce
reduce(T identity, BinaryOperator) / reduce(BinaryOperator)
可以将流中元素反复结合起来,得到一个值
@Test
public void testReduce() {
List<Integer> list= Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum = list.stream()
.reduce(0,(x,y)->x+y);
System.out.println(sum);
Optional<Double> reduce = emps.stream()
.map(Employee2::getSalary)
.reduce(Double::sum);
System.out.println(reduce.get());
}
2.4.3 收集
Collector接口中方法的实现决定了如何对流执行收集操作(如收集到List、Set、Map)。但是Collectors实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:
1)、Collectors.toList()
List<String> collect = emps.stream()
.map(Employee2::getName)
.collect(Collectors.toList());
collect.forEach(System.out::println);
2)、Collectors.toSet()
Set<String> collect2 = emps.stream()
.map(Employee2::getName)
.collect(Collectors.toSet());
collect2.forEach(System.out::println);
3)、Collectors.toCollection(HashSet::new)
HashSet<String> collect3 = emps.stream()
.map(Employee2::getName)
.collect(Collectors.toCollection(HashSet::new));
collect3.forEach(System.out::println);
4)、Collectors.maxBy()
Optional<Double> collect = emps.stream()
.map(Employee2::getSalary)
.collect(Collectors.maxBy(Double::compare));
System.out.println(collect.get());
Optional<Employee2> collect2 = emps.stream()
.collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
System.out.println(collect2.get());
5)、Collectors.minBy()
Optional<Double> collect4 = emps.stream()
.map(Employee2::getSalary)
.collect(Collectors.minBy(Double::compare));
System.out.println(collect4);
Optional<Employee2> collect3 = emps.stream()
.collect(Collectors.minBy((e1,e2)->Double.compare(e1.getSalary(),e2.getSalary())));
System.out.println(collect3.get());
6)、Collectors.summingDouble()
Double collect5 = emps.stream()
.collect(Collectors.summingDouble(Employee2::getSalary));
System.out.println(collect5);
7)、Collectors.averagingDouble()
Double collect6 = emps.stream()
.collect(Collectors.averagingDouble((e)->e.getSalary()));
Double collect7 = emps.stream()
.collect(Collectors.averagingDouble(Employee2::getSalary));
System.out.println("collect6:"+collect6);
System.out.println("collect7:"+collect7);
8)、Collectors.counting()
//总数
Long collect8 = emps.stream()
.collect(Collectors.counting());
System.out.println(collect8);
9)、Collectors.summarizingDouble()
DoubleSummaryStatistics collect9 = emps.stream()
.collect(Collectors.summarizingDouble(Employee2::getSalary));
long count = collect9.getCount();
double average = collect9.getAverage();
double max = collect9.getMax();
double min = collect9.getMin();
double sum = collect9.getSum();
System.out.println("count:"+count);
System.out.println("average:"+average);
System.out.println("max:"+max);
System.out.println("min:"+min);
System.out.println("sum:"+sum);
10)、Collectors.groupingBy()
//分组
@Test
public void testCollect3() {
Map<Status, List<Employee2>> collect = emps.stream()
.collect(Collectors.groupingBy((e)->e.getStatus()));
System.out.println(collect);
Map<Status, List<Employee2>> collect2 = emps.stream()
.collect(Collectors.groupingBy(Employee2::getStatus));
System.out.println(collect2);
}
11)、Collectors.groupingBy()
//多级分组
@Test
public void testCollect4() {
Map<Status, Map<String, List<Employee2>>> collect = emps.stream()
.collect(Collectors.groupingBy(Employee2::getStatus, Collectors.groupingBy((e)->{
if(e.getAge() >= 60)
return "老年";
else if(e.getAge() >= 35)
return "中年";
else
return "成年";
})));
System.out.println(collect);
}
12)、Collectors.partitioningBy()
//多级分组
@Test
public void testCollect4() {
Map<Status, Map<String, List<Employee2>>> collect = emps.stream()
.collect(Collectors.groupingBy(Employee2::getStatus, Collectors.groupingBy((e)->{
if(e.getAge() >= 60)
return "老年";
else if(e.getAge() >= 35)
return "中年";
else
return "成年";
})));
System.out.println(collect);
}
13)、Collectors.joining()
//组接字符串
@Test
public void testCollect6() {
String collect = emps.stream()
.map((e)->e.getName())
.collect(Collectors.joining());
System.out.println(collect);
String collect3 = emps.stream()
.map(Employee2::getName)
.collect(Collectors.joining(","));
System.out.println(collect3);
String collect2 = emps.stream()
.map(Employee2::getName)
.collect(Collectors.joining(",", "prefix", "subfix"));
System.out.println(collect2);
}
@Test
public void testCollect7() {
Optional<Double> collect = emps.stream()
.map(Employee2::getSalary)
.collect(Collectors.reducing(Double::sum));
System.out.println(collect.get());
}
3 异步和线程池
3.1 线程回顾
3.1.1 初始化线程的4 种方式
1)、继承Thread
2)、实现Runnable 接口
3)、实现Callable 接口+ FutureTask (可以拿到返回结果,可以处理异常)
4)、线程池
方式1 和方式2:主进程无法获取线程的运算结果。不适合当前场景
方式3:主进程可以获取线程的运算结果,但是不利于控制服务器中的线程资源。可以导致
服务器资源耗尽。
方式4:通过如下两种方式初始化线程池
Executors.newFiexedThreadPool(3);
//或者
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit unit,
workQueue, threadFactory, handler);
通过线程池性能稳定,也可以获取执行结果,并捕获异常。但是,在业务复杂情况下,一
个异步调用可能会依赖于另一个异步调用的执行结果。
public static class Thread01 extends Thread{
@Override
public void run() {
System.out.println("当前线程:" + Thread.currentThread().getId());
int i = 10 / 2;
System.out.println("运行结果:" + i);
}
}
public static class Runnable01 implements Runnable{
@Override
public void run() {
System.out.println("当前线程:" + Thread.currentThread().getId());
int i = 10 / 2;
System.out.println("运行结果:" + i);
}
}
public static class Callable01 implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("当前线程:" + Thread.currentThread().getId());
int i = 10 / 2;
System.out.println("运行结果:" + i);
return i;
}
}
/** * 1)、继承Thread * Thread01 thread = new Thread01(); * thread.start();//启动线程 * 2)、实现Runnable 接口 * Runnable01 runnable01 = new Runnable01(); * new Thread(runnable01).start(); * 3)、实现Callable 接口+ FutureTask (可以拿到返回结果,可以处理异常) * FutureTask<Integer> futureTask = new FutureTask<>(new Callable01()); * new Thread(futureTask).start(); * //阻塞等待整个线程执行完成,获取返回结果 * Integer integer = futureTask.get(); * 4)、线程池【ExecutorService】 * 给线程池直接提交任务。 * service.execute(new Runnable01()); * 1、创建: * 1)、Executors * 2)、new ThreadPoolExecutor * * Future:可以获取到异步结果 * *区别: * 1、2不能得到返回值,3可以获取返回值。 * 1、2、3都不能控制资源 * 4可以控制资源,性能