Java-进阶-day14-Stream流

Java进阶-day14-Stream流

今日内容

  • 函数式接口
  • 常用函数式接口
  • Stream流

自定义函数式接口&函数式编程

自定义函数式接口

函数式接口概念和定义
  • 函数式接口的概念
  • 函数式接口的定义
  • 总结:
    • A:函数式接口的概念
      • 接口中有且只有一个抽象方法
    • B:函数式接口的定义
@FunctionalInterface
public interface 接口名 {
  抽象方法
}
  • 问题:
    • 函数式接口是否可以有默认或者静态方法?
      • 可以,函数式接口只跟抽象方法有关系,跟其他无关
函数式接口的使用
  • 作为方法的参数
  • 作为方法的返回值
  • 总结:
    • 作为方法的参数
      • 传递的是接口的实现类
      • 使用匿名内部类
      • 如果这个接口是一个函数式接口,可以使用使用Lambda表达式
  1. 类名可以作为方法的参数和返回值
  2. 抽象类可以作为方法的参数和返回值
  3. 接口可以作为方法的参数和返回值。如果这个接口是一个函数式接口,我们可以使用Lambda表示进行传参数和作为返回值
  • 案例代码:
public class TestInterface {
    public static void main(String[] args) {
        // 使用匿名内部类
        method(new MyInterface() {
            @Override
            public void method1() {
                System.out.println("method方法执行了");
            }
        });
        // 采用Lambda表达式进行改进
        method(() -> System.out.println("使用Lambda表达式method方法执行了"));
    }

    public static void method(MyInterface mf){
        mf.method1();
    }
}

  • 作为方法的返回值
	 public static MyInterface fun1() {
	        return () -> System.out.println("函数式接口作为方法的返回值...");
	        /*return new MyInterface() {
	            @Override
	            public void method1() {
	                System.out.println("函数式接口作为方法的返回值...");
	            }
	        };*/

	    }

常用函数式接口

Supplier 接口
  • 总结:

    • 什么是生产型?

      • 用于生产数据的, 简单说就是带有返回值的.
    • Supplier介绍:

      • java.util.function.Supplier 接口仅包含一个无参的方法: T get() 。
        用来获取一个泛型参数指定类型的对象数据
      • get方法 : 就是生产数据的方法
  • 案例代码:

public static void main(String[] args) {
	int i = getInt(() -> Integer.parseInt("100"));
	System.out.println(i);
}

public static int getInt(Supplier<Integer> s) {
	// 通过T get方法来生成数据
	return s.get();
}

  • Supplier接口练习_求数组元素最大值
  • 需求:
    • 使用 Supplier 接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值。
  • 案例代码:
public class SupplierTest {
    public static void main(String[] args) {
       // 求数组中的最大值
        int[] arr =  {1,2,3,4,5};


       int max_value =  getMax(() ->{
            // 定义一个变量,假设这个变量是最大值
            int max = arr[0];

            for (int i = 1; i < arr.length; i++) {
                if (arr[i] > max) {
                    max = arr[i];
                }
            }
            return max;
        });

        System.out.println(max_value);

    }

    public static int getMax(Supplier<Integer> s) {
        // 通过T get方法来生成数据
        return s.get();

    }
}

Consumer 接口
  • 总结:

  • 01 什么是消费型?

    • 不生产数据, 只使用数据.
  • 02 Consumer介绍:

    • java.util.function.Consumer 接口则正好相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛型参数决定
  • 03 Consumer 接口的默认方法andThen

    • andThen()
      • 如果一个方法的参数和返回值全都是 Consumer 类型
        那么就可以实现效果:消费一个数据的时候,首先做一个操作,然后再做一个操作,实现组合
  • 案例代码:

public class ConsumerDemo1 {
    public static void main(String[] args) {
        method1("小希",str -> System.out.println(str));
        // 定义一个字符串
        String name = "姓名:麻生希";

        method2(name,
                str -> System.out.println(str.split(":")[0]),
                str -> System.out.println(str.split(":")[1])
                );

    }
    public static void method2(String name, Consumer<String> con1,Consumer<String> con2){
        // 使用andThen
        con1.andThen(con2).accept(name);
    }


    // 使用Consumer接口消费一个字符串
    public static void method1(String name, Consumer<String> con){
        con.accept(name);
    }
}

  • 04 Consumer接口练习_字符串拼接输出

    • 下面的字符串数组当中存有多条信息,String[] arr = {“柳岩,38”,“汪峰,40”,“杨幂,30”};请按照格式“ 姓名:XX。性别:XX。 ”的格式将信息打印出来。
    • 要求:

    将打印姓名的动作作为第一个 Consumer 接口的Lambda实例,

    将打印性别的动作作为第二个 Consumer 接口的Lambda实例,

    将两个 Consumer 接口按照顺序“拼接”到一起。

public class ConsumerTest {
    public static void main(String[] args) {
        // 定义一个数组
        String[] arr = {"柳岩,38","汪峰,40","杨幂,30"};
        // 遍历数组
        for (String str : arr) {
            // 将遍历到的字符串 传递到方法中进行消费数据
            fun(str, name -> System.out.print("姓名:"+name.split(",")[0]),
                    name -> System.out.println(",年龄:"+name.split(",")[1])
                    );
        }
    }

    public static void fun(String name, Consumer<String> con1, Consumer<String> con2){
        // 使用andThen
        con1.andThen(con2).accept(name);
    }
}

Predicate 接口
  • 01 Predicate概述:

    • 需要对某种类型的数据进行判断,从而得到一个boolean值结果,这时可以考虑使用java.util.function.Predicate 接口
  • 02 Predicate接口中的方法

    • boolean test(T t) : 返回值 boolean类型
  • 03 Predicate 的基本使用

  • 案例演示


public class PredicateDemo {
    public static void main(String[] args) {
        // 1.定义一个字符串
        String str = "lasjdflasjfl";
        // 2.判断字符串是否包含"w"
        boolean flag = isStr(str, s -> {
            return s.contains("w");
        });
        // 输出结果
        System.out.println(flag);
    }

    // 使用Predicate接口判断一个字符串是否是包含XXX
    public static boolean isStr(String str , Predicate<String> pre){
        // return pre.test(str);
        return pre.negate().test(str);
    }
}

  • 04 Predicate 接口中的默认方法

    • and:
      相当于 &&
      有false则为false

    • or:
      相当于 ||
      有 true 则为 true

    • negate:
      相当于 !

      非true则为false

      非false则为true

  • 05 Predicate接口练习_集合信息筛选

    • 需求:

      String[] strArray = {“林青霞,30”, “柳岩,34”, “张曼玉,35”, “貂蝉,31”, “王祖贤,33”};

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

    • 需要同时满足两个条件:

      • 姓名长度大于2
      • 年龄大于33
    • 分析:

      • 1.有两个判断条件,所以需要使用两个Predicate接口,对条件进行判断
      • 2.必须同时满足两个条件,所以可以使用and方法连接两个判断条件

public class PredicateTest {
    public static void main(String[] args) {
        String[] strArray = {"林青霞,30", "柳岩,34", "张曼玉,35", "貂蝉,31", "王祖贤,33"};

        ArrayList<String> array = myFilter(
                // 需要遍历的数组
                strArray,
                // 姓名长度大于2
                s -> s.split(",")[0].length() > 2,
                // 年龄大于33
                // 首先,截取年龄,截取的结果是一个字符串,
                // 想要跟整数进行比较,必须将字符串的年龄转换成整数
                s -> Integer.parseInt(s.split(",")[1]) > 33
        );

        for (String str : array) {
            System.out.println(str);
        }
    }

    //通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayList中
    private static ArrayList<String> myFilter(String[] strArray, Predicate<String> pre1, Predicate<String> pre2) {
        //定义一个集合
        ArrayList<String> array = new ArrayList<String>();

        //遍历数组
        for (String str : strArray) {
            if (pre1.and(pre2).test(str)) {
                array.add(str);
            }
        }

        return array;
    }
}

Function 接口
  • 01 Function 概述:

接口用来根据一个类型的数据得到另一个类型的数据,“123” --> 123 —> “123”

前者称为前置条件,后者称为后置条件。

  • 映射: 转换数据的 String --> Integer

  • Function接口中最主要的抽象方法为:

    • R apply(T t),根据类型T的参数获取类型R的结果。

    • 应用场景: 将字符串转换为int 将int转换为String…

  • 02 Function 接口中的默认方法

    • andThen:用来进行组合操作
  • 案例代码:

public class FunctionDemo1 {
    public static void main(String[] args) {
        method("123",
                s -> Integer.parseInt(s),
                i -> (i + 100)+""
                );
    }

