1、 HashMap 加入了红黑树
2、ConcurrentHashMap 使用了CAS无锁机制
3、 永久区没有了,成了元空间(MetaSpace)。相对于方法区,直接使用的物理内存!相应的PremGenSiz 、 MaxPremGenSize 参数失效了 取而代之的是:MetaSpaceSize MaxMetaSpaceSize
4、Stream Api 和 Lambda表达式
关于Lambda表达式:https://www.cnblogs.com/toov5/p/10583620.html
它是个匿名函数,可以把它表达式理解为是一段可以传递的代码(将代码像数据一样传递)
//箭头表达式 -> 左边表示方法的参数列表 右边表示实现 !
public interface MyInterface { public boolean bigger(T t); }
语法引申:
如果是没有返回值的方法
()-> System.out.println("toov5");
其实Lambda就是简化了 匿名函数 提取了关键代码而已
对 Consumer接口中 Accept方法的实现
public class test111 { public static void main(String[] args) { Consumer<String> s = (x)-> System.out.println(x); //有参数 无返回值的接口方法 s.accept("toov5"); } }
若只有一个参数 小括号可以省略:
x-> System.out.println(x)
有两条参数并且多条语句:
public class test111 { public static void main(String[] args) { Comparator<Integer> com = (x, y) ->{ System.out.println("toov5第一条语句"); return Integer.compare(x, y); }; } }
如果只有一条语句 大括号{} 以及return可以省略
Lambda表达式的参数列表的数据类型可以不写,JVM编译器可以通过上下文推断。
总结:
左右遇一括号省
左侧推断类型省
Lambda表达式需要 函数式接口的支持: 接口中只有一个抽象方法的接口。
改造成Lambda表达式: 就是函数式接口改成 (参数) -> {实现}
定义一个函数式接口:
public interface myFun { Integer getValue(Integer x); }
使用:
public class test { @Test public void test1(){ Integer result = operation(10, x -> x + 90 ); System.out.println(result); } public Integer operation(Integer num, myFun myFun){ return myFun.getValue(num); } }
练习题1:
调用Collection.sort() 方法,通过定制排序比价两个Employee(先按照年龄比,年龄相同按照姓名比),使用Lambda作为参数传递。
public class test22 { List<Employee> employees = Arrays.asList( new Employee(1, "11"), new Employee(2, "22"), new Employee(3, "33"), new Employee(4, "44")); @Test public void test11(){ Collections.sort(employees , (e1,e2)->{ if (e1.getNumber() == e2.getNumber()){ return e1.getName().compareTo(e2.getName()); }else return Integer.compare(e1.getNumber(), e2.getNumber()); }); for (Employee e : employees){ System.out.println(e); } } }
练习2:
(1) 声明函数式接口,接口中声明抽象方法, public String getValue(String str)
(2) 声明类TestLambda, 类中编写方法使用接口作为参数,将一个字符串转成大写。并作为方法的返回值
(3) 再将一个字符串的第二个和第四个索引位置进行截取子串
像数据一样传递:
函数式接口:
public interface myFun<T> { T getValue(T x); }
使用:
@Test public void test2(){ String trimResult = strHalder("java is Toov5", s -> s.trim() ); System.out.println(trimResult); String upperResult = strHalder("adsfsdf", s -> s.toUpperCase()); System.out.println(upperResult); String subResult = strHalder("java is Toov5", s -> s.substring(2,5) ); System.out.println(subResult); } public String strHalder(String str, myFun<String> myFun){ return myFun.getValue(str); }
运行结果:
Lambda 想数据一样 作为参数传递
函数式接口:
public interface myFun2<T,R> { public R getValue(T t1 , T t2); }
使用:
@Test public void test3(){ op(100L, 200L, (x,y)->x+y); op(100L, 200L, (x,y)->x*y); } public void op(Long L1, Long L2 , myFun2<Long, Long> mf){ System.out.println(mf.getValue(L1, L2)); }
Java8 中内置的四大核心函数式接口
1、Consumer<T> 消费型接口 又去无会的 只有输入没有输出
void accept(T t)
举栗子:
@Test public void test4(){ happy(1000, (m) -> System.out.println("消费了")); } public void happy(double money, Consumer<Double> consumer){ consumer.accept(money); }
2、Supplier<T> 供给型接口
T get() //没参数 但是有返回值 调用就返回值
举个栗子:产生指定个数的整数,并放入集合中。
//每次缠身一个随机数 一共十个 List<Integer> numList = getNumList(10, () -> (int) (Math.random() * 100)); for (Integer n : numList){ System.out.println(n); } } public List<Integer> getNumList(int num, Supplier<Integer> supplier){ List<Integer> list = new ArrayList<>(); for (int i = 0; i < num; i++){ Integer n = supplier.get(); list.add(n); } return list; } }
3、Function<T, R> 函数型接口 (传入个T 返回个R)
R apply(T t)
举个栗子: 用于处理字符串
@Test public void test6(){ String result = setHandler("java is toov5", (str) -> str.toUpperCase()); System.out.println(result); } public String setHandler(String str , Function<String, String> function){ return function.apply(str); }
4、Predicate<T> 断言型接口
boolean test(T t)
举个栗子: 将满足条件的字符串添加到集合中去
@Test public void test7(){ List<String> list = Arrays.asList("hello", "java", "toov5", "go"); List<String> resultList = filtefStr(list, (s) -> s.length() > 3); for (String s : resultList){ System.out.println(s); } } public List<String> filtefStr(List<String> list , Predicate<String> predicate){ ArrayList<String> strList = new ArrayList<>(); for (String str : list){ if (predicate.test(str)) { strList.add(str); } } return strList; }
小结:
经常用的接口都给内置了
要不我们还得自己写接口,写这个接口函数
还有好多是这四个的子接口! 大家自己学习吧。
方法引用
若Lambda体重的内容由方法已经实现了,可以使用“方法引用”
可以立即为方法引用是Lambda表达式的另外一种表现形式
主要有三种语法格式:
对象::实例方法名
举个栗子:
@Test public void test8(){ //Lambda体 方法、功能 已经有方法完成的情况,可以使用方法引用。 前提是: 参数列表 和 返回类型 一直 对象::实例 Consumer<String> con = (x) -> System.out.println(x); PrintStream out = System.out; //表示实例 Consumer<String> consumer = out::println; Consumer<String> consumer1 = System.out::println; consumer1.accept("toov5"); }
@Test public void test9(){ Employee employee = new Employee(1, "toov5"); Supplier<String> supplier = ()-> employee.getName(); String str = supplier.get(); System.out.println(str); Supplier<Integer> supplier1 = employee::getNumber; Integer result = supplier1.get(); System.out.println(result); }
类::静态方法名
注意:
Lambda 体汇总嗲用方法的参数列表与返回值,要与函数式接口中抽象方法的函数列表和返回值类型保持一致。 可以用方法引用
举个栗子:
@Test public void test10(){ Comparator<Integer> reult =(x, y)-> Integer.compare(x,y); //compare 是静态方法 可以通过类名去调用 //等价于 Comparator<Integer> result1 = Integer::compare; //类:: 静态方法名字 }
类:: 实例方法名
举个栗子:
@FunctionalInterface public interface BiPredicate<T, U> { /** * Evaluates this predicate on the given arguments. * * @param t the first input argument * @param u the second input argument * @return {@code true} if the input arguments match the predicate, * otherwise {@code false} */ boolean test(T t, U u);
使用:
@Test public void test11(){ //实例方法名字! // 四大核心接口里面没有这个接口 BiPredicate<String, String> bp = (x, y)-> x.equals(y); //上述在Lambda体中也是 一个方法搞定的 与此同时eqals还是个实例方法 //于是使用方法引用! 规则: 第一个参数是这个方法的调用者 第二个参数是要调用的方法的参数时候才可以用实例方法引用 BiPredicate<String, String> bp1 = String::equals; }
注意: Lambda参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时候,可以使用 ClassName:: method
二、 构造器引用:
语法格式: 类名::new
@Test public void test12(){ Supplier<Employee> supplier = ()->new Employee(); //上述通过构造器引用的方式 Supplier<Employee> supplier1 = Employee::new; Employee employee = supplier1.get(); System.out.println(employee); }
可以这么玩儿:
@Test public void test13(){ Function<Integer, Employee> function = (x)-> new Employee(x); //可以这么玩儿 这个调用的是有参构造器 这个是取决于前面的 参数列表 Function<Integer, Employee> function1 = Employee::new; Employee employee = function1.apply(100); System.out.println(employee); }
@Test public void test14(){ BiFunction<Integer, String, Employee> biFunction = Employee::new; Employee toov5 = biFunction.apply(12, "toov5"); System.out.println(toov5); }
注意: 需要调用的构造器的采纳数列表要与函数式接口中抽象方法的参数列表保持一致! 构造器引用就可以随心所欲的应用了!
三、数组的引用
语法: type[]:: new
@Test public void test15(){ Function<Integer, String[]> function = (x)-> new String[x]; String[] arr = function.apply(10); //传入10 时候返回一个长度为10的数组 System.out.println(arr.length); Function<Integer,String[]> function1 = String[]::new; String[] arr1 = function1.apply(20); System.out.println(arr1.length); }
牛逼的Stream APi
如同玩SQL一样 ,操作Java的数据
只要是流 都得有个数据源:
集合 、数组
类似于数据的传输,创建一个传输数据的管道,在传输过程中,做一些列流水线式的中间操作。过滤、筛选、切片等
然后产生新的流。
对于原来的数据源不会有改变,数据源不会受到影响。
相当于 复制了一份数据 改变的新的数据
Stream 是数据渠道, 用于操作数据源(集合、数组等)所生成的元素序列。
集合讲的是数据,流讲的是计算
Stream的流程: 创建Stream 中间操作 终止操作
创建stream的方式:
可以通过Collection 系列结合提供的 Stream() 或者 parallelStream() 注意前者获取的是串行流 后者是并行流
@Test public void test16(){ List<String> list = new ArrayList<>(); //方式1 Stream<String> stringStream = list.stream(); //流也带泛型的 //方式2 通过Arrays中的静态方法stream() 获取数组流 Employee[] employeesArr = new Employee[10]; Stream<Employee> stream = Arrays.stream(employeesArr); //方式3:Stream类中的静态方法 Stream<String > stringStream1 = Stream.of("a","b","c"); //也可以传进数组 //方式4: 创建无限流 //1) 迭代 这里的种子其实就是个起始值 配合终止操作 Stream.iterate(0, x -> x + 2).limit(30) .forEach(System.out::println); //2) 生成 Stream.generate(()-> Math.random()).limit(2) .forEach(System.out::println); }
有了Strem 以后就可以进行一系列流水线的中间操作了!
筛选、切片等
内容:
筛选与切片 接受 Lambda 从流中排除某些元素
limit 截断流 是其元素不超过给定数量
skip 跳过元素 返回一个扔掉了钱n个元素的流,元素不足返回空流 与limit(n) 互补
distinct 筛选 通过流所生成元元素的hashCode() 和 equals() 去除重复元素
没有终止操作时候:
@Test public void test17(){ //只要有用到流 就会有那三步 List<Employee> employees = Arrays.asList( new Employee(1, "11"), new Employee( 2, "22"), new Employee(3, "33"), new Employee(4, "44")); //先来个流 Stream<Employee> employeeStream = employees.stream().filter((e) -> { System.out.println("中间操作"); return e.getNumber() > 2; }); // employeeStream.forEach(System.out::println); }
什么都没有打印
加入终止条件:
@Test public void test17(){ //只要有用到流 就会有那三步 List<Employee> employees = Arrays.asList( new Employee(1, "11"), new Employee( 2, "22"), new Employee(3, "33"), new Employee(4, "44")); //先来个流 Stream<Employee> employeeStream = employees.stream().filter((e) -> { System.out.println("中间操作"); return e.getNumber() > 2; }); employeeStream.forEach(System.out::println); }
结果打印了,满足条件的都有打印::
过程中看下 filter 所需要的参数类型:
小结:
中间操作不会执行任何操作,只有当执行了终止操作后,中间操作一次性执行完成。“”惰性求值“”
官方语言:
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作。否则中间操作不会执行任何的处理。而在终止操作时候一次全部处理
注意内部上述迭代是用Stream APi完成。
内部迭代这么玩儿:
public void test(){ List<Employee> employees = Arrays.asList( new Employee(1, "11"), new Employee( 2, "22"), new Employee(3, "33"), new Employee(4, "44")); Iterator<Employee> iterator = employees.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } }
filter 然后 limit (2) 如果发现 两条满足的数据后 后面的就不执行额 提高效率
@Test public void test18() { List<Employee> employees = Arrays.asList( new Employee(1, "11"), new Employee(2, "22"), new Employee(3, "33"), new Employee(4, "44")); employees.stream().filter( e -> { System.out.println("只是执行满足的哦"); return e.getNumber() > 2; } ).forEach( System.out::println ); }
这个一定要注意的问题!
@Test public void test19() { List<Employee> employees = Arrays.asList( new Employee(1, "11"), new Employee(2, "22"), new Employee(3, "33"), new Employee(4, "44")); employees.stream().filter( e -> e.getNumber() > 2 ).skip(2) .distinct() //如果想去除重复元素 对象必须重写hashcode 和equals方法! .forEach( System.out::println ); }
关于Stream的映射
map-接受Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被映射到每个元素上,并将其映射成一个新的元素。
flatMap 接受一个函数作为参数,将流中的每个值都转换成一个流,然后把所有流连接成一个流
@Test public void test20(){ List<String> list = Arrays.asList("aaa","bbb","ccc","ddd"); list.stream().map( str ->str.toUpperCase()).forEach(System.out::println); List<Employee> employees = Arrays.asList( new Employee(1, "11"), new Employee(2, "22"), new Employee(3, "33"), new Employee(4, "44")); employees.stream().map(Employee::getNumber).map(x->x+1) .forEach(System.out::println); }
下面的栗子:
做个返回流的方法:
map返回的是流
流中还有一个流
@Test public void test21(){ List<String> list = Arrays.asList("aaa","bbb","ccc","ddd"); // map本身返回流 里面的函数又是返回流 流中有流 流中存放的是Character Stream<Stream<Character>> streamStream = list.stream().map(test::filterCharacter); streamStream.forEach( (sm) ->{ sm.forEach(System.out::print); }); } public static Stream<Character> filterCharacter(String str){ List<Character> list = new ArrayList<>(); for (Character ch : str.toCharArray()){ list.add(ch); } return list.stream(); }
这样就用到flatMap了 接收一个函数作为参数,将流中的每个值都换成一个流,然后把所有流连接成一个流
与下面的这个进行比较:
public static Stream<Character> filterCharacter(String str){ List<Character> list = new ArrayList<>(); for (Character ch : str.toCharArray()){ list.add(ch); } return list.stream(); } @Test public void test22(){ List<String> list = Arrays.asList("aaa","bbb","ccc","ddd"); Stream<Character> characterStream = list.stream().flatMap(test::filterCharacter); characterStream.forEach(System.out::println); }
map相当于 每个元素 “”aaa” "bbb" 都是一个流 放入集合中。每个都是一个小流
flatmap 做成同一个流
联想集合的操作:
类似于
add( ) 添加一个集合对象到集合中
addAll( ) 添加一个集合的所有对象到集合中
关于Stream排序:
排序 sorted() 自然排序(Comparable)
sorted(Comparator com) 定制排序(Comparator) //按照指定的方式进行排序
@Test public void test23(){ List<String> list = Arrays.asList("aaa","bbb","ccc","ddd"); list.stream() .sorted() .forEach(System.out::println); List<Employee> employees = Arrays.asList( new Employee(1, "11"), new Employee(2, "22"), new Employee(3, "33"), new Employee(4, "44")); employees.stream().sorted( (e1, e2) -> { if (e1.getNumber().equals(e2.getNumber())){ return e1.getName().compareTo(e2.getName()); }else { return e1.getNumber().compareTo(e2.getNumber()); } }).forEach(System.out::println); }
关于Stream的终止操作:
查找与匹配:
allMatch 检查是否匹配所有元素
anyMatch 检查是否至少匹配一个元素
noneMatch 检查是否没有匹配所有元素
findFirst 返回第一个元素
findAny 返回当前流中的任意元素
count 返回流中元素的总个数
max返回流中的最大值
min 返回流中的最小值
@Test public void test24(){ List<Employee> employees = Arrays.asList( new Employee(1, "11",12.44,Employee.Status.BUSY), new Employee(2, "22",22.53,Employee.Status.FREE), new Employee(3, "33",64.3,Employee.Status.VOCATION), new Employee(4, "44",74.3,Employee.Status.BUSY)); //是不是匹配所有元素呢 boolean b1 = employees.stream().allMatch((e) -> e.getStatus().equals(Employee.Status.BUSY)); System.out.println(b1); //是否至少匹配一个元素 boolean b2 = employees.stream().anyMatch(employee -> employee.getStatus().equals(Employee.Status.BUSY)); System.out.println(b2); //是不是没有匹配所有元素 boolean b3 = employees.stream().noneMatch((e) -> e.getStatus().equals(Employee.Status.BUSY)); System.out.println(b3); //得到第一个 //补充:Java8 尽可能避免空指针异常 就搞了个Optional容器类,把对象放在这个容器类中。 // 如果容器中那个对象为空 就找个替代的对象 op.orElse(obj) 最终的findFirst有可能为空时候 Optional<Employee> first = employees.stream() .sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())) .findFirst(); System.out.println(first.get()); //找一个空闲状态的人 加入到我干活的团队中 Optional<Employee> any = employees.stream() .filter(employee -> employee.getStatus() .equals(Employee.Status.FREE)).findAny(); System.out.println(any.get()); //如果空闲的有两个 可以随便 可以通过并行流进行操作 多个线程同时找空闲的 谁找到算谁的 //并行的话 获取到谁就不一定了 employees.parallelStream() .filter(employee -> employee.getStatus() .equals(employee.getStatus())) .findAny(); //count long count = employees.stream().count(); System.out.println(count); //max 按照工资排序获取员工工资最高的员工信息 Optional<Employee> max = employees.stream().max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())); System.out.println(max); //获取最低工资 Optional<Double> min = employees.stream().map(Employee::getSalary).min(Double::compare); System.out.println(max.get()); }
Stream 规约与收集
reduce (T identity, BinaryOperator) / reduce(BinaryOperator) 可以将流总元素反复结合起来,得到一个值
注意reduce有好几种:
@Test public void test25(){ List<Integer> list = Arrays.asList(1,2,3,4,5,6,7); //reduce 有好几种 第一种有两个参数的: 起始值, 二元运算。 Integer result = list.stream().reduce(0, (x, y) -> x + y); System.out.println(result); }
reduce: 先把0 作为起始值x 然后从流中取出一个元素y 。实现x+y 结果作为x 然后继续从流中取值作为y 依次往下执行。 最终得到一个新值
举个栗子:
累计公司中工资的总数:
@Test public void test26(){ List<Employee> employees = Arrays.asList( new Employee(1, "11",12.44,Employee.Status.BUSY), new Employee(2, "22",22.53,Employee.Status.FREE), new Employee(3, "33",64.3,Employee.Status.VOCATION), new Employee(4, "44",74.3,Employee.Status.BUSY)); //注意返回Optional Optional<Double> result = employees.stream().map(Employee::getSalary) .reduce(Double::sum);//注意sum是Double的静态方法 System.out.println(result.get()); }
运行结果:
注意第一个不是Optional的,因为不论如何不可能为空,有个起始值的。有可能为空的值是封装到Optional中去
补充: map 和 reduce 的连接通常称为map-reduce,因谷歌用它进行网络搜索出名
Stream的终止操作:
count() 返回流中元素总数
max(Comparator c ) 返回流中最大值
min( Comparator c) 返回流中最小值
forEach(Consumer c) 内部迭代(使用Collection接口需要要好过户去做迭代,称为外部迭代。相反,Stream API使用内部迭代-它帮你把迭代做了)
规约:
reduce(T iden, BinaryOperator b) 可以将流汇中元素反复结合起来,得到一个值,返回T
reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值,返回 Option<T>
关于收集:
collect 将流转换为其他形式。 接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
collect(Collector c) 方法 将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
Collector接口中方法的实现决定了如何对刘执行收集操作(如手街道List 、Set、Map)。但是Cllectors实用类提供了很多静态方法,可以方便的创建常见收集器实例。具体方法实例
@Test public void test27(){ List<Employee> employees = Arrays.asList( new Employee(1, "11",12.44,Employee.Status.BUSY), new Employee(2, "22",22.53,Employee.Status.FREE), new Employee(3, "33",64.3,Employee.Status.VOCATION), new Employee(4, "44",74.3,Employee.Status.BUSY)); List<Integer> list = employees.stream().map(Employee::getNumber).collect(Collectors.toList()); list.forEach(System.out::println); }
如果有重复数据,放入到set中!
Set<Integer> list = employees.stream().map(Employee::getNumber).collect(Collectors.toSet());
可以这么玩儿
@Test public void test27(){ List<Employee> employees = Arrays.asList( new Employee(1, "11",12.44,Employee.Status.BUSY), new Employee(2, "22",22.53,Employee.Status.FREE), new Employee(3, "33",64.3,Employee.Status.VOCATION), new Employee(4, "44",74.3,Employee.Status.BUSY)); HashSet<Integer> list = employees.stream().map(Employee::getNumber).collect(Collectors.toCollection(HashSet::new)); //或者LinkedHashSet list.forEach(System.out::println); }
@Test public void test27(){ List<Employee> employees = Arrays.asList( new Employee(1, "11",12.44,Employee.Status.BUSY), new Employee(2, "22",22.53,Employee.Status.FREE), new Employee(3, "33",64.3,Employee.Status.VOCATION), new Employee(4, "44",74.3,Employee.Status.BUSY)); //收集总数 Long count = employees.stream().collect(Collectors.counting()); System.out.println(count); //获取平均值 Double avg = employees.stream().collect(Collectors.averagingDouble(Employee::getSalary)); System.out.println(avg); //总和 DoubleSummaryStatistics sum = employees.stream().collect(Collectors.summarizingDouble(Employee::getSalary)); System.out.println(sum); //最大值 employee 最大值 Optional<Employee> max = employees.stream().collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))); System.out.println(max.get()); //最小值 (最小工资) Optional<Double> min = employees.stream().map(Employee::getSalary).collect(Collectors.minBy(Double::compare)); System.out.println(min.get()); }
关于分组的玩儿法:
@Test public void test28(){ List<Employee> employees = Arrays.asList( new Employee(1, "11",12.44,Employee.Status.BUSY), new Employee(2, "22",22.53,Employee.Status.FREE), new Employee(3, "33",64.3,Employee.Status.VOCATION), new Employee(4, "44",74.3,Employee.Status.BUSY)); Map<Employee.Status, List<Employee>> map = employees.stream() .collect(Collectors.groupingBy(Employee::getStatus)); //分组 System.out.println(map); }
多级分组的玩儿法: 多列分组 比如:先按照部门分 然后部门一样按照薪资分等等
@Test public void test29(){ List<Employee> employees = Arrays.asList( new Employee(1, "11",12.44,Employee.Status.BUSY), new Employee(2, "22",22.53,Employee.Status.FREE), new Employee(3, "33",64.3,Employee.Status.VOCATION), new Employee(4, "44",74.3,Employee.Status.BUSY)); Map<Employee.Status, Map<String, List<Employee>>> map = employees.stream() .collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> { if (((Employee) e).getNumber() <= 35) { return "新来"; } else { return "toov5"; } })));//分组 System.out.println(map); }
分区栗子:true 一个分区 false 一个分区
@Test public void test30(){ List<Employee> employees = Arrays.asList( new Employee(1, "11",12.44,Employee.Status.BUSY), new Employee(2, "22",22.53,Employee.Status.FREE), new Employee(3, "33",64.3,Employee.Status.VOCATION), new Employee(4, "44",74.3,Employee.Status.BUSY)); Map<Boolean, List<Employee>> result = employees.stream() .collect(Collectors.partitioningBy((e) -> e.getSalary() > 50));//按照工资去分区 true一个区 false一个区域 System.out.println(result); }
也可以这么玩儿哦 分区方式的这样玩儿法: summarizingXXX
收集: 将流转换为其他形式,接收一个Collecttor接口实现,用于给Stream中元素做汇总的方法
@Test public void test30(){ List<Employee> employees = Arrays.asList( new Employee(1, "11",12.44,Employee.Status.BUSY), new Employee(2, "22",22.53,Employee.Status.FREE), new Employee(3, "33",64.3,Employee.Status.VOCATION), new Employee(4, "44",74.3,Employee.Status.BUSY)); DoubleSummaryStatistics result = employees.stream().collect(Collectors.summarizingDouble(Employee::getSalary)); System.out.println(result.getAverage()); System.out.println(result.getCount()); System.out.println(result.getMax()); System.out.println(result.getMin()); System.out.println(result.getSum()); }
连接字符串的操作:
@Test public void test30(){ List<Employee> employees = Arrays.asList( new Employee(1, "11",12.44,Employee.Status.BUSY), new Employee(2, "22",22.53,Employee.Status.FREE), new Employee(3, "33",64.3,Employee.Status.VOCATION), new Employee(4, "44",74.3,Employee.Status.BUSY)); String result = employees.stream().map(Employee::getName).collect(Collectors.joining("#")); System.out.println(result); }
Stream API练习
给定一个数字列表,如何返回一个由每个数的平方构成的列表呢?
用map去做
@Test public void test31(){ Integer[] nums = new Integer[]{1,2,3,4,5,6}; Arrays.stream(nums).map(x -> x * x).forEach(System.out::println); //使用reduce }
数一数流中有多少Employee
用reduce 和 map 去做:
@Test public void test32(){ List<Employee> employees = Arrays.asList( new Employee(1, "11",12.44,Employee.Status.BUSY), new Employee(2, "22",22.53,Employee.Status.FREE), new Employee(3, "33",64.3,Employee.Status.VOCATION), new Employee(4, "44",74.3,Employee.Status.BUSY)); Optional<Integer> result = employees.stream().map(employee -> 1).reduce(Integer::sum); System.out.println(result.get()); }
综合练习:
两个应用到的类:
Trader类:
package com.toov5.Java8; //交易员类 public class Trader { private String name; private String city; public Trader() { } public Trader(String name, String city) { this.name = name; this.city = city; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } @Override public String toString() { return "Trader [name=" + name + ", city=" + city + "]"; } }
Transaction类
package com.toov5.Java8; public class Transaction { private Trader trader; private int year; private int value; public Transaction() { } public Transaction(Trader trader, int year, int value) { this.trader = trader; this.year = year; this.value = value; } public Trader getTrader() { return trader; } public void setTrader(Trader trader) { this.trader = trader; } public int getYear() { return year; } public void setYear(int year) { this.year = year; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } @Override public String toString() { return "Transaction [trader=" + trader + ", year=" + year + ", value=" + value + "]"; } }
测试:
@Test public void test33(){ //找出2011年发生的所有交易,并按交易额进行排序(从高到低) transactions.stream() .filter( (t)->t.getYear() == 2011) .sorted( (t1,t2)-> Integer.compare(t1.getValue(), t2.getValue())) .forEach(System.out::println); //交易员都在哪些不同的城市工作? transactions.stream() .map((t)-> t.getTrader().getCity()) .distinct() //去重 .forEach(System.out::println); //查找来自北京的交易员,并且按照姓名排序 transactions.stream().filter( (t) -> t.getTrader().getCity().equals("Beiing")) .map(Transaction::getTrader) //映射关系 取出交易员 .sorted((t1, t2)-> t1.getName().compareTo(t2.getName())) .distinct() .forEach(System.out::println); //返回所有交易员的姓名字符串,按字母排序 transactions.stream() .map( (t)-> t.getTrader().getName()) //取出名字来 .sorted() .forEach(System.out::print); System.out.println("----------------------"); String result = transactions.stream() .map((t) -> t.getTrader().getName()) //取出名字来 .sorted() .reduce("*", String::concat); System.out.println(result); //这么玩儿 System.out.println("*********"); transactions.stream() .map( t -> t.getTrader().getName()) .flatMap(test::filterCharacterMethod) .sorted( (s1,s2) -> s1.compareToIgnoreCase(s2)) .forEach(System.out::print); //看看有没有交易员在北京工作 System.out.println("是否有人在北京工作"); boolean city = transactions.stream().anyMatch((t) -> t.getTrader().getCity().equals("Beiing")); System.out.println(city); //打印生活在北京的所有交易额 System.out.println("生活在北京的所有交易额"); Optional<Integer> sum = transactions.stream() .filter((e) -> e.getTrader().getCity().equals("Beiing")) .map(Transaction::getValue) .reduce(Integer::sum); System.out.println(sum.get()); //所有交易中 最高的交易额是多少 System.out.println("最高的交易额是多少"); Optional<Integer> max = transactions.stream() .map((t) -> t.getValue()) .max(Integer::compare); System.out.println(max.get()); //找到交易额最小的交易 System.out.println("交易额最小的交易"); Optional<Transaction> min = transactions.stream() .min((t1, t2) -> Integer.compare(t1.getValue(), t2.getValue())); System.out.println(min.get()); } public static Stream<String> filterCharacterMethod(String str){ List<String> list = new ArrayList<>(); for (Character ch : str.toCharArray()){ list.add(ch.toString()); } return list.stream(); }
测试结果:
并行流
关于并行流和顺序流
并行流就是把一个内容分成多个数据块,并且用不同的线程分别处理每个数据块的流
Java8 中将并行进行了优化, 我们可以很容易的对数据进行并行操作。Stream API可以通过声明性的通过parallel() 与 sequential() 在并行流与顺序流之间进行切换
补充Fork/Join框架
Fork/Join框架:就是在必要情况下,将一个大任务,进行拆分(fork) 成若干个小任务(拆到不可再拆时候),再将一个个小人物运行结果进行join汇总
设置一个临界值,到达临界值就不再分子任务了。分到不能分位置
然后把这一个个子任务放入CPU的线程队列去执行。执行结束之后,每个子任务都会产生响应的结果。得出对应的结果后,再把结果进行join,最终合并成一个结果。
Fork/Join 框架与传统线程池的区别
采用 “工作窃取”模式(work-stealing): 当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线 程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中。 相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务的 处理方式上.在一般的线程池中,如果一个线程正在执行的任务由于某些原因 无法继续运行,那么该线程会处于等待状态.而在fork/join框架实现中,如果 某个子问题由于等待另外一个子问题的完成而无法继续运行.那么处理该子 问题的线程会主动寻找其他尚未运行的子问题来执行.这种方式减少了线程 的等待时间,提高了性能
为啥效率高呢?
所谓多线程就是把线程分配到不同的cpu核上,也就是不同的线程上进行执行。
以四核cpu为例,如果传统的线程,则通过多线程完成任务。
如果是传统线程,每个任务有可能会阻塞。每个线程啥时候执行cpu说了算。一旦某个线程发生了阻塞的情况,会有后果: 在这个线程上的其他线程就没法执行了。
cpu没有很好的利用好资源,影响效率
fork/join
把每个小任务压入对应的线程中 形成一个个的线程队列。然后进行工作窃取模式。
不会闲着,去别的线程队列去偷任务。更能充分利用cpu资源。
举个栗子:
这两个的区别是一个有返回值,一个没有。
选择个有返回值的,返回累加和
框架:
package com.toov5.Java8; import java.util.concurrent.RecursiveTask; public class ForkJoinCalculate extends RecursiveTask<Long> { private long start; private long end; //临界值 private static final long THREASHOLD = 10000; public ForkJoinCalculate(long start, long end){ this.start = start; this.end = end; } @Override protected Long compute() { long length = end - start; if (length <= THREASHOLD){ long sum = 0; for (long i = start; i < end; i++){ sum+=i; } return sum; }else{ long middle = (start+end) / 2; ForkJoinCalculate left = new ForkJoinCalculate(start, middle); left.fork(); //拆分子任务 同时压入线程队列 ForkJoinCalculate right = new ForkJoinCalculate(middle + 1, end); right.fork(); return left.join() + right.join(); } } }
使用:
package com.toov5.Java8; import org.junit.Test; import java.time.Duration; import java.time.Instant; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; public class testForkJoin { //需要forkJoin线程池的支持 @Test public void test1(){ Instant start = Instant.now();//计算时间戳 ForkJoinPool pool = new ForkJoinPool(); ForkJoinTask<Long> task = new ForkJoinCalculate(0, 100000L); Long sumResult = pool.invoke(task); System.out.println(sumResult); Instant end = Instant.now(); System.out.println(Duration.between(start, end).toMillis()); //gtNano 纳秒 toMillis()毫秒 } @Test public void test2(){ Instant start = Instant.now(); Long sum = 0L; for (long i = 0; i< 100000L; i++){ sum+=i; } System.out.println(sum); Instant end = Instant.now(); System.out.println("耗费时间"+ Duration.between(start, end).toMillis()); } }
注意: 数值小的话,拆分是需要花时间的。不要随便用ForkJoin框架,数值大才适合这样玩儿。
临界值的设置也需要注意合适哦
以上的ForkJoin 比较繁琐,jdk8做了优化:
底层帮助实现了fork / join
java 8 并行流:
@Test public void test3(){ //顺序流 long result1 = LongStream.rangeClosed(0, 100000L) .reduce(0, Long::sum); //并行流 Instant start = Instant.now(); LongStream.rangeClosed(0, 100000L) .parallel() //可以与sequential() 在并行流和顺序流之间进行切换 .reduce(0, Long::sum); Instant end = Instant.now(); System.out.println("耗费时间"+ Duration.between(start, end).toMillis()); }
关于Optional容器类
OPtional<T> 类(Java.util.Optional) 是一个容器类,带包一个值存在或不存在,原来用null表示一个值不存在,现在Optional可以更好的表达这个概念,并且可以避免空指针异常。
常用方法:
1.Optinal.of(T t) 创建一个Optinal实例
@Test public void test3(){ //创建实例 封装对象 封装employee Optional<Employee> op = Optional.of(new Employee()); Employee employee = op.get(); System.out.println(employee); }
如果不是我们自己new的,是从别的地方传递过来的呢?
@Test public void test3(){ //创建实例 封装对象 封装employee Optional<Employee> op = Optional.of(null); Employee employee = op.get(); System.out.println(employee); }
2.Optional.empty() 创建一个空的Optional实例
@Test public void test4(){ //创建实例 封装对象 封装employee Optional<Employee> op = Optional.empty(); System.out.println(op.get()); //空指针是发生在这里的 }
3.Optional.ofNullable(T t) 若t不为null。 创建Optional实例,否则创建空实例
@Test public void test5(){ //创建实例 封装对象 封装employee Optional<Employee> op = Optional.ofNullable(new Employee()); System.out.println(op.get()); //空指针是发生在这里的 }
构建了一个空对象
4.isPresent() 判断是否包含值
@Test public void test6(){ Optional<Employee> op = Optional.ofNullable(new Employee()); if (op.isPresent()){ //如果里面有值 就get System.out.println(op.get()); } }
5. orElse(T t) 如果调用对象包含值,返回该值,否则返回t
@Test public void test6(){ Optional<Employee> op = Optional.ofNullable(null); //如果没有值就获取下面的 没有值就用下面的代替 Default Employee toov5 = op.orElse(new Employee(16, "toov5", 34.34, Employee.Status.FREE)); System.out.println(toov5); }
对于空的判断放到了容器中了
6.orElseGt(Supplier s) 如果调用对象包含之,返回该值,否则返回s获取的值
@Test public void test7(){ Optional<Employee> op = Optional.ofNullable(null); //如果没有值就获取下面的 没有值就用下面的代替 Default Employee employee = op.orElseGet(() -> new Employee()); //这里可以lambda表达式函数式接口 天马行空 的功能 System.out.println(employee); }
7. map(Function f ) 如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()
@Test public void test8(){ Optional<Employee> toov5 = Optional.ofNullable(new Employee(16, "toov5", 34.34, Employee.Status.FREE)); Optional<String> s = toov5.map((e) -> e.getName()); //把容器里面的对象应用到Map函数上了 System.out.println(s); }
8. flatMap(Function mapper) 与map类似,要求返回值必须是Optional
@Test public void test10(){ Optional<Employee> toov5 = Optional.ofNullable(new Employee(16, "toov5", 34.34, Employee.Status.FREE)); //必须包装到Optional中 Optional<String> s = toov5.flatMap((t) -> Optional.of(t.getName())); System.out.println(s.get()); }
这样进一步防止空指针异常
案例:
man类:
@Test public void test34(){ //获取一个男人心中女神的名字 Man man = new Man(); String godnessName = getGodnessName(man); System.out.println(godnessName); } public String getGodnessName(Man man){ return man.getGoddness().getName(); }
man没有女神 于是发生了空指针异常
于是乎可以这么解决:
@Test public void test34(){ //获取一个男人心中女神的名字 Man man = new Man(); String godnessName = getGodnessName(man); System.out.println(godnessName); } public String getGodnessName(Man man){ if (man != null){ Goddness goddness = man.getGoddness(); if (goddness != null){ return goddness.getName(); } } return "java"; }
上述容易if嵌套容易嵌套的很深! 比如男神心中有个男神女神心中有个渣男渣男心中有个外遇
java8 可以更好的解决~ Optional容器类
获取一个男人心中女神的名字
@Test public void test35(){ String godnessName = getGodnessName(null); System.out.println(godnessName); } public static String getGodnessName2(Optional<NewMan> man){ //传进来的对象可以用 没有的话就用默认的 return man.orElse(new NewMan()) .getGoddness() .orElse(new Goddness("toov5")) .getName(); }
这样:
新建NewMan类
public class NewMan { //有可能有 有可能没有的值包装在Optional类里面 private Optional<Goddness> goddness = Optional.empty(); //这样赋值使用! public NewMan() { } public NewMan(Optional<Goddness> goddness) { this.goddness = goddness; } public Optional<Goddness> getGoddness() { return goddness; } public void setGoddness(Optional<Goddness> goddness) { this.goddness = goddness; } @Override public String toString() { return "NewMan{}"; } }
测试
@Test public void test35(){ // String godnessName = getGodnessName(null); // System.out.println(godnessName); Optional<NewMan> op = Optional.ofNullable(null); String str = getGodnessName2(op); System.out.println(str); } public static String getGodnessName2(Optional<NewMan> man){ //传进来的对象可以用 没有的话就用默认的 return man.orElse(new NewMan()) .getGoddness() .orElse(new Goddness("toov5")) .getName(); }
或者:
@Test public void test35(){ // String godnessName = getGodnessName(null); // System.out.println(godnessName); Optional<NewMan> op = Optional.ofNullable(new NewMan()); String str = getGodnessName2(op); System.out.println(str); } public static String getGodnessName2(Optional<NewMan> man){ //传进来的对象可以用 没有的话就用默认的 return man.orElse(new NewMan()) .getGoddness() .orElse(new Goddness("toov5")) .getName(); }
@Test public void test35(){ // String godnessName = getGodnessName(null); // System.out.println(godnessName); Optional<Goddness> gn = Optional.ofNullable(null); Optional<NewMan> op = Optional.ofNullable(new NewMan(gn)); String str = getGodnessName2(op); System.out.println(str); } public static String getGodnessName2(Optional<NewMan> man){ //传进来的对象可以用 没有的话就用默认的 return man.orElse(new NewMan()) .getGoddness() .orElse(new Goddness("toov5")) .getName(); }
接口中的默认方法与静态方法
Java8 中接口中有默认的实现方法
接口默认方法的 “类优先” 原则
若一个接口汇总定义了一个默认方法,而另外一个父类或接口中定义了一个同名的方法时
选择父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数默认方法会被忽略
接口冲突。如果一个父类接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法), 那么必须覆盖该方法来解决冲突
普通类:
public class MyClass { public String getName(){ return "MyClass"; } }
接口类及其默认方法:
public interface MyFun { default String getName(){ return "default"; } }
接口及其实现类:
public interface MyInterface { default String getName(){ return "MyInterface"; } }
继承父类的子类
public class SubClass implements MyFun, MyInterface{ @Override public String getName() { return MyFun.super.getName(); } }
运行:
public class TestDefaultInterface { public static void main(String[] args) { SubClass sc = new SubClass(); System.out.println(sc.getName()); } }
结果:
Java8除了可以有个默认方法,还可以有静态方法
public interface MyInterface { default String getName(){ return "MyInterface"; } // public static void show(){ System.out.println("接口中的静态方法"); } }
使用:
public class TestDefaultInterface { public static void main(String[] args) { MyInterface.show(); } }
类和接口此时已经差距不大了,但是类可以实现实现多接口,不可以多继承
public interface MyInterface { public static void show(){ System.out.println("接口中的静态方法"); } }
测试:
public class TestDefaultInterface { public static void main(String[] args) { MyInterface.show(); } }
结果: