JDK8--函数式编程,Stream流

函数式接口:
概念:只有一个抽象方法的接口, 这种接口我们叫做函数式接口。

回顾一下 我们之前学过的函数式接口:
	Runnable 接口 里面只有一个 run方法。
		interface  Runnable{
			public abstract void run();
		}
	Comparable 接口 里面只有一个 compareTo方法
		interface  Comparable{
			public abstract int compareTo(T t);
		}
	Comparator 接口 里面 compare方法。

函数式接口注解的作用:
	情况: 项目经理让一个小白去写一个函数式接口。
		@FunctionalInterface   // 编译期起作用的注解。
		interface Inter {
			void show();
			//void method();  //编译报错
			//void fun();  // 编译报错
		}


		class Person {
			public void show(){
			
			}
		}
		class Student extends Person {
			@Override   // 编译期起作用的注解。
			public void sh0w(){ //编译报错
			
			}
		}

函数式接口使用:
	多态中去使用 --- 作为方法的参数  作为方法的返回值。

	作为方法的参数 :
		new Thread(
			new Runnable(){
				public void run(){
					System.out.println("线程任务");
				}
			}
		).start();
		
		new Thread(()->System.out.println("线程任务") ).start();
		
		new Thread(System.out::println ).start();
		
		new Thread(
			new Runnable(){
				public void run(){
					System.out.println();
				}
			}
		).start();
		
	作为方法的返回值 :
		
		public class Test {
			public static void main(String[] args){
				
				TreeSet<Integer> ts = new TreeSet<>(getComparator());
				ts.add(10);
				ts.add(20);
				ts.add(50);
				ts.add(30);
				ts.add(15);

			}

			public static Comparator<Integer> getComparator(){
				
				return new Comparator<Integer>(){
							public int compare(Integer in1, Integer in2){
								//return in1 - in2 ; // 从小到大。 
								//return in2 - in1 ; // 从大到小。
								return in2.compareTo(in1); // 从大到小。
							}
						};

				return (in1,in2) -> in2-in1  ;
				return (in1,in2) -> in2.compareTo(in1)  ;

				return Integer::compareTo ; // 从小到大。 
				
				return new Comparator<Integer>(){
							public int compare(Integer in1, Integer in2){
								return in1.compareTo(in2); / 从小到大。 
							}
						};
			}
		}