    // 将字符串的数据转换成int 然后在将int数据转换成字符串
    public static void method(String str, Function<String,Integer> fun1,Function<Integer, String> fun2) {
        String s = fun1.andThen(fun2).apply(str);
        System.out.println(s);
    }
}

  • 案例演示:

    • 需求:

      String s = “林青霞,30”;

      将字符串截取得到数字年龄部分 String String

      将上一步的年龄字符串转换成为int类型的数据 String Integer

      将上一步的int数据加70,得到一个int结果,在控制台输出 Integer Integer

    • 分析

      第一次是把String类型转换为了String类型 所以我们可以使用Function fun1 String i = fun1.apply(“30”);

      第二次是把Integer类型转换为String类型,所以我们可以使用Function fun2int s = fun2.apply(i);

      第三次是把Integer类型转换为Integer类型, 所以我们可以使用Function fun3 int age = fun2.apply(s)+70;

      我们可以使用andThen方法,把三次转换组合在一起使用

      String s = fun1.andThen(fun2).andThen(fun3).apply(“30”);

      fun1先调用apply方法,把字符串转换为String

      fun2再调用apply方法,把String转换为Integer

      fun3再调用apply方法,把Integer转换为Integer

  • 案例代码:

		public class FunctionTest {
		    public static void main(String[] args) {
		        String s = "林青霞,30";

		//        convert(s, (String ss) -> {
		//            return ss.split(",")[1];
		//        },(String ss) -> {
		//            return Integer.parseInt(ss);
		//        },(Integer i) -> {
		//            return i + 70;
		//        });

		       convert(
		               // 要操作的字符串
		               s,
		               // 截取字符串年龄
		               ss -> ss.split(",")[1],
		               // 将字符串的年龄转换成int类型的年龄
		               ss -> Integer.parseInt(ss),
		               // 将转换后的int的年龄 + 70 
		               i -> i + 70
		       );
		//         convert(s, ss -> ss.split(",")[1], Integer::parseInt, i -> i + 70);
		    }

		    private static void convert(String s, Function<String, String> fun1, Function<String, Integer> fun2, Function<Integer, Integer> fun3) {
		//        Integer i = fun1.andThen(fun2).andThen(fun3).apply(s);
		        int i = fun1.andThen(fun2).andThen(fun3).apply(s);
		        System.out.println(i);
		    }
		}

Stream流

  • Stream引言
  • 在Java 8中,得益于Lambda所带来的函数式编程
    * 引入了一个全新的Stream概念,用于解决已有集合类库现有的弊端

流式思想概述

  • 思路: 流式思想类似于工厂车间的“生产流水线”
  • 特点: 只能使用一次,流是一次性的
  • 流式思想的函数模型拼接

大白话: 对一组数据进行多次过滤…筛选…统计…

数据源: A B C D E

筛选后: A B C D

映射后: Aa Bb Cc Dd

跳过后: Bb Cc Dd

获取Stream流的方式

  • 所有的集合都可以通过 stream 默认方法获取流
1. List:
	List<String> list = new ArrayList<String>();
    Stream<String> listStream = list.stream();
2. Set:<br>
	Set<String> set = new Set<String>();
    Stream<String> listStream = set.stream();
3. Map:<br>
	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();
  • Stream 接口的静态方法 of 可以获取数组对应的流
Stream<T> stream = Stream.of(T...args);
String[] strArray = {"hello","world","java"};
Stream<String> strArrayStream = Stream.of(strArray);
Stream<String> strArrayStream2 = Stream.of("hello", "world", "java");
Stream<Integer> intStream = Stream.of(10, 20, 30);

StreamApi之filter过滤功能

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

  • 重点 : Stream流只能消费一次

    • 流水线进入了下一层作业, 不能回退
  • filter案例演示


/*
    Stream filter​(Predicate predicate):用于对流中的数据进行过滤
    Predicate接口中的方法	boolean test​(T t):对给定的参数进行判断,返回一个布尔值
 */
public class StreamDemo01 {
    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集合中以张开头的元素在控制台输出
//        list.stream().filter((String s) -> {
//            return s.startsWith("张");
//        }).forEach(System.out::println);

        Stream<String> stream = list.stream().filter(s -> s.startsWith("张"));
        stream.forEach(System.out::println);
        // 流用完就用完了,是一次性的,如果再想用重新获取流即可
        // 流达到最终一个操作之后,如果再使用这个流:stream has already been operated upon or closed
        // stream.filter(s -> s.startsWith("张"));
        System.out.println("--------");


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

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

StreamAPI之limit截取方法

  • limit方法可以对流进行截取,只取用前n个
    • 参数是一个long型,如果传入的参数, 大于集合的长度, 那么返回的结果, 将会是集合中所有的数据.

    • 如果传入的是一个负数的参数, 那么会出现异常.

    • limit(n); // 1-n

StreamAPI之skip跳过方法

  • 如果希望跳过前几个元素,可以使用 skip 方法获取一个截取之后的新流

    • skip(n); // n-end
    • 如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流
  • 示例代码:

	/*
	    Stream limit​(long maxSize):返回此流中的元素组成的流,截取前指定参数个数的数据
	    Stream skip​(long n):跳过指定参数个数的数据,返回由该流的剩余元素组成的流
	 */
	public class StreamDemo02 {
	    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(name-> name.length() == 3).limit(2).forEach(System.out::println);
	        list.stream().filter(name-> name.length() == 3).skip(2).forEach(System.out::println);

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

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

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

StreamAPI之静态方法 concat 合并流

