IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> lambda接口和Stream流 -> 正文阅读

[Java知识库]lambda接口和Stream流

Lambda表达式

体验lambda表达式

  • 需求:启动一个线程,在控制台输出一句话:多线程启动了
  • 方式1:
    • 定义一个类myRunnable实现Runnable接口,重写run方法
    • 创建myRunnable类的对象
    • 启动Thread类的对象,把MyRunnable的对象作为构造参数传递
    • 启动线程
  • 方式2
    • 隐名内部类的方式改进
  • 方式3
    • lambda表达式改进
public static void main(String[] args) {
    //实现类的方式实现需求
    MyRunnable myRunnable = new MyRunnable();
    Thread thread = new Thread(myRunnable);
    thread.start();
    //使用隐名内部类
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("多线程启动了");
        }
    }).start();
    //lambda表达式的方式改进
    new Thread(()->{
        System.out.println("多线程程序启动了");
    }).start();
}

lambda表达式的标准格式

隐名内部类中重写run()方法的代码分析

  • 方法形式参数为空,说明调用方法时不需要传递参数
  • 方法返回值为void,说明方法执行没有结果返回
  • 方法体中的内容,是我们具体要做的事情

lambda表达式的代码分析

  • ():里面没有内容,可以看成是方法形式参数为空
  • ->:用箭头指向后面要做的事情
  • {}:包含一段diam,我们称之为代码块,可以看成是方法体中的内容

组成lambda表达式的三要素:形式参数,箭头,代码块

lambda表达式的格式

  • 形式(形式参数)->{代码块}
  • 形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可
  • ->:由英文中划线和大于符号组成,固定写法,代表指向动作
  • 代码块:是我们具体要做的事情,也就是以前我们写的方法体内容

lambda表达式的练习

  • 有一个接口Eatable
  • 接口中有且仅有一个抽象方法eat()
public static void main(String[] args) {
    //在主方法中调用useEatable方法
    EatableImpl eatable = new EatableImpl();
    useEatable(eatable);

    //隐名内部类
    useEatable(new Eatable() {
        @Override
        public void eat() {
            System.out.println("一天一苹果,医生远离我");
        }
    });
    //lambda表达式
    useEatable(()->{
        System.out.println("一天一苹果,医生远离我");
    });
}
private static void useEatable(Eatable e){
    e.eat();
}

练习2:

  • 定义一个接口(Flyable)里面定义一个抽象方法:void fly(String s);
  • 定义一个测试类(FlyableDemo),在测试类中提供两个方法
    • 一个方法是:useFlyable(Flyable f)
    • 一个方法是主方法,在主方法中调用useFlyable方法
public static void main(String[] args) {
    useFlyable((String s)->{
        System.out.println(s);
    });
}
private static void useFlyable(Flyable flyable){
    flyable.fly("风和日丽,晴空万里");
}

lambda省略模式

省略规则:

  • 参数类型可以省略.但是有多个参数的情况下,不能只省略一个
  • 如果参数有且仅有一个,那么小括号可以省略
  • 如果代码块的语句只有一句,可以省略大括号和分号,甚至是return;
   public static  void main(String[] args) {
        //参数类型可以省略
        useAddable((x, y) -> {return x+y;});
        //但是有多个参数的情况下,不能只省略一个
//        useAddable((int x, y)->{return x+y});
        //如果参数有且仅有一个,那么小括号可以省略
        useFlyable(s -> {
            System.out.println(s);
        });
        //如果代码块的语句只有一条,可以省略大括号和分号
        useFlyable(s -> System.out.println(s));

        //如果代码块的语句只有一条,可以省略大括号和分号,如果有return,return也要省略
        useAddable(((x, y) ->  x+y));
    }
    private static void useFlyable(Flyable f){
        f.fly("风和日丽,晴空万里");
    }
    private static void useAddable(Addable a){
        int sum = a.add(10, 20);
        System.out.println(sum);
    }

lambda表达式的注意事项

