JavaSE-Lambda 表达式,函数式接口,Stream API

1. Lambda 表达式

         1.1 概述

         Lambda表达式是一种没有名字的函数,也可称为闭包,是java8发布的最重要的新特性。

 本质上是一段匿名内部类, 也可以是一段可以传递的代码,lambda表达式也被叫做箭头函数。

         闭包: 闭包就是能够读取其它函数内部变量的函数,比如在java中,方法内部的局部变量只能在方法内部使用,所以闭包可以理解为定义在一个函数内部的函数。闭包的本质就是函数内部和函数外部链接起来的桥梁。

         1.2 特点

         允许把函数作为一个方法的参数(函数作为参数传递进方法中)。使用lambda表达式可以使代码变得更加简介紧凑。

         和匿名内部类对比:

//和匿名内部类对比
public class Lambda_00 {
	public static void main(String[] args) {
		// 将数组中的元素逆序输出
		Integer[] arr = {1,2,3,5,3,7,9,3,2};
		// asList:把数组转换为list集合
		List integers = Arrays.asList(arr);
		// 匿名内部类写法
		Collections.sort(integers, new Comparator() {

			@Override
			public int compare(Integer o1, Integer o2) {
				return o2 - o1;
			}
		});
		
		// lambda表达式写法
		Collections.sort(integers,(o1,o2) -> o2 - o1);
		System.out.println(integers);
		
	}
}

         1.3 应用场景

          1.列表迭代           2.Map映射         3.Reduce聚合         4.代替一个不想命名的函数或类,该函数或类一般不会太复杂         5.精简代码

         1.4 代码实现

                 1.4.1 具体语法

                         1.(parameters) -> expression

                         2.(parameters) -> (statements)

                 1.4.2 语法特点

                         1. 可选类型声明: 不需要声明参数类型,编译器可以统一识别参数值

                         2. 可选参数圆括号: 只有一个参数时可以不加(),但是有多个参数或者没有参数时,一定要加()

                         3. 可选的大括号: 如果主体只有一个语句的时候,可以不加大括号,此时分号也不加。如果主体有多行代码的时候,遵守正常的编码规范,该写大括号写大括号,该写分号写分号。

                         4. 可选的返回关键字: 如果主体只有一个表达式返回值,编译器会自动返回,不需要写return。如果主题有多条语句,需要在大括号里指明表达式要返回的数值,需要写return。

                         5。 并不是所有的匿名内部类都可以改写成lambda表达式的形式,lambda表达式对应的接口有且只有一个抽象方法。

                 1.4.3 集合遍历

// 集合遍历
public class Lambda_01 {
	public static void main(String[] args) {
		String[] arr = {"a", "b", "c"};
		// 把数组转换为集合
		List list = Arrays.asList(arr);
		// 1.8 以前的遍历
		for (String string : list) {
			System.out.println(string);
		}
		// 1.8 以后
		// 匿名内部类
		list.forEach(new Consumer() {
			@Override
			public void accept(String t) {
				System.out.println(t);
			}		
		});
		
		// lambda表达式
		list.forEach(i -> System.out.println(i));
	}
}
                 1.4.4 集合排序
// 集合排序
public class Lambda_02 {
	public static void main(String[] args) {
		Integer[] arr = {1,3,5,2,0,9,4,6};
		List list = Arrays.asList(arr);
		// 1.8以前
		Collections.sort(list,new Comparator() {

			@Override
			public int compare(Integer o1, Integer o2) {
				return o2 - o1;
			}
		});
		// 1.8 以后
		list.sort((o1,o2) -> o2 - o1);
		System.out.println(list);
	}
}

2. 函数式接口

         2.1 概述

         其本质是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口

         核心目的是为了给lambda表达式更好的支持,进一步达到函数式编程的目的

         函数式编程可以极大的提高编码的效率

         其可以被隐式转换为lambda表达式

         2.2 特点

         函数式接口有且仅有一个抽象方法,可以包含其它非抽象方法

         @FunctionalInterface注解可以检查是否是抽象接口,添加注解后,

         接口一定要满足定义,否则报错。只要满足定义就一定是函数式接口,也可以不加注解。

         回调函数: 方法的参数是另一个方法,在当前方法中对传递过来的方法进行调用

         2.3 应用场景

         适用于任何需要提高编码效率的场景,需结合具体情况调用

         2.4 代码实现

                 2.4.1 无参情况
// 无参情况
public class Functional_00 {
	public static void main(String[] args) {
		// 1. 实现类方式,直接创建子类对象
		test(new Test());
		
		// 2. 匿名内部类方式
		test(new MyFunctionface() {
			
			@Override
			public void print() {
				System.out.println("匿名内部类");
			}
		});
		
		// 3. lambda表达式方式
		test(() -> System.out.println("lambda"));
	}
	
	public static void test(MyFunctionface myFunctionface) {
		myFunctionface.print();
	}
}

// 函数式接口
@FunctionalInterface
interface MyFunctionface{
	// 抽象方法
	void print();
}

class Test implements MyFunctionface{
	@Override
	public void print() {
		System.out.println("子类覆写");
	}
	
}
                 2.4.2 有参情况
