目錄
一、Lambda表達式 
1.1 函數式程式設計思想概述
1.2 體驗Lambda表達式
一個方法是主方法,在主方法中呼叫useAddable方法
1.6 Lambda表达式的注意事项
1.7 Lambda表达式和匿名内部类的区别
二、接口组成更新
1.1 接口组成更新概述
1.2 接口中默认方法
1.3 接口中静态方法
1.4 接口中私有方法
三、方法引用
1.1 体验方法引用
1.2 方法引用符
1.3 Lambda表达式支持的方法引用
1.4 引用类方法
1.5 引用对象的实例方法
1.6 引用类的实例方法
1.7 引用构造器
四、函数式接口
1.1 函数接口概述
1.2 函数式接口作为方法的参数
1.3 函数式接口作为方法的返回值
1.4 常用的函数式接口
1.5 Supplier接口
1.6 Consumer接口
1.7 Predicate接口
1.8 Function接口
五、Stream流
1.1 体验Stream流
1.2 Stream流的生成方式
1.3 Stream流的常见中间操作方法
1.4 Stream流的常见终结操作方法
1.5 Stream流的练习
1.6 Stream流的收集操作
首頁 Java java教程 Java函數式程式設計實例分析

Java函數式程式設計實例分析

Apr 28, 2023 am 09:28 AM
java

這篇「Java函數式程式設計實例分析」文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鏡價值,希望大家閱讀完這篇文章能有所收穫,以下我們一起來看看這篇「Java函數式程式設計實例分析」文章吧。

一、Lambda表達式 

1.1 函數式程式設計思想概述

在數學中,函數就是有輸入量、輸出量的一套計算方案,也就是「拿資料做操作」

物件導向思想強調「必須透過物件的形式來做事情」

函數式思想強調則金量忽略物件導向的複雜語句:「強調做什麼,而不是以什麼形式去做」

而我們要學習的Lambda表達式就是函數式思想的體現

1.2 體驗Lambda表達式

需求:啟動一個線程,在控制台輸出一句話:多線程程式啟動了

方式1:

  • 定義一個類別MyRunnable接口,重寫run方法

  • 建立MyRunnable類別的物件

  • 建立Thread類別對象,把MyRunnable的物件當作建構參數傳遞

  • 啟動執行緒

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("多线程程序启动了");
    }
}
登入後複製
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
登入後複製

方式2:

##在方式1的基礎上進行改進,使用匿名內部類別的方式

      new Thread(new Runnable() {
           @Override
            public void run() {
                System.out.println("多线程程序启动了");
            }
        }).start();
登入後複製
登入後複製

方式3:

#Lambda表達式的方式改進:

        new Thread(() -> {
            System.out.println("多线程程序启动了");
        }).start();
登入後複製
登入後複製

1.3 Lambda表達式的標準格式

匿名內部類別中重寫run()方法的程式碼分析:

  • 方法形式參數為空,說明呼叫方法時不需要傳遞參數

  • 方法傳回值類型為void,說明方法執行沒有結果回傳

  • 方法體中的內容,是我們具體要做的事情

  •       new Thread(new Runnable() {
               @Override
                public void run() {
                    System.out.println("多线程程序启动了");
                }
            }).start();
    登入後複製
    登入後複製

Lambda表達式的程式碼分析:

  • ## ():

    裡面沒有內容,可以看成是方法形式參數為空

  • #->:

    用箭頭指向後面要做的事情

  • {}:

    包含一段程式碼,我們稱之為程式碼區塊,可以看成是方法體中的內容

            new Thread(() -> {
                System.out.println("多线程程序启动了");
            }).start();
    登入後複製
    登入後複製
  • 組成Lambda表達式的三個要素:形式參數、箭頭、程式碼區塊

Lambda表達式的格式:

  • # #格式:(形式參數)->{程式碼區塊}

  • #形式參數:如果有多個參數,參數之間用逗號隔開;若沒有參數,留空即可

  • ->:由英文中畫線和大於符號組成,固定寫法。代表指向動作

程式碼區塊:

是我們具體要做的事情,也就是以前我們寫的方法體內容

  • #1.4 Lambda表達式的練習

  • Lambda表達式的使用前提
  • 有一個介面

##介面中有且只有一個抽象方法

  • 練習1:

  • 定義一個介面(Eatable),裡面定義一個抽象方法:void eat();

    • 定義一個測試類別(EatableDemo),在測試類別中提供
    • 兩個方法:

    • 一個方法是:

      useEatable(Eatable e)

一個方法是主方法,在主方法中呼叫useEatable方法

定義一個介面:

public interface Eatable {
    void eat();
}
登入後複製
方式一:

傳統介面實作類別

public class EatableImpl implements Eatable{
    @Override
    public void eat() {
        System.out.println("一日三餐,必不可少");
    }
}
登入後複製
public class EatableDemo{
    public static void main(String[] args) {
        Eatable eatable = new EatableImpl();
        eatable.eat();
    }

    private static void useEatable(Eatable eatable){
        eatable.eat();
    }
}
登入後複製
#方式2:

匿名內部類別

public class EatableDemo{
    public static void main(String[] args) {
        useEatable(new Eatable() {
            @Override
            public void eat() {
                System.out.println("一日三餐,必不可少");
            }
        });
    }
    private static void useEatable(Eatable eatable){
        eatable.eat();
    }
}
登入後複製

方式3:Lambda表達式

public class EatableDemo{
    public static void main(String[] args) {
        useEatable(()->{
            System.out.println("一日三餐,必不可少");
        });
    }
    private static void useEatable(Eatable eatable){
        eatable.eat();
    }
}
登入後複製

運行結果皆相同
  • 練習2:

  • 定義一個介面(Flyable),在其中定義一個抽象方法:void fiy(String s);
    • 定義一個測試類別(FlyableDemo),在測試類別中提供兩個方法

    • #一個方法是:

      useFlyable(Flyable f)

一個方法是主方法,在主方法中呼叫useFlayable方法

    #
    public interface Flyable {
        void fly(String s);
    }
    登入後複製
    public class FlyableDemo {
        public static void main(String[] args) {
    
            useFlyable(new Flyable() {
                @Override
                public void fly(String s) {
                    System.out.println(s);
                    System.out.println("飞机可以起飞");
                }
            });
            System.out.println("--------------------");
            useFlyable((String s)->{
                System.out.println(s);
                System.out.println("飞机可以起飞");
            });
        }
        private static void useFlyable(Flyable flyable){
            flyable.fly("风和日丽,晴空万里");
        }
    }
    登入後複製
  • 練習3:

  • #定義一個介面(Addable),裡面定義一個抽象方法:int add(int x,int y);
    • 定義一個測試類別(AddableDemo ),在測試類別中提供兩個方法

    • 一個方法是:

      useAddable(Addable a)

一個方法是主方法,在主方法中呼叫useAddable方法

public interface Addable {
    int add(int x,int y);
}
登入後複製
public class AddableDemo {
    public static void main(String[] args) {
        useAddable(new Addable() {
            @Override
            public int add(int x, int y) {
                return x + y;
            }
        });

        useAddable((int x,int y)->{
            return x + y;
        });
    }
    private static void useAddable(Addable addable) {
        int sum = addable.add(10, 20);
        System.out.println(sum);
    }
}
登入後複製

1.5 Lambda表達式的省略模式
  • ##省略規則:

    #########參數型別可以省略。如果有多個參數的情況下,不能只省略一個###
  • 如果参数有且仅有一个,那么小括号可以省略

  • 如果代码块的语句只有一条,可以省略大括号和分号,甚至时return

public class LambdaDemo5 {
    public static void main(String[] args) {
        //参数类型可以省略
        useAddable((x, y) -> {
            return x + y;
        });
        System.out.println("------------------------");
        //如果只有一个参数,小括号也可以省略
        useFlyable(s -> {
            System.out.println(s);
        });
        System.out.println("------------------------");
        //如果代码块的语句只有一条,可以省略大括号和分号(有return时要把return也去掉)
        useFlyable(s ->
            System.out.println(s)
        );
        useAddable((x,y)->x+y);
    }
    private static void useFlyable(Flyable flyable) {
        flyable.fly("风和日丽,晴空万里");
    }
    private static void useAddable(Addable addable) {
        int sum = addable.add(10, 20);
        System.out.println(sum);
    }
}
登入後複製

接口类参考1.4

1.6 Lambda表达式的注意事项

注意事项:

  • 使用Lambda必须要有接口,并且要求接口中有且仅有一个抽象的方法

  • 必须有上下文环境,才能推导出Lambda对应的接口

    • 根据局部变量的赋值得知Lambda对应的接口:Runnable r =() ->System.out.println(“Lambda表达式”);

    • 根据调用方法的参数得知Lambda对应的接口:new Thread(()->System.out.println(“Lambda表达式”)).start();

public interface Inter {
    void show();
}
登入後複製
public class LambdaDemo6 {
    public static void main(String[] args) {
        useInter(()->
                System.out.println("Lambda表达式")
        );
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("匿名内部类");
            }
        }).start();
        Runnable r = () -> System.out.println("Lambda表达式");
        new Thread(r).start();

        new Thread(()->
            System.out.println("Lambda表达式")
        ).start();
    }
    private static void useInter(Inter inter){
        inter.show();
    }
}
登入後複製

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

所需类型不同:

  • 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类

  • Lambda表达式:只能是接口

使用限制不同:

  • 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类

  • 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式

实现原理不同:

  • 匿名内部类:编译之后,产生一个单独的.class字节码文件

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

二、接口组成更新

1.1 接口组成更新概述

接口的组成:

  • 常量:public static final

  • 抽象方法:public abstract

  • 默认方法(Java 8)

  • 静态方法(Java 8)

  • 私有方法 (Java 8)

1.2 接口中默认方法

接口中默认方法得定义格式:

  • 格式:public default 返回值类型 方法名(参数列表){}

  • 范例:public default void show3(){}

