java8新特性
1、简介
-
lambda 、stream,转成面向函数类编程语言,面向对象是数据,面向函数是行为,把行为作为参数。
-
java8中可以有实现的方法,叫做default。
-
stream,lambda、collection息息相关。
2、lambda表达式
-
用来表示匿名函数或闭包
-
在java中无法将函数作为参数传递给一个方法,也无法声明返回一个函数的方法。
3、lambda表达式基本结构
(参数类型 参数)->{执行体/代码块} (param1 ,param2)->{执行体}
4、函数式接口
-
如果一个接口只有一个抽象方法,那么该接口就是一个函数式接口
-
如果我们在某个接口上声明了FunctionInterface注解,那么编译器就会按照函数式接口的定义来要求该接口
-
如果某个接口只有一个抽象方法但我们并没有给该接口声明FunctionInterface注解,那么编译器依旧会将该接口看做是函数式接口
-
lambda表达式起源于函数式接口,FunctionInterfa可以通过lambda表达式引用。
5、函数式接口示例
@FunctionalInterface public interface MyInterface { void run(); @Override String toString(); } //解释:此接口虽然有两个抽象方法,但也是函数式接口,因为toString(),是java.lang.object的方法,所以不算,相当于还是只有一个抽象方法。
6、特点
在java中,lambda表达式是对象,必须依赖特别的对象类型,即函数式接口,lambda为java添加了缺失的函数式编程的特性,lambda表达式类型是函数。
list.forEach() 内部迭代
list.forEach(System.out::print); 遍历list集合,通过方法引用的方式。
(参数)->{方法实现} 必须有上下文信息,才能判定。
Stream 串行流,单线程
ParallelStream 并行流,多线程。
7、例子1
public static void main(String[] args) { new Thread(() -> { System.out.println("thread"); }).start();//启动线程 List<String> list = Arrays.asList("hello", "welcome"); list.forEach(System.out::println); list.forEach(item -> { String s = item.toUpperCase(); System.out.println(s); }); } list.stream().map(item -> item.toUpperCase()).forEach(System.out::println); list.stream().map(String::toUpperCase).forEach(System.out::println);//方法引用实现
8、函数 Function
Function<String,String> 第一参数是输入类型,第二个是输出类型
Function<String, String> function = String::toUpperCase; toUpperCase,实例方法,则输入类型即为调用实例方法的 Collections.sort(list, Comparator.reverseOrder()); //倒序 Collections.sort(list, (String o1, String o2) -> {//lambda方式实现 return o1.compareTo(o2); });
9、lambda表达式介绍
java lambda表达式是一种匿名函数,他没有声明的方法,没有访问修饰符、返回值声明和名字,传递行为,而不仅仅是值。提升抽象层次,api重要性更好,更加灵活。
-
一个lambda表达式可以有0个或多个参数
-
参数的类型即可以明确的声明,也可根据上下文来推断,例如 (int a) (a)
-
所有参数需包含在圆括号内,参数之间用逗号相隔
-
空圆括号代表参数集为空,例如()->{42}
-
匿名函数的返回值类型与代码块的返回类型一致,若没有返回值为空。
-
expression o1.compareTo(o2) 表达式风格 || statement {return o1.compareTo(o2)} 语句风格
10、例子2
public class Demo1 { public static int compute(int a , Function<Integer,Integer> function){ int res=function.apply(a); return res; } public static void main(String[] args) { int res=compute(10,(a)->{return a+10;}); System.out.println(res); } }
11、高阶函数
如果一个函数接收一个函数作为参数,或者返回一个函数作为返回值,那么该函数叫做高阶函数。
12、函数Function和BiFunction介绍
函数Function: Function<Integer,Integer> func=value->value*10;
函数Function方法介绍:
R apply(T t); //接收一个参数返回结果 default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); }//先使用参数的方法,在使用调用的方法。 default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); }//先应用当前apply,再应用参数的apply,实现多function串联 static <T> Function<T, T> identity() { return t -> t; }//静态方法,返回一个同类型的function;
//compose 示例 public class Demo2 { public static void main(String[] args) { int res = compute(10, value1 -> value1 + 2, value2 -> value2 * 5); System.out.println(res); }//res=52 public static int compute(int a, Function<Integer, Integer> fun1, Function<Integer, Integer> fun2) { int res = fun1.compose(fun2).apply(a); return res; } } // andThen示例 public class Demo2 { public static void main(String[] args) { int res = compute(10, value1 -> value1 + 2, value2 -> value2 * 5); System.out.println(res); }//res=60 public static int compute(int a, Function<Integer, Integer> fun1, Function<Integer, Integer> fun2) { int res = fun1.andThen(fun2).apply(a); return res; } } BiFunction :两个参数一个返回值,他只有andThen方法,没有compose方法 //andThen示例 先执行调用者,在执行参数的apply方法。 public class Demo3 { public static void main(String[] args) { int res = compute(10, 20, (v1, v2) -> v1 + v2, value -> value * 2); System.out.println(res);//res=60 } public static int compute(int a, int b, BiFunction<Integer, Integer, Integer> bi, Function<Integer, Integer> fun) { int res = bi.andThen(fun).apply(a, b); return res; } }
13、说明
使用map等方法前,先把集合转换成流,list.stream().filter(p->p.equals()).collect(Collectors.toList()) //输出还是list
有两个参数一个返回值要求的函数,就可以使用lambda中的BiFunction去实现。
public class Demo4 { public static void main(String[] args) { Person p1 = new Person(10, "zhang"); Person p2 = new Person(20, "wang"); List<Person> people = Arrays.asList(p1, p2); get(15, people, (age, per) -> { return per.stream().filter(p -> p.getAge() > age).collect(Collectors.toList()); }).forEach(System.out::println); } public static List<Person> get(int age, List<Person> list, BiFunction<Integer, List<Person>, List<Person>> bi) { List<Person> apply = bi.apply(age, list); return apply; } } public class Person { private int age; private String name; public Person() { } @Override public String toString() { return "Person{" + "age=" + age + ", name='" + name + '\'' + '}'; } public Person(int age, String name) { this.age = age; this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
14、谓词 predicate
说明:输入一个参数,返回一个boolean类型数据,是一个规则函数,原来的函数上多加一个参数,这个参数为行为,则就是函数式编程。
方法说明:
boolean test(T t); //输入t,返回boolean default Predicate<T> and(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) && other.test(t); }//逻辑与,全真为真。,先执行调用者test()方法,在执行参数的test()方法。 default Predicate<T> or(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) || other.test(t); }//逻辑或,有真为真,先执行调用者test()方法,在执行参数的test()方法。 default Predicate<T> negate() { return (t) -> !test(t); }//逻辑非,真假相对 static <T> Predicate<T> isEqual(Object targetRef) { return (null == targetRef) ? Objects::isNull : object -> targetRef.equals(object); }//静态方法,判断一个数值与给定数值是否相等
15、predicate示例
public class Demo5 { public static void main(String[] args) { Predicate<String> pre = p -> p.length() > 5; System.out.println(pre.test("helloworld")); } }
public class Demo5 { public static void main(String[] args) { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); conFilter(list, value -> value % 2 == 0);//返回偶数 System.out.println("========="); conFilter(list, value -> value % 2 == 1);//返回奇数 System.out.println("-----------"); conFilter(list, value -> value >= 6);//返回大于6的数字 } public static void conFilter(List<Integer> list, Predicate<Integer> predicate) { list.stream().filter(x -> predicate.test(x)).forEach(System.out::println); } } //and 示例,输出偶数且大于5 public class Demo6 { public static void main(String[] args) { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); andFilter(list,item1->item1%2==0,item2->item2>5); } public static void andFilter(List<Integer> list, Predicate<Integer> pre1, Predicate<Integer> pre2) { list.stream().filter(x -> pre1.and(pre2).test(x)).forEach(System.out::println); } } //判断hello与给定的def值是否相等,Predicate的静态方法。 String def = "hello"; System.out.println(Predicate.isEqual(def).test("hello"));
16、supplier
说明:不接收任何参数,返回一个结果,类似工厂模式。
public class Demo7 { public static void main(String[] args) { Supplier<String> str = () -> "hello"; System.out.println(str.get()); } } //产生person对象,每次调用产生的对象不是同一个对象。 public class Demo7 { public static void main(String[] args) { Supplier<Person> person = () -> new Person(); person.get().getAge(); Supplier<Person> per = Person::new; per.get().getAge(); } }
17、BinaryOperator
说明:一个参数,他是BiFunction的子类
public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) { Objects.requireNonNull(comparator); return (a, b) -> comparator.compare(a, b) <= 0 ? a : b; }//静态方法,minBy 传入比较规则,找出规则后的最小的 public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) { Objects.requireNonNull(comparator); return (a, b) -> comparator.compare(a, b) >= 0 ? a : b; }//静态方法,maxBy 传入比较规则,找出规则后最大的
//mixBy示例 public class Demo7 { public static void main(String[] args) { int a = 10; int b = 20; System.out.println(compute(a,b,Comparator.reverseOrder())); //输出10 } public static int compute(int a, int b, Comparator<Integer> com) { return BinaryOperator.maxBy(com).apply(a, b); } }
18、Optional
说明:表示可选可不选,主要解决npe问题。
函数介绍:
public static<T> Optional<T> empty() { @SuppressWarnings("unchecked") Optional<T> t = (Optional<T>) EMPTY; return t; }//返回容器为空的对象, public static <T> Optional<T> of(T value) { return new Optional<>(value); }//参数不能为空,包装成容器对象 public static <T> Optional<T> ofNullable(T value) { return value == null ? empty() : of(value); }//参数可为空,可不为空 public T get() { if (value == null) { throw new NoSuchElementException("No value present"); } return value; }//获取容器中的值 public boolean isPresent() { return value != null; }//判断容器是否为空 public void ifPresent(Consumer<? super T> consumer) { if (value != null) consumer.accept(value); }//判断容器是否为空,不为空则取值,推荐使用 public T orElse(T other) { return value != null ? value : other; } //如果对象为空,则输出other,否则输出对象的数据。 public T orElseGet(Supplier<? extends T> other) { return value != null ? value : other.get(); }//如果对象为空,则输出other,否则输出对象的数据,和orElse功能类似,但是比他更灵活一些。
-
示例:
public class Demo8 { public static void main(String[] args) { Optional<Object> empty = Optional.empty(); Optional<String> op = Optional.of("hello"); Optional<String> world = Optional.ofNullable("world"); //isPresent() get()方法使用 if (op.isPresent()) { System.out.println(op.get());//hello } //ifPresent() 对象存在就输出 world.ifPresent(item -> System.out.println(item)); //world System.out.println(op.orElse("welcome"));//hello System.out.println(empty.orElse("welcome"));//welcome System.out.println(op.orElseGet(() -> "hi")); } }
public class Demo9 { public static void main(String[] args) { Employee em1 = new Employee("zhang", 10); Employee em2 = new Employee("wang", 20); Company com = new Company(); com.setCompany("dahua"); List<Employee> employees = Arrays.asList(em1, em2); com.setList(employees); Optional<Company> op = Optional.ofNullable(com); op.map(company -> { return company.getList(); }).orElse(Collections.emptyList());//加上orElse,为了避免getList()方法返回值为null,则我们放回一个空List,而不是null } } public class Employee { private String name; private int age; public Employee() { } public Employee(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } public class Person { private int age; private String name; public Person() { } @Override public String toString() { return "Person{" + "age=" + age + ", name='" + name + '\'' + '}'; } public Person(int age, String name) { this.age = age; this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
19、方法引用
说明:方法引用简化lambda,是lambda表达式的语法糖。method reference,可以将方法引用看做是一个函数指针。
方法引用分为4类:
-
类名::静态方法名
lambda表达式的方法体恰好有一个函数与之对应。
-
引用名::实例方法名/ 对象名::实例方法名
-
类名::实例方法名
public class Demo10 { public static void main(String[] args) { Person p1 = new Person(10, "zhang"); Person p2 = new Person(20, "wang"); List<Person> people = Arrays.asList(p1, p2); people.sort(Person::compareByage); //类名::实例方法名,sort的lambda表达式的第一个参数调用的compareByage()这个方法,其他参数为该方法的参数 } } public class Person { private int age; private String name; public Person() { } @Override public String toString() { return "Person{" + "age=" + age + ", name='" + name + '\'' + '}'; } public Person(int age, String name) { this.age = age; this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int compareByage(Person p1) { return this.getAge() - p1.getAge(); } }
-
构造方法引用: 类名::new
创建了该类的一个对象
20、默认方法
说明:接口中实现的方法,叫做默认方法,用default表示。
当一个类实现两个接口,两个接口中有相同的方法名时,实现类必须重写该方法,或者 接口名.super.method()方式
为什么添加默认方法:为了保证向后兼容。
21、stream
说明:流操作,更好的操作集合,支持并行化。
流由3部分构成
-
源
-
零个或多个中间操作,操作的是源
-
终止操作
流操作的分类
-
惰性求值
-
及早求值
22、Stream例子
public static void main(String[] args) { //通过数组创建流 String[] str = new String[]{"hello", "worf"}; Stream<String> stream1 = Stream.of(str); Stream<String> stream2 = Arrays.stream(str); //通过集合创建流 List<String> list = Arrays.asList(str); Stream<String> stream3 = list.stream(); IntStream.range(3, 8).forEach(System.out::println);//不包含结束 IntStream.of(new int[]{1, 2, 3}).forEach(System.out::println); IntStream.rangeClosed(3, 8).forEach(System.out::println);//包含结束 }
23、流特点
-
collection提供了新的stream,将集合计算转成流计算,更加方便,流不能重复使用,否则报错,流关闭了也不能使用了。
-
集合先转成流,然后函数式编程。
-
流不存储值,通过管道方式获取值,本质是函数式,对流的操作会生成一个结果,不过并不会修改底层的数据源,集合可以作为流的底层数据源
-
只有有终止操作,中间操作才能执行
-
流相当于一个容器
-
stream和迭代器有所不同,stream支持并行化操作,迭代器只能命令式的串行化操作。
-
使用并行去遍历时,数据会被分成多个段,其中每个都在不同的线程中处理,然后将结果一起输出。
-
stream并行操作依赖于java7中引入的fork/join框架
public static void main(String[] args) { //通过数组创建流 Stream<String> stream = Stream.of("hello", "world"); //将流转成数组 String[] strings = stream.toArray(String[]::new); //遍历集合 Arrays.asList(strings).forEach(System.out::println); //每个流操作单独执行,流每次操作后都会关闭 //将流转成集合 List<String> collect = stream.collect(Collectors.toList()); collect.forEach(System.out::println); //手动实现将流转成ArrayList集合 ArrayList<String> collect1 = stream.collect(() -> new ArrayList<String>(), (thislist, item) -> thislist.add(item), (zuilist, list) -> zuilist.addAll(list)); collect1.forEach(System.out::println); LinkedList<String> collect2 = stream.collect(LinkedList::new, LinkedList::add, LinkedList::addAll); collect2.forEach(System.out::println); stream.collect(Collectors.toCollection(ArrayList::new)).forEach(System.out::println); //流中字符串拼接,可有分隔符,也可没有 String s = stream.collect(Collectors.joining("-")).toString(); System.out.println(s); }
//将集合转成流操作,然后再把流转成集合,最终遍历 list.stream().map(String::toUpperCase).collect(Collectors.toList()).forEach(System.out::println); Stream.of(list, list1).flatMap(item -> item.stream()).map(String::toUpperCase).forEach(System.out::println);
Stream<String> generate = Stream.generate(UUID.randomUUID()::toString); //findFirst返回值为optional,对于optional类型,推荐使用ifPresent()进行判断,然后输出值 generate.findFirst().ifPresent(System.out::println);
-
无限流
iterate 无线流,第一参数为初始值,第二个参数根据初始值计算后续值 limit() 限制取几个元素。 skip() 忽略几个元素 sun() 求和 min() 最小值,返回optional类型,使用ifPresent判断取值。 distinct() 去重 Stream.iterate(1, item -> item + 1).limit(5).forEach(System.out::println);
public static void main(String[] args) { Stream<Integer> stream = Stream.iterate(1, item -> item + 1).limit(7); //返回元素的一系列操作 IntSummaryStatistics statistics = stream.filter(item -> item > 3).mapToInt(item -> item * 2).skip(2).limit(2).summaryStatistics(); statistics.getSum(); statistics.getAverage(); statistics.getCount(); statistics.getMax(); statistics.getMin(); }
24、内部迭代|外部迭代
-
内部迭代,操作的是流,自己的代码和流融合,相当于只做一次循环,相当于完形填空,我们只需要补充空缺的地方。
-
外部迭代:操作的是集合本身。
-
forEach是iterate的语法糖
25、集合与流
-
集合关注数据与数据存储本身
-
流关注的是对数据的计算
-
流和迭代器类似的一点是,流无法重复使用或消费
-
stream中间操作都会返回一个stream对象
-
终止操作则不会返回stream类型,可能不返回值,也可能返回其他类型的单个值。
26、串行流并行流时间比较
public class Demo15 { public static void main(String[] args) { ArrayList<String> integers = new ArrayList<>(500000); for (int i = 0; i < 500000; i++) { integers.add(UUID.randomUUID().toString()); } long start = System.nanoTime(); integers.stream().sorted().count(); long end = System.nanoTime(); System.out.println("串行流时间:" + TimeUnit.NANOSECONDS.toMillis(end - start)); long startparall = System.nanoTime(); integers.parallelStream().sorted().count(); long endparall = System.nanoTime(); System.out.println("并行流时间:" + TimeUnit.NANOSECONDS.toMillis(endparall - startparall)); } } //串行流时间:442 //并行流时间:264
27、流的短路和并发
List<String> list = Arrays.asList("hello", "worldwe"); list.stream().mapToInt(item -> item.length()).filter(l -> l > 5).findFirst().ifPresent(System.out::println);//输出7
//只输出hello 5 如果把list元素位置互换,则Worldwe和hello都会输出 public class Demo16 { public static void main(String[] args) { List<String> list = Arrays.asList("hello", "worldwe"); list.stream().mapToInt(item -> { int leng = item.length(); System.out.println(item); return leng; }).filter(l -> l == 5).findFirst().ifPresent(System.out::println); } }
stream相当于一个容器
流先对第一个元素进行所有操作,然后第二个元素进行所有操作,如果第一个满足条件则第二个的中间结果就不会运行了
//Arrays::stream 将string[] 转成string public static void main(String[] args) { List<String> list = Arrays.asList("hello world", "hello welcome"); List<String[]> collect = list.stream().map(item -> item.split(" ")).distinct().collect(Collectors.toList()); List<String> collect1 = list.stream().map(item -> item.split(" ")).flatMap(Arrays::stream).distinct().collect(Collectors.toList()); collect1.forEach(System.out::println); }
//交叉打招呼 List<String> list = Arrays.asList("hello", "hi"); List<String> list1 = Arrays.asList("zhang", "wang"); List<String> collect = list.stream().flatMap(item -> list1.stream().map(it -> item + ":" + it) ).collect(Collectors.toList()); collect.forEach(System.out::println);
flatMap()将结果大平
28、分组操作
//按照名字进行分组public class Demo19 { public static void main(String[] args) { Student s1 = new Student("zhang", 90); Student s2 = new Student("zhang", 80); Student s3 = new Student("wang", 80); List<Student> list = Arrays.asList(s1, s2, s3); Map<String, Long> collect = list.stream().collect(Collectors.groupingBy(Student::getName, Collectors.counting())); collect.forEach((x, y) -> System.out.println(x + ":" + y)); } } //按照名字进行分组,并计算组内人数 public class Demo18 { public static void main(String[] args) { Student s1 = new Student("zhang", 90); Student s2 = new Student("zhang", 80); Student s3 = new Student("wang", 80); List<Student> list = Arrays.asList(s1, s2, s3); Map<String, List<Student>> collect = list.stream().collect(Collectors.groupingBy(Student::getName)); collect.forEach((x, y) -> System.out.println(x + ":" + y)); } }
//按照名字进行分组,并计算组内平均成绩 public class Demo19 { public static void main(String[] args) { Student s1 = new Student("zhang", 90); Student s2 = new Student("zhang", 80); Student s3 = new Student("wang", 80); List<Student> list = Arrays.asList(s1, s2, s3); Map<String, Double> collect = list.stream().collect(Collectors.groupingBy(Student::getName, Collectors.averagingDouble(Student::getScore))); collect.forEach((x, y) -> System.out.println(x + ":" + y)); } }
29、分区操作
分区是分组的一种特例。
//根据成绩是否大于85进行分区,得到map类型,键是boolean public class Demo20 { public static void main(String[] args) { Student s1 = new Student("zhang", 90); Student s2 = new Student("zhang", 80); Student s3 = new Student("wang", 80); List<Student> list = Arrays.asList(s1, s2, s3); Map<Boolean, List<Student>> collect = list.stream().collect(Collectors.partitioningBy((x) -> x.getScore() > 85)); collect.forEach((x,y)-> System.out.println(x+":"+y)); } }
//counting() 求集合中元素的个数 public class Demo20 { public static void main(String[] args) { Student s1 = new Student("zhang", 90); Student s2 = new Student("zhang", 80); Student s3 = new Student("wang", 80); List<Student> list = Arrays.asList(s1, s2, s3); Long collect = list.stream().collect(Collectors.counting()); System.out.println(collect); } }
30、收集器 collect()
说明:collector作为collect()方法的参数,collecto人是一个接口
-
他是一个可变的汇聚操作,将输入元素累积到一个可变的结果容器中。
-
他会在所有元素都处理完毕后,将累积结果转换为一个最终的表示(这是一个可选操作),它支持串行和并行两种方式执行
-
collectors 本身提供了关于collector的常见汇聚实现,collectors本身实际上是一个工厂
collector有4个函数
//A中间结果类型,可变容器的类型 //T流中每一个结果容器的类型 //R结果类型 Supplier<A> supplier(); //创建可变结果容器, BiConsumer<A, T> accumulator(); //将新的元素汇聚到结果容器 BinaryOperator<A> combiner(); //与并发流相关,将两个结果容器合并 Function<A, R> finisher(); //完成器
-
Colletor类讲解
//第一个泛型表示,每一个元素的类型,第二个泛型表示中间结果类型,第三个泛型表示结果类型 public interface Collector<T, A, R> Function<A, R> finisher(); //完成器 enum Characteristics { CONCURRENT, //并行,这条语句可能出现并发修改异常,多个线程会操作同一个结果容器,parallelStream() 如果没有加concurrent,则多个线程操作多个结果容器,则会调用combiner(), //并发修改异常,一个线程修改集合,另一个线程遍历集合,则会抛出异常。 //并行流,如果使用concurrent时,不会调用combiner()因为使用concurrent则操作同一个结果容器。。 UNORDERED,//无序的 IDENTITY_FINISH //同一性表示中间结果容器类型和最终结果容器类型相同,,如果中间结果容器类型和做种结果容器类型不同,不能使用,否则报错。 }
combiner()函数,有4个线程同时去执行,那么就会生成4个部分结果,通过combiner()函数将其合并
为了保证串行与并行操作结果的等待性,collector函数需要满足两个条件identity(同一性)与associativity(结合性)
a== combiner.apply(a,Supplier.get())
reduce:不可变 collect 可变的
collectorImpl实现了collect接口,在collectors类中。
collectors相当于工厂,为开发者提供常用的收集器
-
函数式编程最大的特点:表示做什么,而不是如何做
31、收集器例子
//找出集合中成绩最小的并打印 public static void main(String[] args) { Student s1 = new Student("zhang", 90); Student s2 = new Student("zhang", 80); Student s3 = new Student("wang", 80); List<Student> list = Arrays.asList(s1, s2, s3); list.stream().collect(Collectors.minBy(Comparator.comparing(Student::getScore))).ifPresent(System.out::println); }
public static void main(String[] args) { Student s1 = new Student("zhang", 90); Student s2 = new Student("zhang", 80); Student s3 = new Student("wang", 80); List<Student> list = Arrays.asList(s1, s2, s3); //求成绩的平均值 Double collect = list.stream().collect(Collectors.averagingDouble(Student::getScore)); //求成绩的和 Double collect1 = list.stream().collect(Collectors.summingDouble(x -> x.getScore())); //得到成绩的汇总信息 DoubleSummaryStatistics collect2 = list.stream().collect(Collectors.summarizingDouble(x -> x.getScore())); collect2.getAverage(); collect2.getCount(); collect2.getMax(); collect2.getMin(); collect2.getSum(); }
//流中字符串连接,包括前缀和后缀。 List<String> list1 = Arrays.asList("hello", "word"); String collect3 = list1.stream().collect(Collectors.joining("-", "pre", "post")); System.out.println(collect3);
public static void main(String[] args) { Student s1 = new Student("zhang", 90); Student s2 = new Student("zhang", 80); Student s3 = new Student("wang", 80); List<Student> list = Arrays.asList(s1, s2, s3); //先按成绩分组,然后再按姓名进行分组 Map<Double, Map<String, List<Student>>> collect = list.stream().collect(Collectors.groupingBy(Student::getScore, Collectors.groupingBy(Student::getName))); }
public static void main(String[] args) { Student s1 = new Student("zhang", 90); Student s2 = new Student("zhang", 80); Student s3 = new Student("wang", 80); Student s4 = new Student("li", 70); List<Student> list = Arrays.asList(s1, s2, s3, s4); //先按成绩大于70分区,然后在成绩大于70里面按照成绩大于80进行分区 Map<Boolean, Map<Boolean, List<Student>>> collect = list.stream().collect(Collectors.partitioningBy(x -> x.getScore() > 70, Collectors.partitioningBy(y -> y.getScore() > 80))); } //根据名字进行分组,如果名字相同,接下来按照成绩最小分组。 public static void main(String[] args) { Student s1 = new Student("zhang", 90); Student s2 = new Student("zhang", 80); Student s3 = new Student("wang", 80); Student s4 = new Student("li", 70); List<Student> list = Arrays.asList(s1, s2, s3, s4); Map<String, Student> collect = list.stream().collect(Collectors.groupingBy(Student::getName, Collectors.collectingAndThen(Collectors.minBy(Comparator.comparingDouble(Student::getScore)), Optional::get))); }
32、比较器comparator
//thenComparing()根据前一个比较器结果,再比较,如果第一个比较器可以得出结果,则第二个比较器不会执行,否则才会执行第二个比较器,thenComparing()可能不发挥任何作用。 Comparator<Student> studentComparator = Comparator.comparingDouble(Student::getScore).thenComparing(Student::getScore).thenComparing(Student::getName);
//升序(小值在前):前面的值-后面的值,降序(大值在前):后面的值减去前面的值 public static void main(String[] args) { List<String> list = Arrays.asList("hello", "worldd"); Collections.sort(list); list.forEach(System.out::println); System.out.println("========="); Collections.sort(list,(a,b)->b.length()-a.length()); list.forEach(System.out::println); System.out.println("========="); Collections.sort(list, Comparator.comparingInt(String::length).reversed());//长度降序 list.forEach(System.out::println); }
//lambda表达式,编译器会类型推断,但是有些时候不能进行类型推断,需要显示的给出。例如下面的例子,item是reversed的返回类型,所以需要显示给出类型。 Collections.sort(list,Comparator.comparingInt((String item)->item.length()).reversed()); System.out.println("========="); Collections.sort(list,Comparator.comparingInt(String::length).thenComparing(String.CASE_INSENSITIVE_ORDER));//先按字符串长度排序,然后按照不区分字母大小写排序 Collections.sort(list, Comparator.comparingInt(String::length).thenComparing(String::toUpperCase, Comparator.reverseOrder()));
33、自定义收集器
import java.util.*; import java.util.function.BiConsumer; import java.util.function.BinaryOperator; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collector; public class MyCollecotor<T> implements Collector<T, Set<T>, Set<T>> { @Override public Supplier<Set<T>> supplier() { return HashSet::new; } @Override public BiConsumer<Set<T>, T> accumulator() { return Set::add; } @Override public BinaryOperator<Set<T>> combiner() { return (zuiset, zhongset) -> { zuiset.addAll(zhongset); return zuiset; }; } @Override public Function<Set<T>, Set<T>> finisher() { // return t->t;//两种方式都可以 return Function.identity(); } @Override public Set<Characteristics> characteristics() { return Collections.unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH, Characteristics.UNORDERED)); } } //使用自定义收集器 public static void main(String[] args) { List<String> list = Arrays.asList("hello", "world"); Set<String> collect = list.stream().collect(Collectors.toSet()); Set<String> collect1 = collect.stream().collect(new MyCollecotor<>()); collect1.forEach(System.out::println); }
import java.util.*; import java.util.function.BiConsumer; import java.util.function.BinaryOperator; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collector; public class MyMapCollector<T> implements Collector<T, Set<T>, Map<T, T>> { @Override public Supplier<Set<T>> supplier() { return HashSet<T>::new; } @Override public BiConsumer<Set<T>, T> accumulator() { return Set<T>::add; } @Override public BinaryOperator<Set<T>> combiner() { return (item1, item2) -> { item1.addAll(item2); return item1; }; } @Override public Function<Set<T>, Map<T, T>> finisher() { return item -> { Map<T, T> map = new HashMap<>(); item.forEach(x -> map.put(x, x)); return map; }; } @Override public Set<Characteristics> characteristics() { return Collections.unmodifiableSet(EnumSet.of(Characteristics.UNORDERED)); } } public static void main(String[] args) { List<String> list = Arrays.asList("hello", "world"); Set<String> collect = list.stream().collect(Collectors.toSet()); Map<String, String> collect1 = collect.stream().collect(new MyMapCollector<>()); collect1.forEach((x,y)-> System.out.println(x+":"+y)); }
34、串行流和并行流比较
public static void main(String[] args) { List<String> list = Arrays.asList("hello", "world")//如果同时出现串行流和并行流,则看collect()前面是什么流,则就使用什么流执行。 List<String> collect = list.stream().parallel().sequential().collect(Collectors.toList()); } stream.paralle()//并行流, supplier被调用多次 stream.sequential() //串行流,supplier被调用一次
io比较多用多线程, 并行是会生成与cpu核心数相当的线程。
超线程:一个核会作为两个核。
35、流函数介绍
joining()//字符串拼接 collectingAndThen() //根据前面的,结果在执行 SummingInt() //实现为什么会使用长度为1的数组,因为数字是值类型,不能传递,数组是引用类型,可以传递 groupingBy(function,collect)//第二个参数,是对前面function返回值的操作。 groupingByConcurrent()//对返结果没有要求,并发运行。
函数式接口
lambda->函数式接口(FunctionInterface)->stream->collector
36、流源码分析
-
Autocloseable 在退出 try。。。with。。。resource块是调用,流自动关闭。
-
stream方法在collection接口中以default方法出现,流管道,一般是无状态的
-
不管调用多少次parallel或sequential都是以最后一次为主。
public static void main(String[] args) { List<String> list = Arrays.asList("hello", "world"); //onClose()不会执行,只要加上try时,才会执行 list.stream().onClose(() -> System.out.println("welcome")).onClose(() -> { System.out.println("hi"); }).forEach(System.out::println); }
//onClose()方法会执行一个地方抛异常,不会影响其他的执行,其他的异常作为压制异常,只有第一个异常返回给调用者,其他的作为压制异常。 public static void main(String[] args) { List<String> list = Arrays.asList("hello", "world"); try(Stream<String> stream = list.stream()){ stream.onClose(() -> System.out.println("welcome")).onClose(() -> { System.out.println("hi"); }).forEach(System.out::println); } }
CopyOnWriteArrayList<String> //适合读多写少的情况 ConcurrentHashMap<String,String> //并发map ,tryAdvance() 完成了hasNext()和next()方法,tryAdvance()返回boolean,由下一个元素就执行action动作 forEachRemaining()//剩下的元素继续执行动作。 trysplit() // 尝试分割,返回spliterator ordered //元素顺序确定好的 distinct //去重的 sorted // 排序的,遵循指定的顺序 sized//执行遍历之前或分割之前,返回元素数量的精确值, nonnull //不为空的 immutable //结构上不能修改的,不可变的 concurrent //并发,元素安全的并发修改, subsize //从trysplit返回的是subsize的固定大小。 ofprimitive() //原生类型,原生类型的分割迭代器,int,long,double只原生累型。 ofInt() //专门针对int类型的分割迭代器。
public class MyConsumer { public static void main(String[] args) { MyConsumer consumer = new MyConsumer(); Consumer<Integer> con = i -> System.out.println(i); IntConsumer conint = i -> System.out.println(i); //直接传入consumer对象,可以 consumer.test(con); System.out.println("===="); //lambda表达式,可行 consumer.test(conint::accept); System.out.println("===="); //lambda表达式,可行 consumer.test(con::accept); System.out.println("===="); //强制转换就不行,传递行为就可以 consumer.test((Consumer<Integer>) conint); } public void test(Consumer<Integer> consumer) { consumer.accept(100); } }
-
referencepipeline 中间的管道阶段,管道源阶段,表示流的源阶段,与中间阶段
-
Referencepipeline.head 引用管道的源,静态类
两者在大部分属性的设定上是类似的,但有一些属性的不同,比如head是没有previousstage的,而referencepipeline则存在
-
abstractpipeline ,管道累的父类
-
流的串联就是双向链表,一个sink表示管道的一个阶段
-
流中每一个元素应用中间所有操作,然后执行中止操作。
-
内部类和lambda没有任何关系
-
lambda表达式,表示当前类的实例,,没有开辟新的作用域
-
内部类,开辟了新的作用域
class LambdaTest { Runnable r1 = () -> System.out.println(this); Runnable r2 = new Runnable() { @Override public void run() { System.out.println(this); } }; public static void main(String[] args) { LambdaTest lam=new LambdaTest(); Thread t1 = new Thread(lam.r1); t1.start(); Thread t2 = new Thread(lam.r2); t2.start(); } } //LambdaTest@7e129604 lambda表达式输出结果 //LambdaTest$1@90472a2 内部类输出结果,$1,表示排在第一个的内部类 list.stream().map().filter() //map()的上游是stream()的输出,map()的下游时候filter()
-
terminal 终止操作
-
stream 流程
37、joda time
原来的Date SimpledataFormat Calender 不是线程安全的
java8的日期与时间和jodatime非常相似,与前相关的joda money,java8以后就不建议用joda time了
<!-- https://mvnrepository.com/artifact/joda-time/joda-time --> <dependency> <groupId>joda-time</groupId> <artifactId>joda-time</artifactId> <version>2.9.9</version> </dependency>
-
关于日期和时间
-
格林威治标准时间 “+”表示快,“-”表示慢
-
UTC 时间,不带时区 的时间
-
ISO8601 时间
joda time 1表示的就是一月份,2就是二月份
public static void main(String[] args) { DateTime today = new DateTime(); System.out.println("today:" + today); //时间加一,表示明天 DateTime tomorrow = today.plusDays(1); System.out.println("tomorrow:" + tomorrow); //转成字符串 String s = today.toString("yyyy-MM-dd"); System.out.println(s); //设置为本月1号 DateTime oneday = today.withDayOfMonth(1); System.out.println("oneday:" + oneday); //当前日期后3个月的最后一天。dayOfMonth()得到当前月,withMaximumValue()表示当前月的最后一天 DateTime dateTime = today.plusMonths(3).dayOfMonth().withMaximumValue(); System.out.println("datetime:" + dateTime); //计算两年前,第三个月的最后一天,monthOfYear()得到当前年的月份,setCopy() 设置第几个月 DateTime dateTime1 = today.minusYears(2).monthOfYear().setCopy(3).dayOfMonth().withMaximumValue(); System.out.println("dateTime1:"+dateTime1); }
import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.format.DateTimeFormat; import java.util.Date; public class Demo3 { public static void main(String[] args) { //传入UTC时间,不用考虑时区,标准UTC时间,用UTC方式存储日期,不会涉及时区的问题,他会帮助你自动转换了 System.out.println(Demo3.conver("2018-07-08T07:30:33.876Z")); System.out.println(Demo3.conDatetoUTC(new Date())); System.out.println(Demo3.converDateByformat(new Date(), "yyyy-MM-dd")); } //客户端把时间传给服务器,服务器转换成日期 //传入utc时间,转成date类型 public static Date conver(String uctdate) { try { DateTime dateTime = DateTime.parse(uctdate, DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")); return dateTime.toDate(); } catch (Exception e) { return null; } } // 由服务器端日期转换成客户端的UTC日期,由客户端决定如何呈现 public static String conDatetoUTC(Date javaDate) { DateTime dateTime = new DateTime(javaDate, DateTimeZone.UTC); return dateTime.toString(); } //格式化呈现 public static String converDateByformat(Date javaDate, String dateFormat) { DateTime dateTime = new DateTime(javaDate, DateTimeZone.UTC); return dateTime.toString(dateFormat); } }
38、java8 time
java8time和joda time都是不可变的对象,日期都是不可变的对象
Date calendar 的日期都是可变的线程不安全的日期。
public static void main(String[] args) { //当前时间,不包括时分秒 LocalDate today = LocalDate.now(); System.out.println("today:" + today); System.out.println("year:" + today.getYear() + "month:" + today.getMonth() + "monthvalue:" + today.getMonthValue() + "day:" + today.getDayOfMonth()); //通过指定时间构造时间 LocalDate of = LocalDate.of(2018, 5, 10); System.out.println("of:" + of); //通过月日构造时间 MonthDay of1 = MonthDay.of(10, 21); System.out.println("of1:" + of1); //通过from构建月和日 MonthDay from = MonthDay.from(LocalDate.of(2017, 2, 25)); System.out.println("from:" + from); //增加两周 LocalDate plus = today.plus(2, ChronoUnit.WEEKS); System.out.println("plus:" + plus); //减去两个月 LocalDate minus = today.minus(2, ChronoUnit.MONTHS); System.out.println("minus:" + minus); //时区操作 //返回所有时区 Set<String> availableZoneIds = ZoneId.getAvailableZoneIds(); //排序 List<String> collect = availableZoneIds.stream().collect(Collectors.toList()); Collections.sort(collect); collect.forEach(System.out::println); System.out.println("输出所有时区结束"); //指定时区 ZoneId of2 = ZoneId.of("Asia/Shanghai"); System.out.println("of2:" + of2); //得到默认时区 Clock clock = Clock.systemDefaultZone(); System.out.println("clock:" + clock); //输出毫秒值 System.out.println("输出毫秒值" + clock.millis()); //当前日期时分秒 LocalTime time = LocalTime.now(); System.out.println("time:" + time); //当前时间加上3小时20分 LocalTime localTime = time.plusHours(3).plusMinutes(20); System.out.println("localtime:" + localTime); //当前日期包括时分秒UTC时间 LocalDateTime ldt = LocalDateTime.now(); System.out.println("ldt包括时分秒:" + ldt); //包括时区的时间 ZonedDateTime of3 = ZonedDateTime.of(ldt, of2); System.out.println("包括时区的时间of3:" + of3); //年月操作 //通过指定值,创建年月 YearMonth of4 = YearMonth.of(2018, 10); System.out.println("年月of4:" + of4); //当前月有多少天 System.out.println("当前月有几天" + of.lengthOfMonth()); //判断是否是闰年 System.out.println("是否是闰年:" + of4.isLeapYear()); //判断一年有多少天 System.out.println("一年有多少天:" + of4.lengthOfYear()); //周期性的 LocalDate l1=LocalDate.now(); LocalDate l2=LocalDate.of(2017,10,12); //周期 Period between = Period.between(l1, l2); //间隔多少个月,不跨年,超过12个月就是年 System.out.println("间隔多少个月:"+ between.getMonths()); //间隔多少天 System.out.println("间隔多少天:"+between.getDays()); //间隔多少年 System.out.println("间隔多少年:"+between.getYears()); //时间的比较 //比较是否在今天之前,是返回true System.out.println(today.isBefore(of)); //比较是否在今天之后,是返回true System.out.println(today.isAfter(of)); //比较是否相等 System.out.println(today.isEqual(of)); }