java8的函数式编程

2023-11-17

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<T> 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<T> filter(Predicate<? super T> 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. <R> Stream<R> map(Function<? super T, ? extends R> 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<Address>对象

        result.forEach(item -> System.out.println(item));
        result1.forEach(item -> System.out.println(item));
  1. <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> 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<T> sorted(); 对元素进行排序
  2. Stream<T> sorted(Comparator<? super T> 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<T> peek(Consumer<? super T> 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<T> limit(long maxSize); 截取流元素前几个的数据,可与skip连用
  2. Stream<T> 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, A> R collect(Collector<? super T, A, R> 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<? super T> action):遍历流里面的对象
  2. Optional<T> 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<T> 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<T> max(Comparator<? super T> comparator): 根据比较规则返回最大值
  3. Optional<T> min(Comparator<? super T> 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<? super T> predicate): 检查流中至少一个元素是否有匹配规则
  2. boolean allMatch(Predicate<? super T> predicate): 检查流中所有元素是否匹配规则
  3. boolean noneMatch(Predicate<? super T> 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<T> reduce(BinaryOperator<T> accumulator): 将流中的数据进行累积操作后返回
  2. T reduce(T identity, BinaryOperator<T> 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<Address>)压缩在一起,组成新的集合
*	map是将原来的Person对象取addr属性和name属性映射成List<Address> 和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<U, ? super T, U> accumulator, B:对数据进行累积操作,在并行流时会将每个线程的数据进行累积操作
  • inaryOperator combiner:将参数2的每个累积操作结果进行累积操作,注意,在非并行流时该方法不会去执行
        Integer[] ints = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

        /*
        只有一个参数,会返回Optional<T>
         */
        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);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java8的函数式编程 的相关文章

随机推荐

  • JS栈内存和堆内存详解

    JS变量都存放在内存中 而内存给变量开辟了两块区域 分别为栈区域和堆区域 栈像个容器 容量小速度快 堆像个房间 容量较大 一 基本数据类型和引用数据类型存储方式 js中的数据类型可以分为基本类型和引用类型 基本类型是存在栈内存中的 引用类型
  • 在网页的JS中注入Hook

    在网页的JS中注入Hook Chrome浏览器的overrides的使用 itcoder cn 1 以下为Edge 的示例 1 本地新建一个目录 2 用浏览器关联该目录 选择目录后 浏览器上方会弹出一个横条提示确认 点击允许后即可关联 3
  • Java实现MD5加密及解密的代码实例分享

    原文地址 http www jb51 net article 86027 htm 如果对安全性的需求不是太高 MD5仍是使用非常方便和普及的加密方式 比如Java中自带的MessageDigest类就提供了支持 这里就为大家带来Java实现
  • muduo net库学习笔记4——事件驱动循环EventLoop、runInLoop和queueInLoop及对应唤醒

    首先总体情况 每个muduo网络库有一个事件驱动循环线程池 EventLoopThreadPool 线程池用在事件驱动循环上层 也就是事件驱动循环是线程池中的一个线程 每个TcpServer对应一个事件驱动循环线程池 每个线程池中有多个事件
  • jupyter notebook新建python3空白_jupyter notebook 启动出现404 302,web空白页无反应

    原来装过官网的python2 7和3 6 在这基础上装了anaconda3 启动jupyter notebook时出现404和302 复制url到猎豹和IE浏览器都没有反应 token复制也不行 后来卸载了官网的python2 7和3 6也
  • 区块链基础:交易模型解读

    1 比特币系统UTXO解读 UTXO unspent transaction output 未花费的交易输出 这是比特币交易中核心概念 UTXO是比特币拥有者的公钥锁定的一个数字 实际是是拥有者的公钥加密的数字 只有拥有者的私钥才能解开 U
  • go语言常用标准库(Time)

    go语言常用标准库 Time 1 Time 时间和日期是我们编程中经常会用到的 本文主要介绍了Go语言内置的time包的基本用法 1 1 1 time包 time包提供了时间的显示和测量用的函数 日历的计算采用的是公历 1 1 2 时间类型
  • 线性代数的本质(十一)——复数矩阵

    文章目录 复数矩阵 附录 极大线性无关组 向量叉积 复数矩阵 矩阵 A A A 的元素 a i j
  • 斐波那契数列的递归与非递归

    斐波那契数列 F n 1 n 0 1时 F n F n 1 F n 2 n gt 1时 1 递归实现 int Fib int n if n 1 n 0 return 1 return Fib n 1 Fib n 2 时间复杂度 O 2 n
  • idea常用的快捷键和常用设置

    目录 1 常用idea快捷键 2 查找相关快捷键 3 常用项目快捷键 设置字体 字体文本设置 切换主题 字符编码设置 IDEA模板 idea 目录分层 1 常用idea快捷键 1 全选 CTRL A 最简单 几乎所有的编辑器都有此功能 2
  • UISearchBar 和 UISearchDisplayController的使用

    之前比較少用UISearchBar 和 UISearchDisplayController 最近閱讀了一些有關資料 簡單做個筆記 1 UISearchBar 和 UISearchDisplayController 在IB中是可以直接使用的
  • Xshell连接VMware CentOS7

    https blog csdn net weixin 43593086 article details 90247751
  • Android屏幕适配

    一 一些概念的理解 屏幕尺寸 屏幕的对角线 如一台小米电视49寸说的就是电视对角线长度是49寸 1英寸 2 54厘米 分辨率 1920 1080指纵向1920个像素点 横向1080个像素点 1280 720同理 屏幕像素密度 DPI
  • 跟我说回家,却还在外面鬼混,python程序员教你用微信给对方定位

    跟我说回家 却还在外面鬼混 其实很多情侣之间存在很多这样的信任问题 不相信他 去查岗 可能会恶化两人之间的关系 比如跟我说回家了 但是想知道他是否真的回家了 打电话 打视频查岗吗 今天教大家一个利用微信来给对方定位的黑科技 实现方法 其实实
  • python连接clickhouse,并实现对表内数据的增删改查

    基本信息 clickhouse 基本介绍可以参考 https clickhouse com docs zh python 连接 clickhouse 可以参考 https clickhouse com docs en integration
  • 网络 链路层

    数据链路层是计算机网络的底层 主要负责相邻设备之间的数据帧传输 链路层就是负责每一个相邻结点之间的数据传输 但是相邻设备之间也需要描述识别 主要是因为每一个设备都有可能有多个相邻的设备 这种识别在链路层中是通过MAC地址来实现的 MAC地址
  • C++ 类型转换

    文章目录 c语言中的类型转换 为什么C 需要四种类型转换 C 强制类型转换 static cast reinterpret cast const cast dynamic cast c语言中的类型转换 在C语言中 如果赋值运算符左右两侧类型
  • centos7 搭建深度学习环境

    本文引用转载自博客园 经实践可用 对原内容进行了删减调整 后续作者理解更深了 可能更新 一 安装NVIDA组件 1 安装CUDA CUDA又叫cuda toolkit 是NVIDA公司专门开发的一套接口 方便利用GPU做高速计算 主流的深度
  • 将hexo博客搭建在github上

    注册github账号并创建仓库 首先在github上注册账号 填写用户名 email 密码 会有验证通过邮箱发送给你 进行验证 选择仓库 创建一个和你用户名相同的仓库 如 你的 用户名 github io 必须以用户名开头 创建仓库 步骤
  • java8的函数式编程

    1 函数式接口 特定的一类接口 概念 接口里面有且只有一个抽象方法 对于接口里面的默认方法和静态方法不作限制 一般会有 FunctionalInterface修饰 可以没有 FunctionalInterface public interf