jdk1.8的时候出现了一些新的函数式接口:
	Supplier 供应商接口:Supplier<T>接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用
		 T get():获得结果
		该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据
		
		练习:
			定义一个类(SupplierTest),在类中提供两个方法
			一个方法是:int getMax(Supplier<Integer> sup)   用于返回一个int数组中的最大值
			一个方法是主方法,在主方法中定义一个数组,然后调用getMax方法 接收一个最大值。并打印
		代码:
			public class SupplierTest {
				public static void main(String[] args) {
					//定义一个int数组
					int[] arr = {19, 50, 28, 37, 46};
					
					int maxValue = getMax(()-> {  
						//arr = new int[]{2,4,7,1,3}; // 这是一个lambda表达式。 是不是一个匿名内部类的简写。  是不是一个局部的内部类。 局部内部类访问局部变量 局部变量前面有final修饰。 在jdk1.8的时候,final会默认给出 即使你不写 arr前面也默认有final
					   int max = arr[0];

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

					   return max;
					});

					System.out.println(maxValue);

				}

				//返回一个int数组中的最大值
				private static int getMax(Supplier<Integer> sup) {
					return sup.get();
				}
			}

	Consumer 消费型接口:	Consumer<T>接口也被称为消费型接口,它消费的数据的数据类型由泛型指定		
		void accept(T t):对给定的参数执行此操作
		default Consumer<T> andThen(Consumer after):返回一个组合的 Consumer,依次执行此操作,然后执行 after操作
		
		练习:
			String[] strArray = {"林青霞,30", "张曼玉,35", "王祖贤,33"};
			字符串数组中有多条信息,请按照格式:"姓名:XX,年龄:XX"的格式将信息打印出来
			要求:
				把打印姓名的动作作为第一个Consumer接口的Lambda实例
				把打印年龄的动作作为第二个Consumer接口的Lambda实例
				将两个Consumer接口按照顺序组合到一起使用
		代码:
			public class ConsumerTest {
				public static void main(String[] args) {
					String[] strArray = {"林青霞,30", "张曼玉,35", "王祖贤,33"};

					printInfo(strArray, str -> System.out.print("姓名:" + str.split(",")[0]),
							str -> System.out.println(",年龄:" + Integer.parseInt(str.split(",")[1])));
				}

				private static void printInfo(String[] strArray, Consumer<String> con1, Consumer<String> con2) {
					for (String str : strArray) {
						con1.andThen(con2).accept(str);
					}
				}
			}
				
	Predicate<T>:常用的四个方法 Predicate<T>接口通常用于判断参数是否满足指定的条件
		boolean test(T t):对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值
		default Predicate<T> negate():返回一个逻辑的否定,对应逻辑非
		default Predicate<T> and(Predicate other):返回一个组合判断,对应短路与
		default Predicate<T> or(Predicate other):返回一个组合判断,对应短路或
		
		练习:
			String[] strArray = {"林青霞,30", "柳岩,34", "张曼玉,35", "貂蝉,31", "王祖贤,33"};
			字符串数组中有多条信息,请通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayList中,并遍历ArrayList集合
			要求:同时满足如下要求
				1:姓名长度大于2
				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, s -> s.split(",")[0].length() > 2,
							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<T,R>:常用的两个方法  Function<T,R>接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现),然后返回一个新的值
		R apply(T t):将此函数应用于给定的参数
		default <V> Function andThen(Function after):返回一个组合函数,首先将该函数应用于输入,然后将after函数应用于结果
			
		练习:
			String s = "林青霞,30";
			请按照我指定的要求进行操作:
				1:将字符串截取得到数字年龄部分
				2:将上一步的年龄字符串转换成为int类型的数据
				3:将上一步的int数据加70,得到一个int结果,在控制台输出
			请通过Function接口来实现函数拼接
		
		代码:(andThen方法源码执行流程详见录屏和画图)
			public class FunctionTest {
				public static void main(String[] args) {
					String s = "林青霞,30";

					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 流 (☆☆):
和IO流 半毛钱关系都没有。
Stream流 是操作集合的。
之前200行代码能够通过操作集合搞定的功能
现在一行就搞定了。

Stream流的原理是基于:匿名内部类的延迟执行现象(详见录屏)
	分为三类方法:
		生成流方法(生成流其实就是生成一个匿名内部类对象)
		中间操作方法(不断的去拼接其他的匿名内部类对象,生成更大的匿名内部类对象)
		终结方法(最大的匿名内部类对象调用方法执行)

Stream流的常见生成方式
    1:Collection体系的集合可以使用默认方法stream()生成流
        default Stream<E> stream()
    2:Map体系的集合间接的生成流
    3:数组可以通过Stream接口的静态方法of(T... values)生成流
	
	代码:
		public class StreamDemo {
			public static void main(String[] args) {
				//Collection体系的集合可以使用默认方法stream()生成流
				List<String> list = new ArrayList<String>();
				Stream<String> listStream = list.stream();

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

				//Map体系的集合间接的生成流
				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(T... values)生成流
				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);
			}
		}
Stream流的中间操作方法:
	Stream<T> 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(s -> s.startsWith("张")).forEach(System.out::println);
					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);
				}
			}
	Stream<T> limit(long maxSize):返回此流中的元素组成的流,截取前指定参数个数的数据
	Stream<T> 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("张无忌");

					//需求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);
				}
			}
	tatic <T> Stream<T> concat(Stream a, Stream b):合并a和b两个流为一个流
	Stream<T> 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);
				}
			}
	Stream<T> sorted():返回由此流的元素组成的流,根据自然顺序排序
	Stream<T> 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);
				}
			}

	<R> Stream<R> 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);
				}
			}
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:统计集合中有几个以张开头的元素,并把统计结果在控制台输出
					long count = list.stream().filter(s -> s.startsWith("张")).count();
					System.out.println(count);
				}
			}
	Stream流的收集方法
		R collect(Collector collector)
			它是通过工具类Collectors提供了具体的收集方式
				public static <T> Collector toList():把元素收集到List集合中
				public static <T> Collector toSet():把元素收集到Set集合中
				public static Collector toMap(Function keyMapper,Function valueMapper):把元素收集到Map集合中
		代码:
			public class CollectDemo {
				public static void main(String[] args) {
					//创建List集合对象
					List<String> list = new ArrayList<String>();
					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> names = listStream.collect(Collectors.toList());
					for(String name : names) {
						System.out.println(name);
					}
					
					//创建Set集合对象
					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> setStream = set.stream().filter(age -> age > 25);

					//需求4:把使用Stream流操作完毕的数据收集到Set集合中并遍历
					Set<Integer> ages = setStream.collect(Collectors.toSet());
					for(Integer age : ages) {
						System.out.println(age);
					}
					
					//定义一个字符串数组,每一个字符串数据由姓名数据和年龄数据组合而成
					String[] strArray = {"林青霞,30", "张曼玉,35", "王祖贤,33", "柳岩,25"};

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

					//需求6:把使用Stream流操作完毕的数据收集到Map集合中并遍历,字符串中的姓名作键,年龄作值
					Map<String, Integer> map = arrayStream.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);
					}

				}
			}