注意事项

  • 使用Lambda必须要有接口,并且要求接口中有且仅有一个抽象方法
  • 必须有上下文环境,才能推导出Lambda对应的接口
    • 根据局部变量的得知Lambda对应的接口:Runnable r = ()->System.out.println("Lambda表达式");
    • 根据调用方法的参数得知Lambda对应的接口:new Thread(()->System.out.println("Lambda表达式").start();

Lambda表达式和隐名内部类的区别

所需类型不同

  • 隐名内部类:可以是接口,也可以是抽象类,还可以是具体类
  • Lambda表达式:只能是接口

使用限制不同

  • 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用隐名内部类
  • 如果接口中多于一个抽象方法,只能使用隐名内部类,而不能使用Lambda表达式

实现原理不同

  • 隐名内部类:编译之后,产生一个单独的.class字节码文件
  • Lambda表达式:编译之后,没有一个单独的.class字节码文件.对应的字节码在运行的时候动态生成

体验方法引用

在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方法:拿参数作操作

那么考虑一种情况:如果我们在Lambda所指定的操作方案,已经有地方存在相同方案,那么是否还有必要再写重复逻辑呢?答案是肯定没有必要

public static void main(String[] args) {
    usePrintable(s -> System.out.println(s));

    //方法引用符,::
    usePrintable(System.out::println);
    //可推导的就是可省略的
}
private static void usePrintable(Printable p){
    p.printString("爱生活,爱java");
}

方法引用符

方法引用符

  • ::该符号为引用运算符,而它所在的表达式内称为方法引用
  • 回顾我们在体验方法引用中的代码
    • Lambda表达式:usePrintable(s -> System.out.println(s));
      • 分析:拿到参数s之后通过Lambda表达式,传递给System.out.println方法引用去处理
    • 方法引用:usePrintable(System.out::println);
      • 分析:直接使用System.out中的println方法来去掉lambda,代码更加简洁

推导与省略

  • 如果使用Lambda,那么根据"可推导就是可省略"的原则,无需指定参数类型,也无需指定的重载形式,它们都将被自动推导
  • 如果使用方法引用,也是同样可以根据上下文进行推导
  • 方法引用是Lambda的孪生兄弟
public static void main(String[] args) {
    //在主方法中调用usePrintable2方法
    usePrintable2(i-> System.out.println(i));  //使用这个方法就是上下文环境
    //方法引用
    usePrintable2(System.out::println);
    Printable2 p = System.out::println;  //这个也是上下文环境
}
private static void usePrintable2(Printable2 printable2){
    printable2.printInt(10);
}

引用类方法

引用类方法,其实就是引用类的静态方法

  • 格式:类名::静态方法
  • 范例:Integer::parseInt
    • Integer类的方法: public static int parseInt(String s)将string转换为int类型数据

练习:

  • 定义一个接口(Converter)里面定义一个抽象方法
    • int convert(String s)
  • 定义一个测试类(ConverterDemo ),在测试类中提供两个方法
    • 一个方法是:useConverter(Converter c)
    • 一个是主方法,在主方法中调用useConverter方法
public static void main(String[] args) {
    useConverter(s -> Integer.parseInt(s));

    //引用类方法
    useConverter(Integer::parseInt);
    //Lambda表达式被类方法替代的时候,它的形式参数全部传递给静态方法作为参数
}
public static void useConverter(Converter c){
    int num = c.converter("666");
    System.out.println(num);
}

引用对象的实例方法

引用对象的实例方法,其实就引用类中的成员方法

  • 格式:对象::成员方法
  • 范例: "HelloWorld"::toUpperCase (这个是错误的,因为这个没有参数),具体看例子
    • String类中的方法:public String toUpperCase()将此String所有字符转换为大写

练习

  • 定义一个类(PrintString)里面定义一个方法
    • public void printUpper(String s):把字符串参数编程大写的数据,然后在控制台输出
  • 定义一个接口(Pointer)里面定义一个抽象方法
    • void printUpperCase(String s)
public class PrintString {
    public void printUpper(String s) {
        String s1 = s.toUpperCase();
        System.out.println(s1);
    }
}
  • 定义测试类(PrinterDemo),在测试类中提供两个方法
    • 一个方法是:usePrinter(printer p)
    • 一个是主方法,在方法中调用usePrinter方法
public static void main(String[] args) {

    usePrinter(s -> System.out.println(s.toUpperCase()));
    //引用对象的实例方法
    usePrinter(new PrintString()::printUpper);
    //Lambda表达式被对象的实例方法替代的时候,它的形式参数全部传递给该方法作为参数.
}
private static void usePrinter(Printer p){
    p.printUpperCase("HelloWorld");
}

引用类的实例方法

引用类的实例方法,其实就是引用类的成员方法

  • 格式:类名::成员方法
  • 范例: String::substring (要被实现的类必须第一个形参参数,必须为引用类的类型,参考练习)
    • String类中的方法:public String substring(int beginIndex,int endIndex)
    • 从beginIndex开始到endIndex结束,截取字符串,返回一个子串,子串的长度为endIndex-beginIndex

练习

  • 定义一个接口(MyString),里面定义一个抽象方法:
    • String mySubString(String s,int x,int y)
  • 定义一个测试类(MyStringDemo),在测试类中提供两个方法
    • 一个方法是useMyString(MyString s)
    • 一个方法是主方法,在主方法中调用useMyString方法
public static void main(String[] args) {
    //lambda
    useMyString((s, x, y) -> s.substring(x,y));

    //调用应用类的实例方法
    useMyString(String::substring);
    //Lambda表达式被类的实例方法替代时候
    //第一个参数作为调用者
    //后面的参数全部传递给该方法作为参数
}
private static void useMyString(MyString s){
    String helloworld = s.myString("helloworld", 2, 5);
    System.out.println(helloworld);
}

//Lambda表达式被类的实例方法替代时候

//第一个参数作为调用者

//后面的参数全部传递给该方法作为参数

在来一个例子加强:

    • 引用类
public class Student {
    private String name;
    private int age;

    public Student build(String name,int age){
       return new Student(name,age);
    }

}
  • 接口
public interface StringBuilder2 {

    Student build(Student student ,String name,int age);
}
  • 实现
StringBuilder2 builder2 = Student::build;  
System.out.println(builder2.build(new Student(),"小青",20));

引用构造器

引用构造器,其实就是引用构造方法

  • 格式:类名::new
  • 范例:Student::new

练习

  • 定义一个类(Student),里面有两个成员变量(name,age)
    • 提供无参构造方法和带参构造方法,以及成员变量对应的get和set方法
public interface StudentBuilder {
    Student build(String name, int i);
}
  • 定义一个接口(StudentBuilder),里面定义一个抽象方法
    • Student build(String name,int age)

public interface StudentBuilder { Student build(String name, int i); }

  • 定义一个测试类(StudentDemo),在测试类中提供两个方法
    • 一个方法是useStudentBuilder(StudentBuilder s)
    • 一个是主方法,在主方法中调用useStudentBuilder方法
public static void main(String[] args) {
    //lambda
    useStudentBuilder((s,a)->new Student(s,a));

    //引用构造器
    useStudentBuilder(Student::new);
    //Lambda表达式被构造器替代的时候,它的形式参数全部传递给构造器作为参数
}
public static void useStudentBuilder(StudentBuilder b){
    Student student = b.build("林青霞", 20);
    System.out.println(student);
}

函数式接口

描述

函数式接口:有且仅有一个抽象方法的接口

java中的函数式编程体现就是Lambda表达式,所以函数式接口就是可以适用于Lambda使用的接口

只有确保接口中有且仅有一个抽象方法,java中的Lambda才能顺利地进行推导

如何检测一个接口是不是函数式接口呢?

  • @FunctionalInterface
  • 放在接口定义的上方:如果接口是函数式接口,编译通过,如果不是,编译失败

注意:

  • 我们自己定义函数式接口的时候,@FunctionalInterface是可选的,就算我不写这个注释,只要包中满足函数式接口定义的条件,也照样是函数式接口.但是,建议加上该注解

函数式接口作为方法的参数

需求

  • 定义一个类(RunnableDemo)在类中提供两个方法
    • 一个方法是:startThread(RUnnable r)方法参数Runnable是一个函数式接口
    • 一个方式是主方法,在主方法中调用startThread方法
 public static void main(String[] args) {
        //采用隐名内部类的方式
        startThread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"线程启动了");
            }
        });
        startThread(()->System.out.println(Thread.currentThread().getName()+"线程启动了"));
    }
    private static void startThread(Runnable r){
        Thread thread = new Thread(r);
        thread.start();
    }
}

