java8的函数式编程

1.函数式接口(特定的一类接口)

  1. 概念:接口里面有且只有一个抽象方法,对于接口里面的默认方法和静态方法不作限制。一般会有@FunctionalInterface修饰(可以没有)
@FunctionalInterface
public interface FunctionInterface<T,V> {

    T apply (V value); // 只能有一个

    static void apply1() {
        System.out.println("这是静态方法");
    }
    static void apply2() {
        System.out.println("这是静态方法");
    }

    default void apply3() {
        System.out.println("这是默认方法");
    }
}
  1. 四个内置的函数式接口
Consumer<T>:消费型接口  void accept(T t)
Supplier<T>:供给型接口  T get()
Function<T, R>:函数型接口  R apply(T t)
Predicate<T>:断言型接口   boolean test(T t)

2.lamda表达式

作用: lamda表达式实际上是对函数式接口编写匿名内部类的一种简写。

 Runnable runnable = new Runnable() {
     @Override
     public void run() {

     }
 }; // 普通的匿名内部类的写法
 Runnable runnable1 = () -> {
 };// 使用lamda表达式实现

3.Stream类常用方法

1. 创建stream对象的常用方法

// 数组转化成stream的两种方式Arrays.stream和Stream.of
 Integer[] ints = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
 Stream<Integer> stream = Arrays.stream(ints);
 Stream<Integer> ints1 = Stream.of(ints);

// list集合转化成stream的方式
 ArrayList<String> list = new ArrayList<>();
 Stream<String> stream = list.stream();

// map集合转化成stream的方式
 HashMap<String, String> map = new HashMap<>();
 Stream<Map.Entry<String, String>> stream = map.entrySet().stream();

2. stream常用的方法

Stream类常用方法分为两种:中间操作和终端操作。
中间操作:主要用于对数据处理,每次会返回一个新的流。
终端操作:每个流只能有一个终端操作,该终端操作会返回一个具体的结果。

1.中间操作常用方法

  1. Stream distinct(); 集合去重
    注意:distinct()要想进行对象去重,必须实现对象的hashCode()&equals()方法
@Data
@AllArgsConstructor
public class Person {
    private String name;
    private int age;
    private List<Address> addr;
}
@Data
@AllArgsConstructor
public class Address {
    private String attr1;
    private String attr2;
}
 List<Person> list = new ArrayList<Person>();
        list.add(new Person("张三", 18, null));
        list.add(new Person("张三", 18, null));
        List<Person> result = list.stream()
                .distinct()
                .collect(Collectors.toList());

        result.forEach(item -> System.out.println(item)); // 结果只有一条
  1. Stream filter(Predicate predicate); 对数据集合进行过滤
List<Person> list = new ArrayList<Person>();
        list.add(new Person("张三", 18, null));
        list.add(new Person("张三", 19, null));
        List<Person> result = list.stream()
                .filter(item -> item.getAge() > 18)
                .collect(Collectors.toList());

        result.forEach(item -> System.out.println(item)); // 过滤年龄小于19的数据
  1. Stream map(Function mapper); 将数据集合对象映射成任意对象的数据集合
    List<Person> list = new ArrayList<Person>();
        list.add(new Person("张三", 18, null));
        list.add(new Person("张三", 19, null));
        List<Integer> result = list.stream()
                .map(item -> item.getAge())
                .collect(Collectors.toList()); // 映射成integer对象

        List<Address> result1 = list.stream()
                .map(item -> item.getAddr())
                .collect(Collectors.toList());// 映射成List
对象 result.forEach(item -> System.out.println(item)); result1.forEach(item -> System.out.println(item));
  1. Stream flatMap(Function> mapper); 返回一个stream对象,主要是将一个二维集合压缩成一个一维集合
 List<String> list1 = Arrays.asList("a1","a2","a3");
 List<String> list2 = Arrays.asList("b1","b2","b3");
 List<List<String>> list = Arrays.asList(list1,list2);

 list.stream().forEach(item -> System.out.println(item));
 // 输出结果:[a1, a2, a3], [b1, b2, b3]
 System.out.println("分割线2------------------");
 list.stream()
         .flatMap(item -> item.stream())
         .forEach(item -> System.out.println(item));  
 // 输出结果:每个元素分别输出 a1, a2,a3,b1,b2,b3
  1. Stream sorted(); 对元素进行排序
  2. Stream sorted(Comparator comparator);对元素进行排序,但是排序的对象必须制定排序规则,即实现Comparable接口
  List<Person> list1 = new ArrayList<>();
  list1.add(new Person("张三",18,null));
  list1.add(new Person("李四",20,null));
  list1.add(new Person("王五",19,null));
  list1.add(new Person("赵六",19,null));

  list1.stream()
          .sorted((item1, item2) -> item1.getAge() - item2.getAge());

注意:Comparator接口提供了几个静态的方法来进行排序,我们可以使用其提供的方法来指定排序规则。

/* 
 1. comparing方法:指定排序字段,升序
 2. reversed方法:翻转排序规则,转为降序
 3. thenComparing方法:指定按前面指定排序后再按该字段排序
 4.  5. 排序规则:以age字段降序排,再以name字段降序排
*/
 List<Person> list1 = new ArrayList<>();
 list1.add(new Person("张三",18,null));
 list1.add(new Person("李四",20,null));
 list1.add(new Person("王五",19,null));
 list1.add(new Person("赵六",19,null));

 list1.stream()
         .sorted(Comparator.comparing(Person::getAge).reversed().thenComparing(Person::getName).reversed())
         .forEach(item -> System.out.println(item));
  1. Stream peek(Consumer action);用于调试流执行操作过程,不会对元素进行任何改变
// peek方法就是进行调试用的,可以看到在流操作时每一个元素的执行情况
List<Person> list1 = new ArrayList<>();
list1.add(new Person("张三",18,null));
list1.add(new Person("李四",20,null));
list1.add(new Person("王五",19,null));
list1.add(new Person("赵六",19,null));

list1.stream()
        .peek(item -> System.out.println("去重前" + item))
        .distinct()
        .peek(item -> System.out.println("过滤前" + item))
        .filter(item -> item.getAge() > 18)
        .peek(item -> System.out.println("映射前" + item))
        .map(item -> item.getAge())
        .forEach(item -> System.out.println(item));
  1. Stream limit(long maxSize); 截取流元素前几个的数据,可与skip连用
  2. Stream skip(long n);丢弃流元素前几个的数据,可与limit连用
 List<Person> list1 = new ArrayList<>();
 list1.add(new Person("张三",18,null));
 list1.add(new Person("李四",20,null));
 list1.add(new Person("王五",19,null));
 list1.add(new Person("赵六",19,null));

 list1.stream()
         .distinct()
         .filter(item -> item.getAge() > 18)
         .map(item -> item.getAge())
         .skip(1)
         .limit(1)
         .forEach(item -> System.out.println(item));

2.终端操作常用方法

  1. R collect(Collector collector)收集器,将流转换为需要的对象
        Person person1 = Person.builder()
                .addName("zhangs")
                .addAge(18)
                .build();

        Person person2 = Person.builder()
                .addName("lisi")
                .addAge(19)
                .build();

        ArrayList<Person> list = new ArrayList<>();
        list.add(person1);
        list.add(person2);

        List<Person> list1 = list.stream().collect(Collectors.toList());
        Map<Integer, String> map = list.stream().collect(Collectors.toMap(Person::getAge, Person::getName));
        Set<Person> collect = list.stream().collect(Collectors.toSet());
  1. void forEach(Consumer action):遍历流里面的对象
  2. Optional findFirst():返回流中的第一个元素
        Person person1 = Person.builder()
                .addName("zhangs")
                .addAge(18)
                .build();

        Person person2 = Person.builder()
                .addName("lisi")
                .addAge(19)
                .build();

        ArrayList<Person> list = new ArrayList<>();
        list.add(person1);
        list.add(person2);

        Person person = list.stream().findFirst().get();
  1. Optional findAny():返回当前流中任意一个元素
        Person person1 = Person.builder()
                .addName("zhangs")
                .addAge(18)
                .build();

        Person person2 = Person.builder()
                .addName("lisi")
                .addAge(19)
                .build();

        ArrayList<Person> list = new ArrayList<>();
        list.add(person1);
        list.add(person2);
        Person person = list.stream().findAny().get();
  1. long count():返回流中元素的个数
  2. Optional max(Comparator comparator): 根据比较规则返回最大值
  3. Optional min(Comparator comparator):根据比较规则返回最小值
Person person1 = Person.builder()
                .addName("zhangs")
                .addAge(18)
                .build();

        Person person2 = Person.builder()
                .addName("lisi")
                .addAge(19)
                .build();

        ArrayList<Person> list = new ArrayList<>();
        list.add(person1);
        list.add(person2);
        list.stream().count();
        list.stream().max(Comparator.comparing(Person::getAge));
        list.stream().min(Comparator.comparing(Person::getAge));
  1. boolean anyMatch(Predicate predicate): 检查流中至少一个元素是否有匹配规则
  2. boolean allMatch(Predicate predicate): 检查流中所有元素是否匹配规则
  3. boolean noneMatch(Predicate predicate): 检查流中所有元素是否都不匹配规则
        Person person1 = Person.builder()
                .addName("zhangs")
                .addAge(18)
                .build();

        Person person2 = Person.builder()
                .addName("lisi")
                .addAge(19)
                .build();

        ArrayList<Person> list = new ArrayList<>();
        list.add(person1);
        list.add(person2);
        list.stream().anyMatch(value -> "zhangs".equals(value.getName()));
        list.stream().allMatch(value -> "zhangs".equals(value.getName()));
        list.stream().noneMatch(Value -> Value.getAge() == 18);
  1. Optional reduce(BinaryOperator accumulator): 将流中的数据进行累积操作后返回
  2. T reduce(T identity, BinaryOperator accumulator):同上述方法,多了带初始值
       Person person1 = Person.builder()
                .addName("zhangs")
                .addAge(18)
                .build();

        Person person2 = Person.builder()
                .addName("lisi")
                .addAge(19)
                .build();

        ArrayList<Person> list = new ArrayList<>();
        list.add(person1);
        list.add(person2);
        Optional<Integer> reduce = list.stream()
                .map(Person::getAge)
                .reduce((item1, item2) -> item1 + item2);
        Integer reduce1 = list.stream()
                .map(Person::getAge)
                .reduce(15, (item1, item2) -> item1 + item2);

3.方法引用

1.作用

方法的引用是用来在特定情况下简化lamda表达式的一种写法,可以当成lamda表达式的语法糖

2.使用

1.类::静态方法

        Person person1 = Person.builder()
                .addName("zhangs")
                .addAge(18)
                .build();
        ArrayList<Person> list = new ArrayList<>();
        list.add(person1);
        list.stream().forEach(value -> System.out.println(value));
        list.stream().forEach(System.out::println); // 类::静态方法
  1. 类::非静态方法
        Person person1 = Person.builder()
                .addName("zhangs")
                .addAge(18)
                .build();

        Person person2 = Person.builder()
                .addName("lisi")
                .addAge(19)
                .build();

        ArrayList<Person> list = new ArrayList<>();
        list.add(person1);
        list.add(person2);
        list.stream()
                .map(item -> item.getAge())
                .collect(Collectors.toList());
        list.stream()
                .map(Person::getAge) // 类::非静态方法
                .collect(Collectors.toList());
  1. 对象::非静态方法
public class Student implements Comparator<Student>{
    private String name;
    private int 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;
    }

    @Override
    public int compare(Student o1, Student o2) {
        return o1.getAge() - o2.getAge();
    }
}
        ArrayList<Student> students = new ArrayList<>();
        Student student = new Student();
        students.sort(student::compare);
  1. 类::new
public class Person {
    private String name;
    private int age;

    public Person() {
    }

    public Person(Builder builder) {
        this.name = builder.name;
        this.age = builder.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;
    }

    @Override
    public String toString() {
        return "name" + this.name + "age" + this.age;
    }

    public static Builder builder() {
        return new Builder();
    }

    public static class Builder {
        private String name;
        private int age;

        public Builder addName(String name) {
            this.name = name;
            return this;
        }

        public Builder addAge(int age) {
            this.age = age;
            return this;
        }

        public Person build(Function<Builder,Person> mapper ) {
            return mapper.apply(this);
        }
    }
}
        Person person1 = Person.builder()
                .addName("zhangs")
                .addAge(18)
                .build(Person::new);// 类::new

        Person person2 = Person.builder()
                .addName("lisi")
                .addAge(19)
                .build(Person::new); // 类::new

4.map和flatmap方法的对比

  • map: map方法是将stream里面的每个元素根据需求映射成其他对象,返回值可以是任意的;
  • flatmap:是将一个二维的集合压缩组合成一个一位的集合去处理,返回值必须是Stream类型