// 有参情况
public class Functional_01 {
	public static void main(String[] args) {
		// 1. 类实现方式
		test(new Test1(), "子类实现");
		
		// 2. 匿名内部类实现
		test(new MyFunInterface_1() {
			
			@Override
			public void print(String msg) {
				System.out.println(msg);
			}
		}, "匿名内部类");
		
		// lambda表达式实现
		test((x) -> System.out.println(x), "lambda表达式");
		
		// 创建函数对象方式
		MyFunInterface_1 obj = (x) -> System.out.println(x);
		obj.print("函数对象");
		
	}
	
	public static void test(MyFunInterface_1 myFunInterface_1,String msg) {
		myFunInterface_1.print(msg);
	}
}

@FunctionalInterface
interface MyFunInterface_1{
	void print(String msg);
}

class Test1 implements MyFunInterface_1{

	@Override
	public void print(String msg) {
		System.out.println(msg);
	}

}

         2.5 JDK自带的常用的函数接口

                 1. Supplier接口


/*
 * Supplire接口,代表结果供应商,有返回值,提供一个get方法用于获取数据
 * 
 */
public class Functional_02 {
	public static void main(String[] args) {
		test(() -> "你好");
	}
	
	public static void test(Supplier supplier) {
		System.out.println(supplier.get());
	}
}

                 2. Consumer接口

//Consumer表示消费者,提供了一个accept(T) 方法,有参无返回值
public class Functional_03 {
	public static void main(String[] args) {
		String msg = "消费";
		test(result -> System.out.println(result), msg);
				
	}
	
	public static void test(Consumer fun, String msg) {
		fun.accept(msg);
	}
}

                 3. Function接口

// Function 接口 , R apply(T)有参有返回值,泛型中T表示参数列表类型,R表示返回值类型
public class Functional_04 {
	public static void main(String[] args) {
		String str = "1234";
		test(x -> {int num = Integer.parseInt(str); return num;}, str);
	}
	
	public static void test(Function fun, String msg) {
		int num = fun.apply(msg);
		System.out.println(num);
		
	}
}

                 4. Predicate接口


// Predicate 接口,断言,判断相关, boolean test(T)方法
public class Functional_05 {
	public static void call(Predicate predicate,String msg) {
		System.out.println(predicate.test(msg));
	}
	
	public static void main(String[] args) {
		String msg = "张三";
		call(x -> x.equals("张三"), msg);
	}
}

3. 方法引用和构造器调用

         3.1 概述

          lambda表达式的另一种表现形式,提高方法复用率和灵活性

         3.2 特点

          更简便,代码量更少,复用性、扩张性更高

         3.3 应用场景

         若lambda体中的功能,已经有了方法提供实现,可以使用方法引用

         不需要在覆写已有API的lambda实现

         3.4 代码实现

                 1. 对象引用::成员方法名

/*
 * 成员方法
 * 对象引用 :: 成员方法名
 * 根据调用方法的入参和出参选择相应函数式接口接收
 * 
 */
public class FunCall_00 {
	
	public static void main(String[] args) {
		Integer i1 = new Integer("123");
		System.out.println(i1.equals(123));
		// lambda表达式
		Predicate p = i1::equals;
		System.out.println(p.test(123));
	}
}

                 2. 类名::静态方法名

/*
 * 静态方法
 * 类名::静态方法名
 */
public class FunCall_01 {
	public static void main(String[] args) {
		System.out.println(Integer.max(4, 3));
		BiFunction bf = Integer::max;
		System.out.println(bf.apply(4, 3));
	}
}

                 3. 类名::成员方法名

// 类名::成员方法名
public class FunCall_02 {
	public static void main(String[] args) {
		BiPredicate bp = String :: equals;
		System.out.println(bp.test("abc", "abc"));
	}
}

                 4. 构造方法

// 构造方法
public class FunCall_03 {
	public static void main(String[] args) {
		Object o1 = new Object();
		
		// lambda 
		// 无参
		Supplier supplier = () -> new Object();
		System.out.println(supplier.get());
		
		supplier = Object::new;
		System.out.println(supplier.get());
		
		// lambda 
		// 有参
		Function function = (x) -> new Integer(x);
		System.out.println(function.apply("1234"));
		function = Integer::new;
		System.out.println(function.apply("12"));
	}
}

4. Stream API

         4.1 概述

         数据渠道、管道,用于操作数据源(集合,数组等)所生成的元素序列。

         即一组用来处理数组,集合的API

         4.2 特点

         Stream 不是数据结构,没有内部存储,自己不会存储数据

         Stream 不会改变源对象,他们会返回一个持有结果的新Stream

         Stream 操作是延迟执行的,这意味着他们会等到需要结果时才执行

         Stream 不支持索引访问,支持并行,支持过滤,查找,转换,汇总,聚合等操作

        4.3 应用场景

         流式计算处理,需要延迟计算,更方便的并行计算,更灵活的集合处理方式的场景。

         4.4 运行机制说明

         Stream分为源source,中间操作,终止操作。

         流的源可以是一个数组,集合,生成器方法,I/O通道等等。

         一个流可以有零个或多个中间操作,每一个中间操作都会返回一个新的流,供下一个操作使用,一个流只会有一个终止操作。

          中间操作也称为转换算子-transformation

           Stream只有遇到终止操作,它的数据源会开始执行遍历操作。终止操作也称为动作算子-action,因为动作算子的返回值不再是 stream,所以这个计算就终止了

           只有碰到动作算子的时候,才会真正的计算

         4.5 代码实现

                 1. 生成Stream流的五种方式