函数式接口作为方法的返回值

需求

  • 定义一个类(ComparatorDemo),在类中提供方法
    • 一个方法是:Comparator getComparator()方法返回值Comparator是一个函数式接口
    • 一个方法是主方法,在主方法中调用getComparator方法
  • 如果方法的返回值是一个函数式接口,我们可以是还有Lambda表达式作为结果返回
public class ComparatorDemo {
    public static void main(String[] args) {
        //构造使用的场景;
        //定义集合,存储字符串元素
        ArrayList<String> strings = new ArrayList<>();
        strings.add("abb");
        strings.add("bbbb");
        strings.add("ddddddd");
        System.out.println(strings);

        Collections.sort(strings,getComparator());
        System.out.println(strings);
    }
    private static Comparator<String> getComparator(){
//       return new Comparator<String>() {
//            @Override
//            public int compare(String o1, String o2) {
//                return o2.length()-o1.length();
//            }
//        };
        return (s,x)->s.length()-x.length();
    }
}

常用的函数式接口

java8在java.util..function包下预定义了大量的函数式接口供我们实现

Supplier接口

Supplier接口:包含一个无参的方法

  • T get():获得结果
  • 该方法不需要参数,它会按照某种实现逻辑(由lambda表达式实现)返回一个数据
  • Supplier接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用