接口中默认方法的注意事项:

  • 默认方法不是抽象方法,所以不强制被重写。但是可以被重写,重写的时候去掉default关键字

  • public可以省略,default不能重写

public interface MyInterface {
    void show1();

    void show2();

    default void show3(){
        System.out.println("show3");
    }
}
登入後複製
public class MyInterfaceImplOne implements MyInterface{
    @Override
    public void show1() {
        System.out.println("One show1");
    }

    @Override
    public void show2() {
        System.out.println("One show2");
    }
}
登入後複製
public class MyInterfaceImplTwo implements MyInterface{
    @Override
    public void show1() {
        System.out.println("Two show1");
    }

    @Override
    public void show2() {
        System.out.println("Two show2");
    }
}
登入後複製
public class InterfaceDemo {
    public static void main(String[] args) {
        MyInterface myInterface = new MyInterfaceImplOne();
        myInterface.show1();
        myInterface.show2();
        myInterface.show3();
        System.out.println("------------------");
        MyInterface myInterface2 = new MyInterfaceImplTwo();
        myInterface2.show1();
        myInterface2.show2();
        myInterface2.show3();
    }
}
登入後複製

运行结果:

One show1
One show2
show3
------------------
Two show1
Two show2
show3

1.3 接口中静态方法

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

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

  • 范例:public static void show(){ }

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

  • 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用

  • public可以省略,static不能省略

public interface Inter {
    void show();
    default void method() {
        System.out.println("Inter 中的默认方法执行了");
    }
    public static void test(){
        System.out.println("Inter 中的静态方法执行了");
    }
}
登入後複製
public class InterImpl implements Inter{
    @Override
    public void show() {
        System.out.println("show方法执行了");
    }
}
登入後複製
public class InterDemo {                                           
    public static void main(String[] args) {                       
        Inter inter = new InterImpl();                             
        inter.show();                                             
        inter.method();                                           
        Inter.test();                                             
    }                                                             
}
登入後複製

1.4 接口中私有方法

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

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

  • 格式1:private 返回值类型方法名(参数列表){ }

  • 范例1:private void show(){ }

  • 格式2:private static 返回值类型 方法名(参数列表){ }

  • 范例2:private static void method(){ }

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

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

  • 静态方法只能调用私有的静态方法

public interface Inter {
    default void show1(){
        System.out.println("show1开始执行");
        method();
        System.out.println("show1结束执行");
    }
    default void show2(){
        System.out.println("show2开始执行");
        method();
        System.out.println("show2结束执行");
    }
    static void method1(){
        System.out.println("method1开始执行");
        method();
        System.out.println("method1结束执行");
    }
    static void method2(){
        System.out.println("method2开始执行");
        method();
        System.out.println("method2结束执行");
    }
    static void method(){
        System.out.println("初级工程师");
        System.out.println("中级工程师");
        System.out.println("高级工程师");
    }
}
登入後複製
public class InterImpl implements Inter{
}
登入後複製
public class InterDemo {
    public static void main(String[] args) {
        Inter inter = new InterImpl();
        inter.show1();
        System.out.println("------------------------");
        inter.show2();
        System.out.println("------------------------");
        Inter.method1();
        System.out.println("------------------------");
        Inter.method2();
    }
}
登入後複製

三、方法引用

1.1 体验方法引用

通过方法引用来使用已经存在的方案

public interface Printable {
    void printString(String s);
}
登入後複製
public class PrintableDemo {
    public static void main(String[] args) {
        usePrintable(s->
            System.out.println(s)
        );

        usePrintable(System.out::println);
    }
    private static void usePrintable(Printable p){
        p.printString("hello world");
    }
}
登入後複製

1.2 方法引用符

  • ::该符号为引用运算符,而它所在表达式被称为方法引用符

  • Lambda表达式:usePrintable(s->System.out.println(s));

分析:拿到参数s之后通过Lambda表达式,传递给System.out.println方法去处理

  • 方法引用:usePrintable(System.out::println);

分析:直接使用System.out中的println方法来取代Lambda,代码更加的简洁

推导与省略:

  • 如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重载形式,它们都将被自动推导

  • 如果使用方法引用,也同要可以根据上下文进行推导

  • 方法引用是Lambda的孪生兄弟

public interface Printable {
    void printInt(int i);
}
登入後複製
public class PrintableDemo2 {
    public static void main(String[] args) {
        usePrintable(i -> System.out.println(i));

        usePrintable(System.out::println);
    }
    private static void usePrintable(Printable printable){
        printable.printInt(1);
    }
}
登入後複製

1.3 Lambda表达式支持的方法引用

常见的引用方式:

  • 引用类方法

  • 引用对象的实例方法

  • 引用类的实例方法

  • 引用构造器

1.4 引用类方法

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

  • 格式:类名::静态方法

  • 范例:Integer::parseInt

    • Integer类的方法:public static int parsenInt(String s),将此String转换为int类型数据

    • Lambda表达式被类方法替代的时候,它的形式参数全部传递给静态方法作为参数

