JAVA基础---函数式接口、Stream流

函数式编程

Stream流

一、函数式编程:

1、1.概念

函数式接⼝在Java中是指:有且仅有⼀个抽象⽅法的接⼝。

函数式接⼝,即适⽤于函数式编程场景的接⼝。⽽Java中的函数式编程体现就是Lambda,所以函数式接⼝ 就是可以适⽤于Lambda使⽤的接⼝。只有确保接⼝中有且仅有⼀个抽象⽅法,Java中的Lambda才能顺利 地进⾏推导。

1、2.格式

只要确保接⼝中有且仅有⼀个抽象⽅法即可:

修饰符 interface 接⼝名称 {
 public abstract 返回值类型 ⽅法名称(可选参数信息);
 // 其他⾮抽象⽅法内容
}

由于接⼝当中抽象⽅法的 public abstract 是可以省略的,所以定义⼀个函数式接⼝很简:

public interface MyFunctionalInterface {
 void myMethod();
}

1、3.自定义函数式接口

对于刚刚定义好的 MyFunctionalInterface 函数式接⼝,典型使⽤场景就是作为⽅法的参数:

public class Demo1{
    Private static void do(MyFunctionalInterface inter){
        inter.myMethod();
    }
    public static void main(String[] args){
        do (() -> System.out.println("----"))
    }
}

2、常用函数接口

2、1.Supplier

java.util.function.Supplier 接⼝仅包含⼀个⽆参的⽅法: T get() 。⽤来获取⼀个泛型参数 指定类型的对象数据。由于这是⼀个函数式接⼝,这也就意味着对应的Lambda表达式需要“对外提供”⼀ 个符合泛型类型的对象数据。

实例:

import java.util.function.Supplier;
public class Demo1{
    private static String getString(Supplier<String> function){
        return function.get();
    }
    public static void main(String[] args){
        String A = "aa";
        String B = "bb";
        System.out.println(getString(() -> A + B));
    }
}

2.2.Consumer

java.util.function.Consumer 接⼝则正好与Supplier接⼝相反,它不是⽣产⼀个数据,⽽是消费 ⼀个数据,其数据类型由泛型决定。

抽象方法:accept

Consumer 接⼝中包含抽象⽅法 void accept(T t) ,意为消费⼀个指定泛型的数据。基本使⽤如:

import java.util.function.Consumer;
public class Demo1{
    private static void consumeString(Consumer<String> function){
function.accept("aa");
    }
    public static void main(String[] args){
        consumeString(s -> System.out.println(s));
    }
}

默认方法:andThen

如果⼀个⽅法的参数和返回值全都是 Consumer 类型,那么就可以实现效果:消费数据的时候,⾸先做⼀ 个操作,然后再做⼀个操作,实现组合。⽽这个⽅法就是 Consumer 接⼝中的default⽅法 andThen 。 要想实现组合,需要两个或多个Lambda表达式即可 。

2、3.Predicate

有时候我们需要对某种类型的数据进⾏判断,从⽽得到⼀个boolean值结果。这时可以 使 java.util.function.Predicate 接⼝。

抽象方法:test

有时候我们需要对某种类型的数据进⾏判断,从⽽得到⼀个boolean值结果。这时可以 使 java.util.function.Predicate 接⼝。


import java.util.function.Predicate;
public class Demo15PredicateTest {
 private static void method(Predicate<String> predicate) {
 boolean veryLong = predicate.test("HelloWorld");
 System.out.println("字符串很⻓吗:" + veryLong);
 }
 
 public static void main(String[] args) {
 method(s -> s.length() > 5);
 }
}



默认方法:

2.3.2、and:

 既然是条件判断,就会存在与、或、⾮三种常⻅的逻辑关系。其中将两个 Predicate 条件使⽤“与”逻辑连 接起来实现“并且”的效果时,可以使⽤default⽅法 and 。其JDK源码为 

