首页 > 编程语言 >JAVA函数式接口与Stream流

JAVA函数式接口与Stream流

时间:2022-09-23 13:22:38浏览次数:50  
标签:JAVA String Stream strArr System 接口 add out

JAVA函数式接口与Stream流

函数式接口概述

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

  • java中的函数式编程体现就是Lambda表达式,所以函数式接口就是可以适用于Lambda表达式的接口
  • 只有确保接口中有且只有一个抽象方法,java中的Lambda表达式才能顺利的进行推导

函数式接口的注解

  • @FunctionalInterface
  • 放在接口定义的上方,如果接口是函数式接口则编译通过,反之失败
  • 这个标注可加可不加,不标注的时候,只有一个抽象方法的接口虽然也是函数式接口,但是标注后能让其他人更加清晰的分辨以及方便编译,建议加上

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

public class Printable1 {
    public static void main(String[] args) {
        //采用匿名内部类
        starRunnable(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+":线程启动了");
            }
        });
        //Lambda表达式
        starRunnable(()-> System.out.println(Thread.currentThread().getName()+":线程启动了"));
    }

    private static void starRunnable(Runnable r) {
        new Thread(r).start();
    }

}
  • 此处线程的Runnable接口是一个只有run()抽象方法的函数式接口,作为starRunnable()方法的参数

  • 进而可以使用匿名内部类甚至是Lambda表达式简写

  • 也就是说多线程的时候,因为Runnable是函数式接口,我们不需要去创建对象实现多线程。直接Lambda实现

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

public class ComparatorDemo {
    public static void main(String[] args) {
        //构造使用场景
        //定义集合,储存字符串元素
        ArrayList<String> arr = new ArrayList<String>();
        arr.add("aaaaa");
        arr.add("bb");
        arr.add("ddd");
        arr.add("ccccccc");
        System.out.println("排序前" + arr);
        //此排序方法需要传入一个list集合对象,以及可以加入一个比较器接口的实现类对象
        // getComparator()方法返回的就是一个Comparator接口匿名实现类对象
        Collections.sort(arr, getComparator());
        System.out.println("排序后" + arr);
    }

    private static Comparator<String> getComparator() {
//        Comparator<String> com = new Comparator<String>() {
//            @Override
//            public int compare(String s1, String s2) {
//                return s1.length()-s2.length();
//            }
//        };
        //简化后返回一个Comparator接口的匿名实现类对象
//        return new Comparator<String>() {
//            @Override
//            public int compare(String s1, String s2) {
//                return s1.length() - s2.length();
//            }
//        };

        //因为是函数式接口,使用Lambda表达式继续简化
//        return(String s1,String s2)->{
//            return s1.length() - s2.length();
//        };

        //进一步简化
        return (s1, s2) -> s1.length() - s2.length();
    }

}
  • 如果方法的返回值是一个函数式接口,我们可以用Lambda表达式作为结果返回(将上面的代码精简一下)
private static Comparator<String> getComparator() {
    return (s1, s2) -> s1.length() - s2.length();
}

常用的函数式接口

Supplier接口

练习

  • 定义一个类SupplierTest,在类中提供两个方法
    • 一个是:int getMax(Supplier sup) 用于返回一个int集合中的最大值
    • 一个是主方法,主方法中使用getMax方法
public class SupplierTest {
    public static void main(String[] args) {
        ArrayList<Integer> num = new ArrayList<Integer>();
        num.add(5);
        num.add(9);
        num.add(6);
        num.add(2);
        num.add(11);

        int maxNum = getMax(() -> {
            Integer max = num.get(0);
            for (Integer i : num) {
                if (i > max) {
                    max = i;
                }
            }
            return max;
        });
        System.out.println("该int集合中最大的数字是:"+maxNum);
    }

    private static int getMax(Supplier<Integer> sup) {
        return sup.get();
    }

}

Consumer接口

消费就是拿已经有的使用,生产就是创造没有的

public class ConsumerDemo {
    public static void main(String[] args) {
        //依次是匿名内部类,Lambda表达式,方法引用
        operatorString("nihaooo", new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
        operatorString("nihaooo",s->System.out.println(s));
        operatorString("nihaooo",System.out::println);
        //反转字符串
        operatorString("nihaooo",s -> System.out.println(new StringBuilder(s).reverse()));
        //消耗两次
        System.out.println("--------------");
        operatorString("nihaooo",s -> System.out.println(s),s-> System.out.println(new StringBuilder(s).reverse()));


    }

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

    }


    //定义一个方法,消费一个字符串数据
    private static void operatorString(String name, Consumer<String> con) {
        con.accept(name);
    }
}

练习

经范例通过Lambda表达式简化后代码