  • concat : 将两个流, 整合为一个流

    • Stream.concat() : 静态方法, 将两个流, 整合为一个流.

StreamAPI之distinct返回由该流的不同元素

  • count方法 : 去除流中的重复的元素,根据Object.equals(Object)

  • 示例代码:

	/*
	    static  Stream concat​(Stream a, Stream b):合并a和b两个流为一个流
	    Stream distinct​():返回由该流的不同元素(根据Object.equals(Object) )组成的流
	 */
	public class StreamDemo03 {
	    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> s1 = list.stream().limit(4);

	        //需求2:跳过2个数据组成一个流
	        Stream<String> s2 = list.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);
	    }
	}

StreamAPI之 sorted 排序

  • sorted 方法 : 根据自然顺序排序

  • sorted(Comparator comparator)方法: 根据提供的Comparator进行排序

  • 示例代码:

	/*
	    Stream sorted​():返回由此流的元素组成的流,根据自然顺序排序
	    Stream sorted​(Comparator comparator):返回由该流的元素组成的流,根据提供的Comparator进行排序
	        Comparator 接口中的方法	int compare​(T o1, T o2)
	 */
	public class StreamDemo04 {
	    public static void main(String[] args) {
	        //创建一个集合,存储多个字符串元素
	        ArrayList<String> list = new ArrayList<String>();

	        list.add("linqingxia");
	        list.add("zhangmanyu");
	        list.add("wangzuxian");
	        list.add("liuyan");
	        list.add("zhangmin");
	        list.add("zhangwuji");



	        //需求1:按照字母顺序把数据在控制台输出
	//        list.stream().sorted().forEach(System.out::println);

	        //需求2:按照字符串长度把数据在控制台输出
	//        list.stream().sorted((s1, s2) -> s1.length() - s2.length()).forEach(System.out::println);

	        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);
	    }
	}

StreamAPI之map映射方法

  • map : 需要将流中的元素映射到另一个流中,可以使用 map 方法
    • Stream map(Function mapper);
  • mapToInt
    • IntStream mapToInt(ToIntFunction mapper):返回一个IntStream其中包含将给定函数应用于此流的元素的结果
	/*
	     Stream map​(Function mapper):返回由给定函数应用于此流的元素的结果组成的流
	        Function接口中的方法		R apply​(T t)
	    IntStream mapToInt​(ToIntFunction mapper):返回一个IntStream其中包含将给定函数应用于此流的元素的结果
	        IntStream:表示原始 int 流
	        ToIntFunction接口中的方法	 int applyAsInt​(T value)
	 */
	public class StreamDemo05 {
	    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 sum​() 返回此流中元素的总和
	        int result = list.stream().mapToInt(Integer::parseInt).sum();
	        System.out.println(result);


	    }
	}

StreamAPI之forEach逐一处理方法

  • 当前forEach是一个方法, 并不是增强for循环

    • 区分:
      • 当前的forEach方法, 依赖于的是函数式接口
      • 增强for, 依赖的是迭代器.

StreamAPI之count统计个数

  • count方法 : 统计流中的元素个数
    • 终结方法

综合案例

  • 案例需求

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

    1. 男演员只要名字为3个字的前三人
    2. 女演员只要姓林的,并且不要第一个
    3. 把过滤后的男演员姓名和女演员姓名合并到一起
    4. 把上一步操作后的元素作为构造方法的参数创建演员对象,遍历数据
  • 案例代码

public class StreamTest {

	public static void main(String[] args) {

		ArrayList<String> maleList = new ArrayList<>();
		maleList.add("周润发");
		maleList.add("成龙");
		maleList.add("刘德华");
		maleList.add("吴京");
		maleList.add("周星驰");
		maleList.add("李连杰");

		ArrayList<String> femaleList = new ArrayList<>();
		femaleList.add("林心如");
		femaleList.add("张曼玉");
		femaleList.add("林青霞");
		femaleList.add("柳岩");
		femaleList.add("林志玲");
		femaleList.add("王祖贤");

	//	Stream maleStream = maleList.stream().filter(s -> s.length() == 3).limit(3);
	//	Stream femaleStream = femaleList.stream().filter(s -> s.startsWith("林")).skip(1);

	//	Stream.concat(maleStream,femaleStream).map(Actor::new).forEach(p -> System.out.println(p.getName()));

		Stream.concat(
				maleList.stream().filter(s -> s.length() == 3).limit(3),
				femaleList.stream().filter(s -> s.startsWith("林")).skip(1)).
				map(Actor::new).
				forEach(p -> System.out.println(p.getName())
				);

	}
}


你可能感兴趣的:(Java进阶)