综合练习:
	现在有两个ArrayList集合,分别存储6名男演员名称和6名女演员名称,要求完成如下的操作:
		1:男演员只要名字为3个字的前三人
		2:女演员只要姓林的,并且不要第一个
		3:把过滤后的男演员姓名和女演员姓名合并到一起
		5:合并到一起之后,去掉重复的名字,然后返回把这些元素存储到一个新的List集合中。
		6:把上一步操作后的集合里面的元素 每个元素 封装到 演员对象中,把所有的演员对象 存储到一个新的集合中。(这一步必须用Stream流)
		7:统计这个新集合里面演员 名字 是两个字。 的人员个数。
			演员类Actor已经提供,里面有一个成员变量,一个带参构造方法,以及成员变量对应的get/set方法

		练习题答案(分步做):
			List<String> man = List.of("张敬轩", "张铁林", "承龙", "黄渤", "林心如", "冯小刚");
			List<String> woman = List.of("林心如", "杨紫", "杨幂", "景甜", "林青霞", "林颖");

			Stream<String> manStream = man.stream().filter(s -> s.length() == 3).limit(3);
			Stream<String> womanStream = woman.stream().filter(s -> s.startsWith("林")).skip(1);

			List<String> allPerson = Stream.concat(manStream, womanStream).distinct().collect(Collectors.toList());

			//这是第一种做法
			//ArrayList<Actor> al = new ArrayList<>();
			//for (String name : allPerson) {
			//    al.add(new Actor(name));
			//}

			//这是第二种做法
			//ArrayList<Actor> al = new ArrayList<>();
			//AllPerson.stream().forEach(s-> al.add(new Actor(s)));

			//这是第三种做法
			List<Actor> collect = allPerson.stream().map(s -> new Actor(s)).collect(Collectors.toList());
			long count = collect.stream().filter(a->a.getName().length()==2).count();
			System.out.println(count);
	
		练习题答案(合成一步做):
			System.out.println(
					Stream.concat(
						List.of("张敬轩s", "张铁林", "承龙", "黄渤", "林心如", "冯小刚").stream().filter(s -> s.length() == 3).limit(3),
						List.of("林心如", "杨紫", "杨幂", "景甜", "林青霞", "林颖").stream().filter(s -> s.startsWith("林")).skip(1)
					).distinct().map(s -> new Actor(s)).filter(a->a.getName().length()==2).count()
			);
posted @ 2021-01-08 02:01  红尘客栈-zhang  阅读(57)  评论(0)    收藏  举报