public class ConsumerDemo {
    public static void main(String[] args) {
        String[] strArr = {"xuzhiyuan,20","shijuanjun,24","ximu,27"};
        //使用String类的spilt分割逗号获取姓名年龄分别输出
        operatorString(strArr,str -> System.out.print("姓名:"+str.split(",")[0]),
                str -> System.out.println(",年龄:"+str.split(",")[1]));

    }


    
    
    private static void operatorString(String[] strArr, Consumer<String> con1, Consumer<String> con2) {
        for (String str : strArr) {
            con1.andThen(con2).accept(str);
        }

    }
}

Predicate接口

  • Predicate接口通常用于判断参数是否满足指定的条件
  • test()与negate()方法的使用
public class PredicateDemo {
    public static void main(String[] args) {
        //判断字符长度是否大于8
        boolean b1 = checkString("hello", s -> s.length() > 8);
        System.out.println(b1);

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


    public static boolean checkString(String s, Predicate<String> pre) {
//        return pre.test(s);正常的判断逻辑
//        return !pre.test(s);可以用!返回相反逻辑
        return pre.negate().test(s);//接口自身的方法也可以返回非的逻辑

    }
}
  • and()方法和or()方法的使用
public static void main(String[] args) {
        boolean b1 = checkString("hello", s -> s.length() > 8);
        System.out.println(b1);

        boolean b2 = checkString("djaosdjasdasd", s -> s.length() > 8);
        System.out.println(b2);
        System.out.println("------------");
        boolean b3 = checkString("dasdasd", s -> s.length() > 8, s -> s.length() < 15);
        System.out.println(b3);
    }

    //同一个字符串给出两个不同判断,最后把这两个判断的结果与运算的结果作为最终的结果
    public static boolean checkString(String s, Predicate<String> pre1, Predicate<String> pre2) {
//        boolean p1 = pre1.test(s);
//        boolean p2 = pre2.test(s);
//        boolean b = p1 && p2;
//        return b;
        //可以用接口自带的and or方法写成以下方式
        return pre1.and(pre2).test(s) && pre1.or(pre2).test(s);
    }

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

练习

  • String[]strArr = {"xuzhiyuan,30","ximu,19","tianming,24","demmo,21","shijuanjun,18"};

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

  • 满足要求:名字长度大于5,年龄大于20

public class PredicateDemo {
    public static void main(String[] args) {
        String[] strArr = {"xuzhiyuan,30", "ximu,19", "tianming,24", "demmo,21", "shijuanjun,18"};
        //通过2个Predicate接口的匿名内部类简写为Lambda表达式重写,切割字符数组为姓名年龄分别判断条件
        //将返回的集合新建对象接收
        ArrayList<String> arry = checkString(strArr, s -> s.split(",")[0].length() > 5, s -> Integer.parseInt(s.split(",")[1]) > 20);
        //输出集合在控制台
        System.out.println("满足名字长度大于5个字符,年龄大于20的人有:" + arry);
    }

    //同一个字符串给出两个不同判断,最后把这两个判断的结果与运算的结果作为最终的结果
    public static ArrayList<String> checkString(String[] strArr, Predicate<String> pre1, Predicate<String> pre2) {
        ArrayList<String> arry = new ArrayList<>();

        for (String s : strArr) {//if括号里使用接口的and方法判断2个条件都为为true的话
            if (pre1.and(pre2).test(s)) {
                arry.add(s);//将遍历出来的姓名加年龄的字符串添加到arry集合里
            }
        }
        return arry;//返回整理好的arry集合
    }
}

Function接口

public class FunctionDemo {
    public static void main(String[] args) {
        convert("123", s -> Integer.parseInt(s));
//        convert("abc",Integer::parseInt);进行方法引用后

        convert(100, i -> String.valueOf(i + 567));
//        convert(100,String::valueOf);方法引用简化

        convert("234", s -> Integer.parseInt(s), s -> String.valueOf(s + 789));
//        convert("234",Integer::parseInt,String::valueOf);方法引用简化

    }

    //定义一个方法,把一个字符串转换int类型,在控制台输出
    private static void convert(String s, Function<String, Integer> fun) {
        Integer i = fun.apply(s);
        System.out.println(i);
    }

    //定义一个方法,把一个int类型的数据加上一个整数后,转为字符串在控制台输出
    private static void convert(int i, Function<Integer, String> fun) {
        String str = fun.apply(i);
        System.out.println(str);
    }

    //定义一个方法,把一个字符串转换int类型,把int类型的数据加上一个整数之后,转为字符串在控制台输出
    private static void convert(String s, Function<String, Integer> fun1, Function<Integer, String> fun2) {
        String str = fun1.andThen(fun2).apply(s);
        System.out.println(str);
    }
}

练习

public class FunctionDemo01 {
    public static void main(String[] args) {
        String s = "林青霞,30";
        //最简化后
        strMaker(s, str -> str.split(",")[1], Integer::parseInt, str -> str + 70);
    }