public interface Converter {
    int convert(String s);
}
登入後複製
public class ConverterDemo {
    public static void main(String[] args) {
        useConverter(s -> Integer.parseInt(s));
        useConverter(Integer::parseInt);
    }
    private static void useConverter(Converter c) {
        int number = c.convert("666");
        System.out.println(number);
    }
}
登入後複製

1.5 引用对象的实例方法

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

  • 格式:对象::成员方法

  • 范例:“HelloWorld"::toUpperCase

  • String 类中的方法:public String toUpperCase()将此String所有字符转为大写

  • Lambda表达式被对象的实例方法替代的时候,它的形式参数全部传递给该方法作为参数

public interface Printer {
    void printUpperCase(String s);
}
登入後複製
public class PrintString {
    public void printUpper(String s){
        String result = s.toUpperCase();
        System.out.println(result);
    }
}
登入後複製
public class PrinterDemo {
    public static void main(String[] args) {
        usePrinter(s -> System.out.println(s.toUpperCase()));

        PrintString printString = new PrintString();
        usePrinter(printString::printUpper);
    }
    private static void usePrinter(Printer printer) {
        printer.printUpperCase("HelloWorld");
    }
}
登入後複製

1.6 引用类的实例方法

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

  • 格式:类名::成员方法

  • 范例:String::substring

  • String类中的方法:public String subString(int beginIndex,int endIndex)从beginIndex开始到endIndex结束,截取字符串。返回一个子串,字串的长度为endIndex-beginIndex

  • Lambda表达式被类的实例方法替代的时候,第一个参数作为调用者,后面的参数全部传递给该方法作为参数

public interface MyString {
    String mySubString(String s, int x, int y);
}
登入後複製
public class MyStringDemo {
    public static void main(String[] args) {
        useMyString((s, x, y) -> s.substring(x, y));

        useMyString(String::substring);
    }
    private static void useMyString(MyString myString){
        String s = myString.mySubString("HelloWorld", 5, 10);
        System.out.println(s);
    }
}
登入後複製

1.7 引用构造器

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

  • 格式:类名::new

  • 范例:Student::new

  • Lambda表达式被构造器代替的时候,它的形式参数全部传递给构造器作为参数

public class 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;
    }
    public Student() {
    }
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
登入後複製
public interface StudentBuilder {
    Student build(String name,int age);
}
登入後複製
public class StudentDemo {
    public static void main(String[] args) {
        useStudentBuilder((name, age) -> new Student(name,age));

        useStudentBuilder(Student::new);
    }

    private static void useStudentBuilder(StudentBuilder studentBuilder){
        Student student = studentBuilder.build("xuanxuan", 22);
        System.out.println(student.getName()+","+student.getAge());
    }
}
登入後複製

四、函数式接口

1.1 函数接口概述

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

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

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

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

  • @FunctionalInterface

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

注意:

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

@FunctionalInterface
public interface MyInterface {
    void show();
}
登入後複製
public class MyInterfaceDemo {
    public static void main(String[] args) {
        MyInterface myInterface = ()-> System.out.println("函数式接口");
        myInterface.show();
    }
}
登入後複製

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

如果方法的参数是一个函数式接口,我们可以使用Lambda表达式作为参数传递

startThread(() -> System.out.println(Thread.currentThread().getName() + "线程启动了"));
登入後複製
public class RunnableDemo {
    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() + "线程启动了"));

        startThread(()->{
            System.out.println(Thread.currentThread().getName() + "线程启动了");
        });
    }
    private static void startThread(Runnable runnable) {
        new Thread(runnable).start();
    }
}
登入後複製

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

如果方法的返回值是一个函数式接口,我们可以使用Lambda表达式作为结果返回

private static Comparator<String> getComparator() {
        return (s1,s2) -> s1.length() - s2.length();
    }
登入後複製
public class ComparatorDemo {
    public static void main(String[] args) {
        ArrayList<String> arrayList = new ArrayList<String>();

        arrayList.add("ccc");
        arrayList.add("aa");
        arrayList.add("dddd");
        arrayList.add("b");
        System.out.println("排序前" + arrayList);
        Collections.sort(arrayList);
        System.out.println("排序后" + arrayList);
        Collections.sort(arrayList, getComparator());
        System.out.println("使用定义比较器排序方法后:" + arrayList);
    }

    private static Comparator<String> getComparator() {
//        return new Comparator<String>() {
//            @Override
//            public int compare(String s1, String s2) {
//                return s1.length() - s2.length();
//            }
//        };
        return (s1,s2) -> s1.length() - s2.length();
    }
}
登入後複製

1.4 常用的函数式接口

Java 8 在java.util.function包下预定了大量的函数式接口供我们使用,常用如下:

  • Supplier接口

  • Consumer接口

  • Predicate接口

  • Function接口

1.5 Supplier接口

Supplier接口

  • T get():获得结果

  • 该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据

  • Supplier 接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用

public class SupplierDemo {
    public static void main(String[] args) {
        String s = getString(() -> "xuanxuan");
        System.out.println(s);

        Integer i = getInteger(() -> 666);
        System.out.println(i);
    }