public static void main(String[] args) {
    String string = getString(() -> "林青霞");
    System.out.println(string);
    Integer integer = getInteger(() -> 30);
    System.out.println(integer);
}
//定义一个方法,返回一个整数数据
private static Integer getInteger(Supplier<Integer> supplier){
    return supplier.get();
}
//定义一个方法,返回一个字符串数据
public static String getString(Supplier<String> sup){
    return sup.get();
}

Consumer:包含两个方法

  • void accept(T t)对给定的参数执行此操作
  • default Consumer andThen(Consumer after):返回一个组合的Consumer,执行此操作,然后执行after操作
  • Consumer 接口也被称为消费型接口,它消费的数据类型由泛型指定
public static void main(String[] args) {
    operatorString("林青霞",System.out::println);
    operatorString("林青霞",s -> System.out.println(new StringBuilder(s).reverse().toString()));
    System.out.println("-------------------------");
    operatorString1("林青霞",s -> System.out.println(s),s -> new StringBuilder(s).reverse().toString());
}

//定义一个方法,用不同的方法消费同一个字符串消费同一个字符串数据两次
private static void operatorString1(String name, Consumer<String> con,Consumer<String> con2){
    con.accept(name);
    con2.andThen(con2).accept(name);
}
//定义一个方法,消费一个字符串数据
private static void operatorString(String name, Consumer<String> con){
    con.accept(name);
}

Predicate接口

Predicat:常用四个方法

  • boolean test(T t):对给定的参数进行判断(判断逻辑由lambda表达式实现)返回一个布尔值
  • default Predicate negate():返回一个逻辑的否定,对应逻辑非
  • default Predicate and(Predicate other) :返回一个组合判断,对应短路与
  • default Predicate or(Predicate other):返回一个组合判断.对应短路或
  • Predicate接口通常用于判断参数是否满足指定的条件
public static void main(String[] args) {
    boolean b0 = checkString("hello", s -> s.length() > 8);
    boolean b1 = checkString("hello2222222222", s -> s.length() > 8);
    System.out.println(b0);
    System.out.println(b1);
    System.out.println(checkString2("123456952",s -> s.length()>3,s -> s.length()<8));
}

//判断给定的字符串是否满足要求
private static boolean checkString(String s, Predicate<String> predicate){
    //negate否定
    return predicate.negate().test(s);
}