    private static void strMaker(String str, Function<String, String> fun1, Function<String, Integer> fun2, Function<Integer, Integer> fun3) {
        System.out.println(fun1.andThen(fun2).andThen(fun3).apply(str));
    }
}

Stream流

体验stream

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

        strArr.add("张无忌");
        strArr.add("徐志远");
        strArr.add("张路");
        strArr.add("汐木");
        strArr.add("张小凡");

        ArrayList<String> zh = new ArrayList<String>();

//        for (String s : strArr) {
//            if((s.startsWith("张"))){
//                zh.add(s);
//            }
////            if("张".equals(String.valueOf(s.charAt(0)))){
////                zh.add(s);
////                通过charAt获取第一个下标姓氏的字符char类型,转换成string后equals判断是否是张也可完成
////                    但过于繁琐,使用starwith方法可以更加快速判断
////            };
//        }
//        System.out.println(zh);
//
//        ArrayList<String> zh1 = new ArrayList<String>();
//
//        for (String str : zh) {
//            if(strMaker(str, ss -> ss.length() == 3)){
//                zh1.add(str);
//            }
//        }

//        System.out.println(zh1);

        System.out.println("--------");
       
        //使用stream改进
        strArr.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(System.out::println);

    }

    private static boolean strMaker(String ss, Predicate<String> pre){
        boolean b = pre.test(ss);
        return b;
    }
}

Stream流的生成方式

Stream流的使用

  • 生成流

    • 通过数据源(集合,数组等)生成流
    • 对象.stream()
  • 中间操作

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

    • 一个流只能有一个操作,当这个操作执行后,流就被使用光了无法再被操作,所以这必定是流的最后一个操作
    • forEach()

Stream流的生成方式

public static void main(String[] args) {
    //Collection体系的集合可以使用默认方法stream()生成流
    List<String> list = new ArrayList<String>();
    Stream<String> listStream = list.stream();

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

    //Map体系的集合间接的生成流
    Map<Integer,String> map = new HashMap<Integer,String>();
    Stream<Integer> keyStream = map.keySet().stream();//键对象对应的流
    Stream<String> valueStream = map.values().stream();//值对象对应的流
    Stream<Map.Entry<Integer, String>> entryStream = map.entrySet().stream();//键值对对象的流

    //数组可以通过stream接口的静态方法
    String[] str = {"nihao","ooooo","dasda"};
    Stream<String> str1 = Stream.of(str);
    //可以传可变参数
    Stream<String> stream1 = Stream.of("dasda", "fqdqw", "fqfdqwd");
    Stream<Integer> intstream = Stream.of(10, 20, 40, 50);
}

Stream流常见的中间操作方法--filter

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

    • 传入Predicate接口的对象后使用函数接口中的 boolean test(T t):对给定的参数进行判断,返回一个布尔值
  • 参考“体验Stream”部分和以下的截图实例

Stream流常见的中间操作方法--limit&skip

  • Stream limit(long maxSize) 返回一个包含该流的元素组成的流,截断长度不超过 maxSize。
  • Stream skip(long n) 跳过指定参数的个数的数据,返回由该流的剩余元素组成的流
public static void main(String[] args) {
    ArrayList<String> strArr = new ArrayList<String>();

    strArr.add("张无忌");
    strArr.add("徐志远");
    strArr.add("张路");
    strArr.add("汐木");
    strArr.add("张小凡");

    //取前三个数据在控制台输出
    strArr.stream().limit(3).forEach(System.out::println);
    System.out.println("---------");

    //跳过3个元素,把剩下的元素在控制台输出
    strArr.stream().skip(3).forEach(System.out::println);
    System.out.println("---------");

    //跳过2个元素,把剩下元素的前2个在控制台输出
    strArr.stream().skip(2).limit(2).forEach(System.out::println);
}

Stream流常见的中间操作方法--concat&distinct

  • static Stream concat(Stream<? extends T> a, Stream<? extends T> b)

    • 合并a,b两个流为一个流
  • Stream distinct() 返回由该流的不同(不重复)的元素(根据 Object.equals(Object))组成的流。

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

    strArr.add("张无忌");
    strArr.add("徐志远");
    strArr.add("张路");
    strArr.add("汐木");
    strArr.add("张小凡");
    strArr.add("天明");

    //1.取前4个数据组成一个流
    Stream<String> s1 = strArr.stream().limit(4);

    //2.跳过两个数据组成一个流
    Stream<String> s2 = strArr.stream().skip(2);

    //3.合并1,2需求的流为一个流,并把结果输出在控制台
    Stream.concat(s1, s2).forEach(System.out::println);

    //4.合并1,2需求的流为一个流,并把结果输出在控制台,要求字符串元素不能重复
    Stream.concat(s1, s2).distinct().forEach(System.out::println);
}