    public static String getString(Supplier<String> supplier) {
        return supplier.get();
    }
    public static Integer getInteger(Supplier<Integer> supplier) {
        return supplier.get();
    }
}
登入後複製

练习:获取最大值

public class SupplierDemo {
    public static void main(String[] args) {
        int[] arr = new int[]{17, 28, 49, 21, 32, 66};
        int maxNumber = getMax(() -> {
            int max = arr[0];
            for (int i = 1; i < arr.length; i++) {
                if (max < arr[i]) {
                    max = arr[i];
                }
            }
            return max;
        });
        System.out.println("数组中的最大值是:" + maxNumber);
    }

    private static int getMax(Supplier<Integer> supplier) {
        return supplier.get();
    }
}
登入後複製

1.6 Consumer接口

Consumer:包含两个方法

  • void accept(T t):对给定的参数执行此操作

  • default Consumer andThen(Consumer after):返回一个组合的Consumer,依次执行此操作,然后执行after操作

  • Consumer 接口也被称为消费型接口,它消费的数据类型由泛型指定

public class ConsumerDemo {
    public static void main(String[] args) {
        operatorString("abc", s -> System.out.println(s));
        operatorString("abc", System.out::println);
        operatorString("abc", s -> System.out.println(new StringBuilder(s).reverse().toString()));
        System.out.println("----------------------------------");
        operatorString("abc", s -> System.out.println(s), s -> System.out.println(new StringBuilder(s).reverse().toString()));
    }

    private static void operatorString(String name, Consumer<String> consumer) {
        consumer.accept(name);
    }

    private static void operatorString(String name, Consumer<String> consumer1, Consumer<String> consumer2) {
//        consumer1.accept(name);
//        consumer2.accept(name);
        consumer1.andThen(consumer2).accept(name);
    }
}
登入後複製

练习:

字符串数组中又多条信息,按照:“姓名:name,年龄:age"的格式将信息打印出来

public class ConsumerDemo {
    public static void main(String[] args) {
        String[] arr = new String[]{"abc,30", "cbd,35", "dna,33"};
        printInfo(arr, s -> System.out.print("姓名:" + s.split(",")[0] + ","), s -> System.out.println("年龄:" + Integer.parseInt(s.split(",")[1])));
    }

    private static void printInfo(String[] arr, Consumer<String> consumer1, Consumer<String> consumer2) {
        for (String s : arr) {
            consumer1.andThen(consumer2).accept(s);
        }
    }
}
登入後複製

1.7 Predicate接口

常用方法:

Java函數式程式設計實例分析

练习:判断给定的字符串是否满足要求

public class PredicateDemo {
    public static void main(String[] args) {
        boolean b1 = checkString("hello", s -> s.length() > 5);
        System.out.println(b1);

        boolean b2 = checkString("helloworld", s -> s.length() > 8);
        System.out.println(b2);

        boolean b3 = checkString("hello", s -> s.length() > 5, s -> s.length() > 8);
        System.out.println(b3);

        boolean b4 = checkString("helloworld", s -> s.length() > 5, s -> s.length() > 8);
        System.out.println(b4);
    }

    private static boolean checkString(String s, Predicate<String> predicate) {
        return predicate.test(s);
    }

    private static boolean checkString(String s, Predicate<String> predicate, Predicate<String> predicate2) {
//        return predicate.and(predicate2).test(s);
        return predicate.or(predicate2).test(s);
    }
}
登入後複製

练习2:

  • String[] strArray ={“孙悟空,30”,“唐僧,36”,“沙僧,34”,“猪八戒,32”,“白骨精,5000”}

  • 字符串数组中有多条信息,请通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayLitst中,并遍历ArrayLitst集合

  • 同时满足如下要求:name长度大于2,age大于33

public class PredicateDemo3 {
    public static void main(String[] args) {
        String[] strArray = new String[]{"孙悟空,30", "唐僧,36", "沙僧,34", "猪八戒,32", "白骨精,5000"};
        ArrayList<String> arrayList = myFilter(strArray, s -> s.split(",")[0].length() > 2, s -> Integer.parseInt(s.split(",")[1]) > 33);
        System.out.println("name长度大于2,age大于33有:");
        for (String s : arrayList) {
            System.out.print("name:" + s.split(",")[0] + ",");
            System.out.println("age:" + Integer.parseInt(s.split(",")[1]));
        }
    }

    private static ArrayList<String> myFilter(String[] strArray, Predicate<String> predicate1, Predicate<String> predicate2) {
        ArrayList<String> arrayList = new ArrayList<String>();
        for (String s : strArray) {
            if (predicate1.and(predicate2).test(s)) {
                arrayList.add(s);
            }
        }
        return arrayList;
    }
}
登入後複製

1.8 Function接口

Function两个常用方法:

Java函數式程式設計實例分析

Function接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现)然后返回一个新的值

练习:

public class FunctionDemo {
    public static void main(String[] args) {
        convert("100", s -> Integer.parseInt(s));
        convert("100", Integer::parseInt);

        convert(100, i -> String.valueOf(100 + i));

        convert("100", s -> Integer.parseInt(s), i -> String.valueOf(i + 566));

    }
    //定义一个方法,把一个int类型的数据加上一个整数之后,转为字符串在控制台输出
    private static void convert(String s, Function<String, Integer> function) {
        Integer i = function.apply(s);
        System.out.println(i);
    }
    //定义一个方法,把一个int类型的数据加上一个整数之后,转为字符串在控制台输出
    private static void convert(int i, Function<Integer, String> function) {
        String s = function.apply(i);
        System.out.println(s);
    }
    //定义一个方法,把一个字符串转换为int类型,把int类型的数据加上一个整数之后,转为字符串在控制台输出
    private static void convert(String s, Function<String, Integer> function1, Function<Integer, String> function2) {
        String ss = function2.apply(function1.apply(s));
        System.out.println(ss);
    }
}
登入後複製

练习2:提取String中的年龄加70岁,并以int型输出

public class FunctionDemo {
    public static void main(String[] args) {
        String s = "孙悟空,30";
        convert(s, s1 -> s1.split(",")[1], s1 -> Integer.parseInt(s1) + 70);
    }

    private static void convert(String s, Function<String, String> function1, Function<String, Integer> function2) {
        Integer i = function2.apply(function1.apply(s));
        System.out.println(i);
    }
}
登入後複製

五、Stream流

1.1 体验Stream流

需求:按照下面的要求完成集合的创建和遍历

  • 创建一个集合,存储多个字符串元素

  • 把集合中所有以“张”开头的元素存储到一个新的集合再

  • 把长度为3的元素存储到一个新集合

  • 最后遍历上一步得到的集合

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

  • 直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:生成流、过滤姓氏、过滤长度为3、逐一打印

  • Stream流把真正的函数式编程风格引入到java中

list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(s -> System.out.println(s));
登入後複製
public class StreamDemo {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add("张飞");
        list.add("张三丰");
        list.add("张三");
        list.add("李四");
        list.add("孙悟空");
        list.add("张一飞");

        ArrayList zhangList = new ArrayList();
        for (String s : list) {
            if (s.startsWith("张")) {
                zhangList.add(s);
            }
        }
        ArrayList treeList = new ArrayList();
        for (String s : zhangList) {
            if (s.length() == 3) {
                treeList.add(s);
            }
        }
        for (String s : treeList) {
            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));
    }
}
登入後複製

1.2 Stream流的生成方式

Stream流的使用

生成流:通过数据源(集合、数组等)生成流

list.stream();
登入後複製

中间操作:一个流后面可以跟随零个或者多个中间操作,其目的主要是打开流,做出某种程度的数据过滤/映射,然后返回一个新的流,交给下一个操作使用

filter()
登入後複製

终结操作:一个流只能有一个终结操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作

forEach()
登入後複製

Stream流的常见生成方式

Collection体系的集合可以使用默认方法stream()生成流

default Stream<E> stream()
登入後複製
  • Map体系的集合间接的生成流

  • 数组可以通过Stream接口的静态方法of(T…values)生成流

public class StreamDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        Stream<String> listStream = list.stream();

        Set<String> set = new HashSet<String>();
        Stream<String> setStream = set.stream();

        Map<String, Integer> map = new HashMap<String, Integer>();
        Stream<String> keyStream = map.keySet().stream();
        Stream<Integer> valueStream = map.values().stream();
        Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();

        String[] strArray = {"hello", "world", "java"};
        Stream<String> strArrayStream = Stream.of(strArray);
        Stream<String> strArrayStream2 = Stream.of("hello", "world", "java");
        Stream<Integer> strArrayStream3 = Stream.of(10, 20, 30);
    }
}
登入後複製

1.3 Stream流的常见中间操作方法

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

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

public class StreamDemo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        list.add("张飞");
        list.add("张三丰");
        list.add("张三");
        list.add("李四");
        list.add("孙悟空");
        list.add("张一飞");
        list.stream().filter(s -> s.startsWith("张")).forEach(System.out::println);
        System.out.println("----------------------");
        list.stream().filter(s -> s.length() == 3).forEach(System.out::println);
        System.out.println("----------------------");
        list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(System.out::println);
    }
}
登入後複製
  • Stream limit(long maxSize):返回此流中的元素组成的流,截取前指定参数个数的数据

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

public class StreamDemo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        list.add("张飞");
        list.add("张三丰");
        list.add("张三");
        list.add("李四");
        list.add("孙悟空");
        list.add("张一飞");
        //取前三个数据在控制台输出
        list.stream().limit(3).forEach(System.out::println);
        System.out.println("-----------------------------");
        //跳过2个元素,把剩下的元素在控制台上输出
        list.stream().skip(2).forEach(System.out::println);
        System.out.println("-----------------------------");
        //跳过2个元素并将剩下元素的前两个元素在控制台上输出
        list.stream().skip(2).limit(2).forEach(System.out::println);
    }
}
登入後複製
  • Stream Stream concat(Stream a,Stream b):合并a和b两个流为一个流

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