//判断给定的字符串是否满足要求
private static boolean checkString2(String s, Predicate<String> predicate, Predicate<String> predicate2){
    return predicate.and(predicate2).test(s);
}

练习

public static void main(String[] args) {
    //筛选出姓名长度大于2,年龄大于33
    String[] strings = {"林青霞,30","柳岩,34","张曼玉,35","貂蝉,31","王祖贤,33"};
    cheekPerson(strings,s -> s.split(",")[0].length()>2,s -> s.split(",")[1].compareTo("33")>0);
}

private static void cheekPerson(String[] strings, Predicate<String> p1,Predicate<String> p2){
    for (String s :strings){
        boolean test = p1.and(p2).test(s);
        if (test){
            System.out.println(s);
        }
    }
}

Function接口

Function:常用两个方法(T是参数,T是返回类型)

  • R apply(T t):将次函数应用于给定的参数
  • default Function andThen(Function after):返回一个组合函数,首先将该函数引用与输入,然后将after函数应用于结果
  • Function接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现),然后返回一个新值.
public static void main(String[] args) {
        convert("123",s -> Integer.parseInt(s));
        convert("567",Integer::parseInt);

        convert2(123,integer -> String.valueOf(integer+566));

        convert3("1",s -> Integer.parseInt(s)+3,String::valueOf);
    }
    //定义一个方法,把一个字符串转换成int类型,在控制台输出
    private static void convert(String s, Function<String,Integer> func){
        Integer apply = func.apply(s);
        System.out.println(apply);
    }
    //定义一个方法,把一个int类型的数据加上一个整数之后,转为字符串在控制台输出
    private static void convert2(Integer i, Function<Integer,String> func){
        String apply = func.apply(i);
        System.out.println(apply);
    }
    //定义一个方法,把一个字符串转换为int类型,把int类型的数据加上一个整数之后,转为字符串在控制台输出
    private static void convert3(String s, Function<String,Integer> func,Function<Integer,String> func2){
//        Integer apply = func.apply(s);
//        String apply1 = func2.apply(apply);
//        System.out.println(apply1);
        String apply = func.andThen(func2).apply(s);
        System.out.println(apply);
    }

练习

/*
    String s = "林青霞,30,
    请按我指定的要求进行操作:
       1.将字符串截取得到数字年龄部分
       2.将上一步的年龄转换称为int类型的数据
       3.将上一步的int数据加70,得到一个int结果,在控制台输出
 */
public static void main(String[] args) {
    convert("林青霞,30",s -> s.split(",")[1], //截取年龄
            s -> Integer.parseInt(s),//将字符串转为int类型
            integer -> integer+70); //将数字加70
}
private static void convert(String s, Function<String,String> fun1,Function<String,Integer> fun2, Function<Integer,Integer> fun3){
    Integer apply = fun1.andThen(fun2).andThen(fun3).apply(s);
    System.out.println(apply);
}

接口

接口组成更新描述

接口的组成

  • 常量
    • public static final
  • 抽象方法
    • public abstract
  • 静态方法(java8)
  • 默认方法(java8)
  • 私有方法(java9)

默认方法

接口默认方法的定义格式:

  • 格式:public default 返回值类型 方法名(参数列表){}
  • 范例:public default void show3(){可以有代码}
public interface MyInterface {
    void show1();

    void show2();

//    void show3();
    public default void show3(){
        System.out.println("sxa");
    }

接口中静态方法

接口中静态方法的定义格式

  • 格式:public static 返回值类型 方法名(参数列表){}
  • 范例:public static void show(){}

接口中静态方法的注意事项:

  • 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
  • public可以省略,static不能省略;

接口中私有方法

java9中新增了带方法体的私有方法,这其实在java8就埋下了伏笔:java8允许在接口中定义带方法的默认方法和静态方法.这样可能就引发一个问题:当两个默认方法或者静态方法中包含一段相同的代码实现时,程序必然考虑将这段实现代码抽取成一个共性方法,而这个共性方法是不需要让别使用的,因此用私有给隐藏起来,这就是java9增加私有方法的必然性

接口中私有方法的定义格式:

  • 格式1:private 返回值类型 方法名(参数列表){}
  • 范例1:private void show(){}
  • 格式2:private static 返回值类型 方法名(参数列表){}
  • 范例2:private static void method(){}

接口中私有方法的注意事项:

  • 默认方法可以调用私有的静态方法和非静态方法
  • 静态方法只能调用私有的静态方法

Stream流

体验Stream流

需求:

  • 创建一个集合,存储多个字符串元素
  • 把集合中所有以"张"开头的元素存储到一个新的集合
  • 把"张"开头的集合中的长度为3的元素存储到一个新的集合
  • 遍历上一步得到的集合
 public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();

        list.add("林青霞");
        list.add("张曼玉");
        list.add("王祖贤");
        list.add("柳岩");
        list.add("张敏");
        list.add("张无忌");
        //把集合中所有以"张"开头的元素存储到一个新的集合
        ArrayList<String> zhangList = new ArrayList<>();
        for (String s:list){
            if (s.startsWith("张")){
                zhangList.add(s);
            }
        }
        System.out.println(zhangList);
        //把"张"开头的集合汇总的长度为3的元素存储到一个新的集合
        ArrayList<String> threeList = new ArrayList<>();
        for (String zhang: zhangList){
            if (zhang.length()==3){
                threeList.add(zhang);
            }
        }
//        System.out.println(threeList);
        //遍历上一步去得到的集合
        threeList.forEach(s -> System.out.println(s));
        System.out.println("------------");
        //Stream流进行改写
        list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length()==3).forEach(s -> System.out.println(s));
    }

使用Stream流的方式完成过滤操作

  • list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length()==3).forEach(s -> System.out.println(s));
  • 直接阅读代码的字面意思即可完美展示无关逻辑方法的语义:生成流,过滤张性,过滤长度为3,逐一打印
  • Stream流把真正的函数式编程风格引入到java中

Stream流的生成方式

Stream流的使用

  • 生成流
    • 通过数据源(集合,数组等)生成流
    • list.stream()
  • 中间操作
    • 一个流后面可以跟随零个或多个中间操作,其目的主要是打开流,做出某种程度的数据过滤/映射,然后返回一个新的流,交给下一个操作使用
    • filter()
  • 终结操作
    • 一个流只能有一个终结操作,党政操作执行后,流就被使用"光"了,无法再被操作,所以这必定是流的最后一个操作
    • forEach()

Stream流的常见生成方式

  • Collection体系的集合可以使用默认方法stream()生成流
    • default Stream stream();
  • Map体系的集合间接的生成流
  • 数组可以通过Stream接口的静态方法of(T...values)生成流
//Collection体系的集合可以使用默认方法stream()生成流
ArrayList<String> list = new ArrayList<>();
Stream<String> stream = list.stream();

HashSet<String> strings = new HashSet<>();
Stream<String> stream1 = strings.stream();

//Map体系的集合间接生成流
HashMap<String, String> stringStringHashMap = new HashMap<>();
Stream<String> keyStream = stringStringHashMap.keySet().stream();
Stream<String> stream2 = stringStringHashMap.values().stream();
Stream<Map.Entry<String, String>> stream3 = stringStringHashMap.entrySet().stream();

//数组可以通过Stream接口的静态方法of(T...values)生成流
String[] array={"hello","world","java"};
Stream<String> array1 = Stream.of(array);
Stream<Integer> integerStream = Stream.of(10, 20, 20, 30);

Stream filter(Predicate predicate):用于对流中的数据进行过滤

Predicate接口中的方法 Boolean test(T t) 对给定的参数进行判断,返回一个布尔值

public static void main(String[] args) {
    ArrayList<String> list = new ArrayList<>();

    list.add("林青霞");
    list.add("张曼玉");
    list.add("王祖贤");
    list.add("柳岩");
    list.add("张敏");
    list.add("张无忌");

    //需求:把list集合中以张开头的元素在控制台输出
    list.stream().filter(s -> s.startsWith("张")).forEach(System.out::println);

    //需求,list集合中长度为3的元素在控制台输出
    list.stream().filter(s -> s.length()==3).forEach(System.out::println);
    //把list集合中以张开头的,长度为3的元素在控制台输出
    list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length()==3).forEach(System.out::println);
}