Stream流常见的中间操作方法--sorted

  • Stream sorted() 返回由该流的元素组成的流,按自然顺序排序。
  • Stream sorted(Comparator<? super T> comparator) 返回一个包含该流的元素流,根据提供的 Comparator排序。
public static void main(String[] args) {
    ArrayList<String> strArr = new ArrayList<String>();

    strArr.add("qdnqwdqwdq");
    strArr.add("netrbewrbvewbv");
    strArr.add("ebervwvwecv");
    strArr.add("qwfqfqfvqw");
    strArr.add("gtrjnetbwev");
    strArr.add("bfewfeqfcqefc");

    //1,按照字母顺序把数据输出在控制台
    strArr.stream().sorted().forEach(System.out::println);
    System.out.println("----------------");
    //2.按照字符串长度把数据输出在控制台输出
    strArr.stream().sorted((s1, s2) -> {
        int num = s1.length() - s2.length();
        int num1 = num == 0 ? s1.compareTo(s2) : num;
        return num1;
    }).forEach(System.out::println);
}

Stream流常见的中间操作方法--map&mapToInt

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

    strArr.add("10");
    strArr.add("20");
    strArr.add("30");
    strArr.add("40");
    strArr.add("50");

    //将集合中的字符串转换为整数之后在控制台输出//soutc:方法引用输出快捷
    strArr.stream().map(Integer::parseInt).forEach(System.out::println);
    strArr.stream().mapToInt(Integer::parseInt).forEach(System.out::println);//两个方法都可以实现


    //int sum()返回此流中元素的总和,但这个方法实在intstream下的, 需要通过mapToInt方法转成int后的包含结果的intstream使用sum方法
    int result = strArr.stream().mapToInt(Integer::parseInt).sum();
    System.out.println(result);
}

Stream流常见的终结操作方法--forEach&count

  • void forEach(Consumer<? super T> action) 对该流的每个元素执行动作。
    • Consumer接口中的方法 void accept(T t)
  • long count():返回此流中的元素数

Stream的综合练习

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

    str1.add("林青霞");
    str1.add("林楚蓉");
    str1.add("吴立");
    str1.add("李欣");
    str1.add("林初心");
    str1.add("貂蝉");

    str2.add("石田立");
    str2.add("天明");
    str2.add("杜天");
    str2.add("刘德华");
    str2.add("林俊杰");
    str2.add("蔡明宪");

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

    //女演员只要姓林的,除了第一个都要
    Stream<String> woman = str1.stream().filter(s -> s.startsWith("林")).skip(1);

    //把过滤后的男女演员合并到一起
    Stream<String> actorStream = Stream.concat(man, woman);

    //把上一步的合并流作为构造方法的参数创建演员对象,并遍历
    Stream<Actor> actorStream1 = actorStream.map(s -> new Actor(s));
    actorStream1.forEach(s -> System.out.println(s.getActor()));
    //简化后
    actorStream.map(Actor::new).forEach(s -> System.out.println(s.getActor()));


    //最终把所有代码简化为一条代码
    Stream.concat(str2.stream().filter(s -> s.length() == 3).limit(3),str1.stream().filter(s -> s.startsWith("林")).skip(1)).map(Actor::new).forEach(s -> System.out.println(s.getActor()));

}

Stream流的收集操作

public static void main(String[] args) {
    ArrayList<String> str1 = new ArrayList<String>();
    str1.add("林青霞");
    str1.add("林楚蓉");
    str1.add("吴立");
    str1.add("李欣");

    //得到名字为3个字的流
    str1.stream().filter(s -> s.length() == 3).collect(Collectors.toList()).forEach(System.out::println);


    //创建set数组
    Set<Integer> set = new HashSet<Integer>();
    set.add(10);
    set.add(20);
    set.add(30);
    set.add(40);
    set.add(55);

    //得到数字大于25的流并收集到一个新的hashset集合并遍历
    set.stream().filter(s -> s > 25).collect(Collectors.toSet()).forEach(System.out::println);

    System.out.println("-------");
    //创建字符串数组
    String[] str3 = {"xuzhiyuan,24", "tianming,30", "ximu,29", "shijuanjun,33"};

    //通过数组获取Stream的方法,获取大于28岁的字符串并收集到新的map集合,获取键值后遍历
    Map<String, String> map = Stream.of(str3).filter(s -> Integer.parseInt(s.split(",")[1]) > 28).collect(Collectors.toMap(s -> s.split(",")[0], s -> s.split(",")[1]));
    Set<String> key = map.keySet();
    for (String s : key) {
        System.out.println(s+","+map.get(s));
    }
}

标签:JAVA,String,Stream,strArr,System,接口,add,out
From: https://www.cnblogs.com/joeximu/p/16722372.html

相关文章