public class StreamDemo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        list.add("张飞");
        list.add("张三丰");
        list.add("张三");
        list.add("李四");
        list.add("孙悟空");
        list.add("张一飞");
        //需求1:取前4个数据组成一个流
        Stream<String> limitStream = list.stream().limit(4);
        //需求2:跳过2个数据组成一个流
        Stream<String> skipStream = list.stream().skip(2);
        //需求3:合并需求1和需求2得到的流,并把结果在控制台输出
//        Stream.concat(limitStream,skipStream).forEach(System.out::println);
        //需求4:合并需求1和需求2得到的流,并把结果在控制台输出,要求字符串元素不能重复
        Stream.concat(limitStream,skipStream).distinct().forEach(System.out::println);
    }
}
登入後複製
  • Stream sorted():返回由此流的元素组成的流,根据自然顺序排序

  • Stream sorted(Comparator comparator):返回由该流的元素组成的流,根据提供的Comparator进行排序

public class StreamDemo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        list.add("zhangfei");
        list.add("zhangsanfeng");
        list.add("zhangsan");
        list.add("lisi");
        list.add("sunwukong");
        list.add("zhangyifei");

        //需求1:按照字母顺序把数据在控制台输出
        list.stream().sorted().forEach(System.out::println);
        //需求2:按照字符串长度把数据在控制台输出
        list.stream().sorted((s1, s2) -> {
            int num = s1.length() - s2.length();
            int num2 = num == 0 ? s1.compareTo(s2) : num;
            return num2;
        }).forEach(System.out::println);
    }
}
登入後複製
  • Stream map(Function mapper):返回由给定函数应用于此流的元素的结果组成的流(Function接口中的方法 R apply(T t))

  • IntStream mapToInt(ToIntFunction mapper):返回一个IntStream其中包含将给定函数应用于此流的元素的结果

public class StreamDemo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        list.add("10");
        list.add("20");
        list.add("30");
        list.add("40");
        list.add("50");

//        list.stream().map(s -> Integer.parseInt(s)).forEach(System.out::println);
        list.stream().map(Integer::parseInt).forEach(System.out::println);

        list.stream().mapToInt(Integer::parseInt).forEach(System.out::println);

        int result = list.stream().mapToInt(Integer::parseInt).sum();
        System.out.println(result);
    }
}
登入後複製

1.4 Stream流的常见终结操作方法

  • void forEach(Consumer action):对此流的每个元素执行操作(Consumer接口中的方法 void accept(T t):对给定的参数执行此操作)

  • long count():返回此流中的元素数

public class StreamDemo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        list.add("张飞");
        list.add("张三丰");
        list.add("张三");
        list.add("李四");
        list.add("孙悟空");
        list.add("张一飞");

        //需求1:把集合中的元素在控制台输出
        list.stream().forEach(System.out::println);

        //需求2:统计集合中有几个姓张的元素并在控制台输出
        list.stream().filter(s -> s.startsWith("张")).forEach(System.out::println);
    }
}
登入後複製

1.5 Stream流的练习

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

  • 男演员只要名字为3个字的前三人

  • 女演员只要姓林的,并且不要第一个

  • 把过滤后的男演员姓名和女演员姓名合并到一起

  • 把上一步操作后的元素作为构造方法的参数创建演员对象,遍历数据(演员类Actor已经提供,里面有一个成员变量,一个带参构造方法,以及成员变量对应的get/set方法)

public class StreamDemo {
    public static void main(String[] args) {
        ArrayList<String> manList = new ArrayList<String>();
        manList.add("周润发");
        manList.add("成龙");
        manList.add("刘德华");
        manList.add("吴京");
        manList.add("周星驰");
        manList.add("李连杰");
        ArrayList<String> womanList = new ArrayList<String>();
        womanList.add("林心如");
        womanList.add("张曼玉");
        womanList.add("林青霞");
        womanList.add("柳岩");
        womanList.add("林志玲");
        womanList.add("王祖贤");

        //男演员只要名字为3个字的前三人
        Stream<String> manStream = manList.stream().filter(s -> s.length() == 3).limit(3);

        //女演员只要姓林的,并且不要第一个
        Stream<String> womanStream = womanList.stream().filter(s -> s.startsWith("林")).skip(1);

        //把过滤后的男演员姓名和女演员姓名合并到一起
        Stream<String> stream = Stream.concat(manStream, womanStream);

        //把上一步操作后的元素作为构造方法的参数创建演员对象,遍历数据
        stream.map(Actor::new).forEach(p -> System.out.println(p.getName()));
        System.out.println("------------------------------------");
        //改进
        Stream.concat(manList.stream().filter(s -> s.length() == 3).limit(3), womanList.stream().filter(s -> s.startsWith("林")).skip(1)).map(Actor::new).forEach(p -> System.out.println(p.getName()));
    }
}
登入後複製

1.6 Stream流的收集操作

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

