java8新特性

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>

  • 关于日期和时间

  1. 格林威治标准时间 “+”表示快,“-”表示慢

  2. UTC 时间,不带时区 的时间

  3. 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));
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值