limit和skip方法

Stream limit(long maxSize):返回此流中的元素组成的流,截取前指定参数个数的数据

Stream skip(long n):跳过指定参数个数的数据,返回该留的剩余元素组成的流

public static void main(String[] args) {
    ArrayList<String> list = new ArrayList<>();

    list.add("林青霞");
    list.add("张曼玉");
    list.add("王祖贤");
    list.add("柳岩");
    list.add("张敏");
    list.add("张无忌");

    //取前三个数据在控制台输出
    list.stream().limit(3).forEach(System.out::println);
    System.out.println("--------");
    //跳过前三个元素,把剩下的元素在控制台输出
    list.stream().skip(3).forEach(System.out::println);
    System.out.println("-------");
    //跳过2个元素,把剩下的元素中前2个在控制台输出
    list.stream().skip(2).limit(2).forEach(System.out::println);
}

concat和distinct方法

static Stream concat(Stream a,Stream b):合并a和b两个流为一个流

Stream distinct():返回由该流的不同元素(根据Object.equals(Object))组成的流

public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();

        list.add("林青霞");
        list.add("张曼玉");
        list.add("王祖贤");
        list.add("柳岩");
        list.add("张敏");
        list.add("张无忌");

        //需求1,取前4个数据组成一个流
        Stream<String> limit = list.stream().limit(4);
        //需求2,取跳过2个数据组成一个流
        Stream<String> skip = list.stream().skip(2);
        //需求3,合并需求1和需求2得到的流,并把结果输出
//        Stream.concat(limit,skip).forEach(System.out::println);
        //需求4,合并需求1和需求2得到的流,并把结果输出在控制台,要求字符串元素不能重复
        Stream.concat(limit,skip).distinct().forEach(System.out::println);
    }

sorted方法

  • Stream sorted(Comparator comparator):返回由该流的元素组成的流,根据提供的Comparator进行排序
public static void main(String[] args) {
    ArrayList<String> list = new ArrayList<>();

    list.add("linqinxia");
    list.add("zhangmanyu");
    list.add("wangzuxian");
    list.add("liuyan");
    list.add("zhangmin");
    list.add("zhnagwuji");

    //按照字母顺序把数据在控制台输出
    list.stream().sorted((x,y)->x.compareTo(y)).forEach(System.out::println);
    System.out.println("---------------");
    //按照字母长度把数据在控制台输出
    list.stream().sorted((x,y)->x.length()-y.length()).forEach(System.out::println);
}

map和mapToInt方法

  • Stream map(Function mapper):返回给定函数应用于此流的元素的结果组成的流
  • IntStream mapToInt(ToIntFunction mapper):返回一个IntStream其中包含将给定函数应用于此流的元素的结果
    • IntStream :表示原始int流
    • ToIntFunction接口中的方法 int applyAsInt(T value)
public static void main(String[] args) {
    ArrayList<String> list = new ArrayList<>();

    list.add("20");
    list.add("30");
    list.add("15");
    list.add("56");
    list.add("40");
    list.add("10");

    //需求:将集合中的字符串数据转换为整数之后在控制台输出
    list.stream().map(Integer::parseInt).forEach(System.out::println);
    list.stream().mapToInt(Integer::parseInt).forEach(System.out::println);
    //int sum()返回此流中元素的总和
int sum = list.stream().mapToInt(Integer::parseInt).sum();
}

forEach和count方法

Stream流的常见终结操作方法

  • void forEach(Consumer action):对此流的每个元素执行操作
    • Consumer接口中的方法 void accept(T t):对给定的参数执行此操作
  • long count():返回此流中的元素数
public static void main(String[] args) {
    ArrayList<String> list = new ArrayList<>();

    list.add("林青霞");
    list.add("张曼玉");
    list.add("王祖贤");
    list.add("柳岩");
    list.add("张敏");
    list.add("张无忌");
    //需求1:把集合中的元素在控制台输出
    list.stream().forEach(System.out::println);
    //统计集合中以张开头的元素
    long 张 = list.stream().filter(s -> s.startsWith("张")).count();
    System.out.println(张);
}