```java
default Predicate<T> and(Predicate<? super T> other) {
 Objects.requireNonNull(other);
 return (t) -> test(t) && other.test(t);
}

2.3.3、or:

与 and 的“与”类似,默认⽅法 or 实现逻辑关系中的“或”。JDK源码为:

default Predicate<T> or(Predicate<? super T> other) {
 Objects.requireNonNull(other);
 return (t) -> test(t) || other.test(t);
}

2.3.4、negate:

“与”、“或”已经了解了,剩下的“⾮”(取反)也会简单。默认⽅法 negate 的JDK源代码为:

default Predicate<T> negate() {
 return (t) -> !test(t);
}

ps:它是执⾏了test⽅法之后,对结果boolean值进⾏“!”取反⽽已。⼀定要在 test ⽅法 调⽤之前调⽤ negate ⽅法,正如 and 和 or ⽅法⼀样:

2、4.Function:

java.util.function.Function 接⼝⽤来根据⼀个类型的数据得到另⼀个类型的数据,前者称为 前置条件,后者称为后置条件。

抽象⽅法:apply

Function 接⼝中最主要的抽象⽅法为: R apply(T t) ,根据类型T的参数获取类型R的结果。 使⽤的场景例如:将 String 类型转换为 Integer 类型。

import java.util.function.Function;
public class Demo11FunctionApply {
 private static void method(Function<String, Integer> function) {
 int num = function.apply("10");
 System.out.println(num + 20);
 }
 
 public static void main(String[] args) {
 method(s -> Integer.parseInt(s));
 }
}

默认方法: andThen

Function 接⼝中有⼀个默认的 andThen ⽅法,⽤来进⾏组合操作。JDK源代码如:

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
 Objects.requireNonNull(after);
 return (T t) -> after.apply(apply(t));
}

ps: 该⽅法同样⽤于“先做什么,再做什么”的场景,和 Consumer 中的 andThen 差不多

二、Stream流

1.1获取流的方法

java.util.stream.Stream 是Java 8新加⼊的最常⽤的流接⼝。(这并不是⼀个函数式接⼝。) 获取⼀个流⾮常简单,有以下⼏种常⽤的⽅式:

所有的 Collection 集合都可以通过 stream 默认⽅法获取流;

Stream 接⼝的静态⽅法 of 可以获取数组对应的流。

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;

public class Demo1 {
    public static void main(String[] args) {
        //获得Stream对象

        //List获得stream对象
        List <String> list = List.of("鄙人","张麻子","黄四郎");//快速生成集合,创建之后不可变
        Stream<String> stream = list.stream();

        //set获得stream对象
        Set<String > set = Set.of("鄙人","张麻子","黄四郎");
        Stream<String> stream1 = set.stream();

        //Map获得stream对象
        Map<String ,Integer>map = Map.of("鄙人",1,"张麻子",2,"黄四郎",3);
        Stream<String> stream2 = map.keySet().stream();
        Stream<Integer> stream3 = map.values().stream();
        Stream<Map.Entry<String, Integer>> stream4 = map.entrySet().stream();

        //数组获得stream对象
        String [] strArr = {"鄙人","张麻子","黄四郎"};
         Stream<String> stream6 = Arrays.stream(strArr);
       
        Stream<Integer> stream5 = Stream.of(1,2,3,4,5);
        
    }
}

1、2.常用方法:

1.2.1:逐一处理:forEach

虽然⽅法名字叫 forEach ,但是与for循环中的“for-each”昵称不同。

 void forEach(Consumer<? super T> action);

该⽅法接收⼀个 Consumer 接⼝函数,会将每⼀个流元素交给该函数进⾏处理。

实例:

import java.util.stream.Stream;
public class Demo12StreamForEach {
 public static void main(String[] args) {
 Stream<String> stream = Stream.of("鄙人", "张麻子", "黄老爷");
 stream.forEach(name-> System.out.println(name));
 }
}

1.2.2:过滤:filter

可以通过 filter ⽅法将⼀个流转换成另⼀个⼦集流。⽅法签名:

Stream<T> filter(Predicate<? super T> predicate);

该接⼝接收⼀个 Predicate 函数式接⼝参数(可以是⼀个Lambda或⽅法引⽤)作为筛选条件。

该⽅法将会产⽣⼀个boolean值结果,代表指定的条件是否满⾜。如果结果为true,那么Stream流的 filter ⽅法将会留⽤元素;如果结果为false,那么 filter ⽅法将会舍弃元素。

实例: 通过Lambda表达式来指定了筛选的条件:必须姓张。

import java.util.stream.Stream;
public class Demo07StreamFilter {
 public static void main(String[] args) {
 Stream<String> original = Stream.of("张麻子", "黄四郎", "九筒");
 Stream<String> result = original.filter(s -> s.startsWith("张"));
 }
}

1.2.3:映射:map

如果需要将流中的元素映射到另⼀个流中,可以使⽤ map ⽅法。⽅法签名:

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

该接⼝需要⼀个 Function 函数式接⼝参数,可以将当前流中的T类型数据转换为另⼀种R类型的流。

实例: map ⽅法的参数通过⽅法引⽤,将字符串类型转换成为了int类型(并⾃动装箱为 Integer类对象)。

import java.util.stream.Stream;
public class Demo08StreamMap {
 public static void main(String[] args) {
 Stream<String> original = Stream.of("10", "12", "18");
 Stream<Integer> result = original.map(str->Integer.parseInt(str));
 }
}

1.2.4:统计个数:count

正如旧集合 Collection 当中的 size ⽅法⼀样,流提供 count ⽅法来数⼀数其中的元素个数:

long count();

该⽅法返回⼀个long值代表元素个数(不再像旧集合那样是int值)。

import java.util.stream.Stream;
public class Demo09StreamCount {
 public static void main(String[] args) {
 Stream<String> original = Stream.of("鄙人","张麻子","黄四郎");
 Stream<String> result = original.filter(s -> s.startsWith("张"));
 System.out.println(result.count()); // 2
 }
}

1.2.5: 取⽤前⼏个:limit

limit ⽅法可以对流进⾏截取,只取⽤前n个。⽅法签名:

Stream<T> limit(long maxSize);

参数是⼀个long型,如果集合当前⻓度⼤于参数则进⾏截取;否则不进⾏操作。基本使⽤:

import java.util.stream.Stream;
public class Demo10StreamLimit {
 public static void main(String[] args) {
 Stream<String> original = Stream.of("鄙人","张麻子","黄四郎");
 Stream<String> result = original.limit(2);
 System.out.println(result.count()); // 2
 }
}

1.2.6: 跳过前⼏个:skip

如果希望跳过前⼏个元素,可以使⽤ skip ⽅法获取⼀个截取之后的新流:

Stream<T> skip(long n);

如果流的当前⻓度⼤于n,则跳过前n个;否则将会得到⼀个⻓度为0的空流。基本使⽤:

import java.util.stream.Stream;
public class Demo11StreamSkip {
 public static void main(String[] args) {
 Stream<String> original = Stream.of("鄙人","张麻子","黄四郎");
 Stream<String> result = original.skip(2);
 System.out.println(result.count()); // 1
 }
}

1.2.7: 组合:concat

如果有两个流,希望合并成为⼀个流,那么可以使⽤ Stream 接⼝的静态⽅法 concat :

static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
实例:
import java.util.stream.Stream;
public class Demo12StreamConcat {
 public static void main(String[] args) {
 Stream<String> streamA = Stream.of("张麻子");
 Stream<String> streamB = Stream.of("张起灵");
 Stream<String> result = Stream.concat(streamA, streamB);
 }
}

1、3 小结练习

题⽬ :现在有两个 ArrayList 集合存储队伍当中的多个成员姓名,要求使⽤传统的for循环(或增强for循环)依 次进⾏以下若⼲操作步骤:

第⼀个队伍只要名字为3个字的成员姓名;存储到⼀个新集合中。

第⼀个队伍筛选之后只要前3个⼈;存储到⼀个新集合中。

第⼆个队伍只要姓张的成员姓名;存储到⼀个新集合中。

第⼆个队伍筛选之后不要前2个⼈;存储到⼀个新集合中。

将两个队伍合并为⼀个队伍;存储到⼀个新集合中。

根据姓名创建 Person 对象;存储到⼀个新集合中。

打印整个队伍的Person对象信息。

package Stream;

import java.util.ArrayList;
import java.util.stream.Stream;

public class Demo4 {
    public static void main(String[] args) {

        //第⼀⽀队伍
        ArrayList<String> one = new ArrayList<>();
        one.add("迪丽热巴");
        one.add("宋远桥");
        one.add("苏星河");
        one.add("⽯破天");
        one.add("⽯中⽟");
        one.add("⽼⼦");
        one.add("庄⼦");
        one.add("洪七公");

        //第⼆⽀队伍
        ArrayList<String> two = new ArrayList<>();
        two.add("古⼒娜扎");
        two.add("张⽆忌");
        two.add("赵丽颖");
        two.add("张三丰");
        two.add("尼古拉斯赵四");
        two.add("张天爱");
        two.add("张⼆狗");

        Stream<String> s1 = one.stream().filter(t -> t.length() == 3).limit(3);
        Stream<String> s2 = two.stream().filter(t -> t.startsWith("张")).skip(2);
        Stream<String> stream = Stream.concat(s1, s2);
        stream.map(name -> new Person(name)).forEach(p -> System.out.println(p  ));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值