/*
*	flatmap将Person的addr属性(类型是List
)压缩在一起,组成新的集合 * map是将原来的Person对象取addr属性和name属性映射成List
和String的集合 */ List<Person> list3 = new ArrayList<>(); List<Address> addList = new ArrayList<>(); addList.add(new Address("test1","test1")); addList.add(new Address("test2","test2")); list3.add(new Person("张三",18,addList)); System.out.println("分割线------------------"); list3.stream() .flatMap(item -> item.getAddr().stream()) .forEach(item -> System.out.println(item)); // 输出结果: // Address(attr1=test1, attr2=test1) // Address(attr1=test2, attr2=test2) list3.stream() .map(item -> item.getAddr()) .forEach(item -> System.out.println(item)); // 输出结果: // [Address(attr1=test1, attr2=test1), Address(attr1=test2, attr2=test2)] list3.stream() .map(item -> item.getName()) .forEach(item -> System.out.println(item)); // 输出结果: // 张三
/*
*	对于多层,可以多次使用flatMap方法来获取值
*/
 List<String> list4 = Arrays.asList("c1","c2","c3");
 List<String> list5 = Arrays.asList("d1","d2","d3");
 List<List<String>> list6 = Arrays.asList(list4,list5);
 List<List<List<String>>> list7 = Arrays.asList(list6);
 list7.stream()
         .flatMap(item -> item.stream())
         .flatMap(item -> item.stream())
         .forEach(item -> System.out.println(item));
// 输出结果: c1,c2,c3,d1,d2,d3

5.reduce方法详情

1.参数

  • U identity:相当于一个初始值,注意,在使用并行流时,会在每个线程中都赋予该初始值
  • BiFunction accumulator, B:对数据进行累积操作,在并行流时会将每个线程的数据进行累积操作
  • inaryOperator combiner:将参数2的每个累积操作结果进行累积操作,注意,在非并行流时该方法不会去执行
        Integer[] ints = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

        /*
        只有一个参数,会返回Optional
         */
        Optional<Integer> reduce1 = Arrays.stream(ints).reduce((item1, item2) -> {
            System.out.println("参数3--" + item1);
            System.out.println("参数4--" + item2);
            Integer value = item1 + item2;
            System.out.println("运行结果2 ----" + Thread.currentThread() + ":" + value);
            return value;
        });


        /*
            有两个参数,第一个参数相当于默认值参与运算
         */
        Integer reduce2 = Arrays.stream(ints).reduce(777, (item1, item2) -> {
            System.out.println("参数3--" + item1);
            System.out.println("参数4--" + item2);
            Integer value = item1 + item2;
            System.out.println("运行结果2 ----" + Thread.currentThread() + ":" + value);
            return value;
        });

        /*
        有三个参数,但是使用的是串行流,第三个参数的方法是不会去执行的,也只有一个主进程,结果是正常的
         */
        Integer reduce3 = Arrays.stream(ints)
                .peek(value -> System.out.println(Thread.currentThread() + ":" + value))
                .reduce(777, (item1, item2) -> {
                    System.out.println("参数1--" + item1);
                    System.out.println("参数2--" + item2);
                    Integer value = item1 + item2;
                    System.out.println("运行结果1 ----" + Thread.currentThread() + ":" + value);
                    return value;
                }, (item1, item2) -> {
                    System.out.println("参数3--" + item1);
                    System.out.println("参数4--" + item2);
                    Integer value = item1 + item2;
                    System.out.println("运行结果2 ----" + Thread.currentThread() + ":" + value);
                    return value;
                });
        System.out.println(reduce3);

        /*
        有三个参数,用parallel()转成并行流,java会开启多个线程去将数据加载到不同线程中分别计算,
        最后每个线程的结果在方法3里面进行累积,
        此时会看到结果有问题,因为开启多个线程,导致每个线程都用了第一个参数的初始值,最终结果出了问题
        因此需要注意在并行流里面需要考虑初始值对结果是否有影响
         */
        Integer reduce4 = Arrays.stream(ints).parallel()
                .peek(value -> System.out.println(Thread.currentThread() + ":" + value))
                .reduce(777, (item1, item2) -> {
                    System.out.println("参数1--" + item1);
                    System.out.println("参数2--" + item2);
                    Integer value = item1 + item2;
                    System.out.println("运行结果1 ----" + Thread.currentThread() + ":" + value);
                    return value;
                }, (item1, item2) -> {
                    System.out.println("参数3--" + item1);
                    System.out.println("参数4--" + item2);
                    Integer value = item1 + item2;
                    System.out.println("运行结果2 ----" + Thread.currentThread() + ":" + value);
                    return value;
                });
        System.out.println(reduce3);

你可能感兴趣的:(java基础,java,面试,c++)