// 生成Stream流的五种方式
public class Stream_00 {
	public static void main(String[] args) {
		// 1. 数组
		String[] str = {"a","c","d","r","f"};
		Stream stream1 = Stream.of(str);
		
		// 2. 集合
		List list = Arrays.asList(str);
		Stream stream2 = list.stream();
		
		// 3. 通过Stream.generate创建
		// 创建的是一个无限流(无限大),通常结合limit一起使用,limit限制输出的最大条数
		Stream stream3 = Stream.generate(() -> 1); // 无数个1
		stream3.limit(10).forEach(x -> System.out.println(x)); // 输出10个1
		
		// 4. 通过Stream.iterate创建
		// 创建的是一个无限流(无限大),通常结合limit一起使用,limit限制输出的最大条数
		// 1 表示起始值为1,返回x+2表示步长为2,里面的数据就是1,3,5,7....
		Stream stream4 = Stream.iterate(1, x -> x+2);
		stream4.limit(10).forEach(x -> System.out.println(x));
		
		// 5. 通过已有类的API
		String strs = "sadfwee";
		IntStream chars = strs.chars();
		chars.forEach(X -> System.out.println(X));
	}
}

                 2. 常用转换算子

                         filter: 对元素进行过滤筛选,去掉不符合条件的元素

                         distinct: 去重

                         skip: 跳过多少条数据

                         limit: 取一个集合的前几条

                         map: 对符合条件的数据进行修改,filter返回值true就要,false就不要,但是map不是,map是把返回值重新保存

                         sorted: 排序

// 常用转换算子
public class Stream_01 {
	public static void main(String[] args) {
		List strings = Arrays.asList("a","c","d","a","s","f");
		// filter : 对元素进行过滤
		// collect 时动作算子,把结果返回集合
		Stream stream = strings.stream();
		List result = stream.filter(x -> x.equals("a")).collect(Collectors.toList());
		System.out.println(result); //[a, a]
		
		// 操作完成后Stream需要重新生成
		// skip: 跳过多少条数据
		stream = strings.stream();
		result = stream.skip(2).collect(Collectors.toList());
		System.out.println(result);//[d, a, s, f]
		
		// distinct: 去重
		stream = strings.stream();
		result = stream.distinct().collect(Collectors.toList());
		System.out.println(result);//[a, c, d, s, f]
		
		// map
		stream = strings.stream();
		result = stream.map(x -> x.equals("a")? x + "+++" : x).collect(Collectors.toList());
		System.out.println(result);//[a+++, c, d, a+++, s, f]
		
		// sorted: 排序
		List list = Arrays.asList(1,2,4,2,0,9,1,2);
		Stream stream1 = list.stream();
		// 默认升序
		// List value = stream1.sorted().collect(Collectors.toList());
		// 降序
		List value = stream1.sorted((o1,o2) -> o2-o1).collect(Collectors.toList());
		System.out.println(value); //[9, 4, 2, 2, 2, 1, 1, 0]
	}
}

                 3. 常用动作算子

                         循环 forEach

                         计算 min、max、count、average

                         匹配 anyMatch、allMatch、noneMatch、findFirst、findAny

                         汇聚 reduce

                         收集器 collect

// 常用动作算子
public class Stream_02 {
	public static void main(String[] args) {
		List strings = Arrays.asList("a","c","f","e","c","a");
		Stream stream = strings.stream();
		// forEach: 遍历
		stream.forEach(x -> System.out.println(x));		
		
		// count : 统计计数,返回long类型
		stream = strings.stream();
		long count = stream.count();
		System.out.println(count);
		// 一般count需要集合一些中间算子,才有价值,否则不如直接使用集合,比如统计集合中有、多少个a
		stream = strings.stream();
		count = stream.filter(x -> x.equals("a")).count();
		System.out.println(count);
		
		// collect 把运算结果封装到集合中
		stream = strings.stream();
		List result = stream.filter(x -> x.equals("a")).collect(Collectors.toList());
		System.out.println(result);
		
		
		List list = Arrays.asList(1,4,5,7,2,99,3);
		Stream stream1 = list.stream();
		// max获取集合中最大元素
		int i1 = stream1.max((o1,o2) -> o1-o2).get();
		System.out.println(i1);
		// 最小值
		stream1 = list.stream();
		i1 = stream1.min((o1,o2) -> o1-o2).get();
		System.out.println(i1);
		// anyMatch 是否包含
		stream1 = list.stream();
		boolean flag = stream1.anyMatch(x -> x==5);
		System.out.println(flag);
		
		
	}
}

你可能感兴趣的:(eclipse,java)