Stream流的手机方法

  • R collect(Collector collector)

  • 但是这个收集方法的参数是一个Collector接口

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

  • public static Collector toList():把元素收到List集合中

  • public static Collector toSet():把元素收集到Set集合中

  • public static Collector toMap(Function keyMapper,Function valueMapper):把元素收集到Map集合中

public class StreamDemo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        list.add("张飞");
        list.add("张三丰");
        list.add("张三");
        list.add("李四");
        list.add("孙悟空");
        list.add("张一飞");

        //需求1:得到名字为3个字的流
        Stream<String> listStream = list.stream().filter(s -> s.length() == 3);
        //需求2:把使用Stream流操作完毕的数据收集到List集合中并遍历
        List<String> collect = listStream.collect(Collectors.toList());
        for (String s : collect) {
            System.out.println(s);
        }

        Set<Integer> set = new HashSet<Integer>();
        set.add(10);
        set.add(20);
        set.add(30);
        set.add(33);
        set.add(35);

        //需求3:得到年龄大于25的流
        Stream<Integer> integerStream = set.stream().filter(age -> age > 25);

        //需求4:把使用Stream流操作完毕的数据收集到Set集合中并遍历
        Set<Integer> collect2 = integerStream.collect(Collectors.toSet());
        for (Integer i : collect2) {
            System.out.println(i);
        }

        String[] strArray = {"张飞,28", "张三丰,33", "张三,26", "李四,44"};

        //需求5:得到字符串年龄中数据大于28的流
        Stream<String> stringStream = Stream.of(strArray).filter(s -> Integer.parseInt(s.split(",")[1]) > 28);

        //需求6:把使用Stream流操作完毕的数据收集到Map集合中并遍历,字符串的姓名作为键,年龄作为值
        Map<String, Integer> map = stringStream.collect(Collectors.toMap(s -> s.split(",")[0], s -> Integer.parseInt(s.split(",")[1])));
        Set<String> keySet = map.keySet();
        for (String key : keySet) {
            Integer value = map.get(key);
            System.out.println(key + "," + value);
        }
    }
}
登入後複製

以上是Java函數式程式設計實例分析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
威爾R.E.P.O.有交叉遊戲嗎?
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

Java 中的完美數 Java 中的完美數 Aug 30, 2024 pm 04:28 PM

Java 完美數指南。這裡我們討論定義,如何在 Java 中檢查完美數?

Java中的Weka Java中的Weka Aug 30, 2024 pm 04:28 PM

Java 版 Weka 指南。這裡我們透過範例討論簡介、如何使用 weka java、平台類型和優點。

Java 中的史密斯數 Java 中的史密斯數 Aug 30, 2024 pm 04:28 PM

Java 史密斯數指南。這裡我們討論定義,如何在Java中檢查史密斯號?帶有程式碼實現的範例。

Java Spring 面試題 Java Spring 面試題 Aug 30, 2024 pm 04:29 PM

在本文中,我們保留了最常被問到的 Java Spring 面試問題及其詳細答案。這樣你就可以順利通過面試。

突破或從Java 8流返回? 突破或從Java 8流返回? Feb 07, 2025 pm 12:09 PM

Java 8引入了Stream API,提供了一種強大且表達力豐富的處理數據集合的方式。然而,使用Stream時,一個常見問題是:如何從forEach操作中中斷或返回? 傳統循環允許提前中斷或返回,但Stream的forEach方法並不直接支持這種方式。本文將解釋原因,並探討在Stream處理系統中實現提前終止的替代方法。 延伸閱讀: Java Stream API改進 理解Stream forEach forEach方法是一個終端操作,它對Stream中的每個元素執行一個操作。它的設計意圖是處

Java 中的時間戳至今 Java 中的時間戳至今 Aug 30, 2024 pm 04:28 PM

Java 中的時間戳記到日期指南。這裡我們也結合範例討論了介紹以及如何在java中將時間戳記轉換為日期。

Java程序查找膠囊的體積 Java程序查找膠囊的體積 Feb 07, 2025 am 11:37 AM

膠囊是一種三維幾何圖形,由一個圓柱體和兩端各一個半球體組成。膠囊的體積可以通過將圓柱體的體積和兩端半球體的體積相加來計算。本教程將討論如何使用不同的方法在Java中計算給定膠囊的體積。 膠囊體積公式 膠囊體積的公式如下: 膠囊體積 = 圓柱體體積 兩個半球體體積 其中, r: 半球體的半徑。 h: 圓柱體的高度(不包括半球體)。 例子 1 輸入 半徑 = 5 單位 高度 = 10 單位 輸出 體積 = 1570.8 立方單位 解釋 使用公式計算體積: 體積 = π × r2 × h (4

如何在Spring Tool Suite中運行第一個春季啟動應用程序? 如何在Spring Tool Suite中運行第一個春季啟動應用程序? Feb 07, 2025 pm 12:11 PM

Spring Boot簡化了可靠,可擴展和生產就緒的Java應用的創建,從而徹底改變了Java開發。 它的“慣例慣例”方法(春季生態系統固有的慣例),最小化手動設置

See all articles