练习

现有两个ArrayList集合,分布存储6名男演员名称和女演员名称,要求完成如下操作

  • 男演员只要名字为3个字的前三人
  • 女演员只有姓林的,并且不要第一个
  • 把过滤后的男演员姓名和女演员姓名合并到一起
  • 把上一步操作后的元素作为构造方法的参数创建演员对象,遍历数据
    • 演员类Actor,里面有一个成员变量,一个带参构造方法,以及成员变量对应的get/set方法
public static void main(String[] args) {
    ArrayList<String> man = new ArrayList<>();
    man.add("周仁发");
    man.add("成龙");
    man.add("刘德华");
    man.add("吴京");
    man.add("周星驰");
    man.add("李连杰");
    ArrayList<String> list = new ArrayList<>();
    list.add("林青霞");
    list.add("张曼玉");
    list.add("王祖贤");
    list.add("柳岩");
    list.add("张敏");
    list.add("张无忌");
    //男演员只有名字为3个字的前三人
    Stream<String> limit = man.stream().filter(s -> s.length() == 3).limit(3);
    //女演员只要姓林的,并且不要第一个人
    Stream<String> lin = list.stream().filter(s -> s.startsWith("林")).skip(1);
    //把过滤后的男演员姓名和女演员姓名合并到一起
    Stream<String> concat = Stream.concat(lin, limit);
    //把上一步操作后的元素作为构造方法的参数创建演员对象,遍历数据
    concat.map(Actor::new).forEach(System.out::println);
}


package seventh;

/**
 * @author keep
 * @create 2022/4/17
 */
public class Actor {
    private String name;

    public String getName() {
        return name;
    }


    public void setName(String name) {
        this.name = name;
    }

    public Actor(String name) {
        this.name = name;
    }
    public Actor() {
    }

    @Override
    public String toString() {
        return "Actor{" +
                "name='" + name + '\'' +
                '}';
    }
}

流的收集

对数据使用Stream流的方式操作完成后,把流的数据收集到集合中

Stream流 的收集方法

  • R collect(Collector collector)
  • 收集方法的参数是一个Collector接口

工具类Collectors提供了具体的收集方式

  • public static Collector list():把元素收集到list集合中
  • public static Collector toSet()把元素收集到Set集合中
  • public static Collector toMap(Function keyMapper,Function valueMaooer):把元素收集到集合中
ArrayList<String> list = new ArrayList<>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
list.add("张敏");
list.add("张无忌");
//得到名字为3个字的流
Stream<String> stringStream = list.stream().filter(s -> s.length() == 3);
//使用Stream流操作完毕的数据收集到list集合中并遍历
List<String> collect = stringStream.collect(Collectors.toList());
collect.forEach(System.out::println);

//创建set集合对象
HashSet<Integer> integers = new HashSet<>();
integers.add(10);
integers.add(20);
integers.add(30);
integers.add(40);
//得到年龄大于25的流
Stream<Integer> integerStream = integers.stream().filter(integer -> integer > 25);
//把使用Stream流操作完毕的数据收集到set集合中并遍历
Set<Integer> collect1 = integerStream.collect(Collectors.toSet());
collect1.forEach(System.out::println);

//定义一个字符串数组,每一个字符串数据由姓名数据和年龄数据组合而成
String[] strArray={"林青霞,30","张曼玉,35","王祖贤,33","小雪,20"};
//得到字符串年龄数据大于25的流
Stream<String> stringStream1 = Stream.of(strArray).filter(s -> Integer.parseInt(s.split(",")[1]) > 25);
//使用Stream流操作完成的数据收集到Map集合中并遍历,字符串中的姓名作为键,年龄作值
Map<String, Integer> collect2 = stringStream1.collect(Collectors.toMap(s -> s.split(",")[0], s -> Integer.parseInt(s.split(",")[1])));
collect2.forEach((x,y)->{
    System.out.println(x+y);
});

?

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-18 17:27:33  更:2022-04-18 17:28:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 5:02:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码