java8 lambda表达式与集合类批处理操作

一、基本概念

    λ表达式可以被当做是一个Object。λ表达式的类型,叫做“目标类型(target type)”。λ表达式的目标类型是“函数接口(functional interface)”,这是Java8新引入的概念。它的定义是:一个接口,如果只有一个显式声明的抽象方法,那么它就是一个函数接口。一般用@FunctionalInterface标注出来(也可以不标)。

@FunctionalInterface
public interface Runnable { void run(); }

public interface Callable<V> { V call() throws Exception; }

public interface ActionListener { void actionPerformed(ActionEvent e); }

public interface Comparator<T> { int compare(T o1, T o2); boolean equals(Object obj); }

    注意最后这个Comparator接口。它里面声明了两个方法,貌似不符合函数接口的定义,但它的确是函数接口。这是因为equals方法是Object的,所有的接口都会声明Object的public方法——虽然大多是隐式的。所以,Comparator显式的声明了equals不影响它依然是个函数接口 

    集合类的批处理操作API的目的是实现集合类的“内部迭代”,并期望充分利用现代多核CPU进行并行计算。
    Java8之前集合类的迭代(Iteration)都是外部的,即客户代码,不能充分利用cpu的多核资源。而内部迭代意味着改由Java类库来进行迭代,而不是客户代码.

    Java8为集合类引入了一个重要概念:流(stream)。一个流通常以一个集合类实例为其数据源,然后在其上定义各种操作。流的API设计使用了管道(pipelines)模式。对流的一次操作会返回另一个流。如同IO的API或者StringBuffer的append方法那样,从而多个不同的操作可以在一个语句里串起来.

二、λ表达式的使用

    一个λ表达式只有在转型成一个函数接口后才能被当做Object使用

    可以用一个λ表达式为一个函数接口赋值,然后再赋值给一个Object

    一个λ表达式可以有多个目标类型(函数接口),只要函数匹配成功即可。但需注意一个λ表达式必须至少有一个目标类型

    λ表达式主要用于替换以前广泛使用的内部匿名类,各种回调,比如事件响应器、传入Thread类的Runnable等

new Thread( () -> {
        System.out.println("This is from an anonymous method (lambda exp).");
    } );

    注意线程里的λ表达式,你并不需要显式地把它转成一个Runnable,因为Java能根据上下文自动推断出来:一个Thread的构造函数接受一个Runnable参数,而传入的λ表达式正好符合其run()函数,所以Java编译器推断它为Runnable。 

    filter方法的参数是Predicate类型,forEach方法的参数是Consumer类型,它们都是函数接口,所以可以使用λ表达式

三、代码样例

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Main {
	private static List<Person> persons = Arrays.asList(new Person("Joe", 12),
			new Person("Jim", 19), new Person("John", 21));
	public static void main(String[] args) throws Exception {
		// testStreamAPI();
		// testStreamMap();
		// testStreamPerformance();
		testInt(2, 3, 4, 2, 3, 5, 1);
		testOccurrence(2, 3, 4, 2, 3, 5, 1);
		distinctSum(2, 3, 4, 2, 3, 5, 1);
		testNestLambda();
	}

	public static void testStreamAPI() {
		// 打印年龄大于12的人
		System.out.println("使用顺序流串行打印");
		persons.stream().filter(p -> p.getAge() > 12)
				.collect(Collectors.toCollection(ArrayList::new))
				.forEach(p -> {
					System.out.println(p);
				});
		System.out.println("使用并行流并行打印,即利用多核技术可将大数据通过多核并行处理");
		persons.parallelStream().filter(p -> p.getAge() > 12)
				.collect(Collectors.toCollection(ArrayList::new))
				.forEach(p -> {
					System.out.println(p);
				});
	}

	public static void testStreamMap() {
		// 应该用filter过滤,然后再使用map进行转换
		persons.parallelStream().map(p -> {
			if (p.getAge() > 18)
				return p;
			return null;
		}).collect(Collectors.toCollection(ArrayList::new)).forEach(p -> {
			if (p != null)
				System.out.println(p);
		});

		persons.parallelStream().filter(p -> p.getAge() > 18)
				.map(p -> new Adult(p))
				.collect(Collectors.toCollection(ArrayList::new))
				.forEach(p -> {
					if (p != null)
						System.out.println(p);
				});
	}

	public static void testStreamReduce() {
		persons.parallelStream().filter(p -> p.getAge() > 18)
				.map(p -> new Adult(p))
				.collect(Collectors.toCollection(ArrayList::new))
				.forEach(p -> {
					if (p != null)
						System.out.println(p);
				});
	}

	public static void testStreamPerformance() {
		// 初始化一个范围100万整数流,求能被2整除的数字,toArray()是终点方法
		long start1 = System.nanoTime();
		int a[] = IntStream.range(0, 1_000_000).filter(p -> p % 2 == 0)
				.toArray();
		System.out.printf("测试顺序流的性能: %.2fs",
				(System.nanoTime() - start1) * 1e-9);

		long start2 = System.nanoTime();
		int b[] = IntStream.range(0, 1_000_000).parallel()
				.filter(p -> p % 2 == 0).toArray();
		System.out.printf(" 测试并行流的性能: %.2fs",
				(System.nanoTime() - start2) * 1e-9);
		// 本机的测试结果是:测试顺序流的性能: 0.02s 测试并行流的性能: 0.01s
		// 在100万时,并发流快些,1000万,并发流反而会慢些,估计和线程的频繁切换有关(本机是8线程CPU)
	}

	public static void testInt(Integer... numbers) {
		List<Integer> l = Arrays.asList(numbers);
		List<Integer> r = l.stream()
				.map(e -> new Integer(e))
				.filter(e -> e > 2)
				.distinct()
				.collect(Collectors.toList());
		System.out.println("testInt result is: " + r);
	}

	public static void testOccurrence(Integer... numbers) {
		List<Integer> l = Arrays.asList(numbers);
		Map<Integer, Integer> r = l
				.stream()
				.map(e -> new Integer(e))
				.collect(
						Collectors.groupingBy(p -> p,
								Collectors.summingInt(p -> 1)));
		System.out.println("testOccurrence result is: " + r);
	}

	public static void distinctSum(Integer... numbers) {
		List<Integer> l = Arrays.asList(numbers);
		int sum = l.stream()
				.map(e -> new Integer(e))
				.distinct()
				.reduce(0, (x, y) -> x + y); // equivalent to .sum()
		System.out.println("distinctSum result is: " + sum);
	}

	public static void testNestLambda() throws Exception{
		 Callable<Runnable> c1 = () -> () -> { System.out.println("Nested lambda"); };
		 c1.call().run();
		// 用在条件表达式中
		Callable<Integer> c2 = false ? (() -> 42) : (() -> 24);
		System.out.println(c2.call());
	}
}

class Person {
	private String name;
	private int age;

	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getName() {
		System.out.println(name);
		return name;
	}

	public int getAge() {
		return age;
	}

	@Override
	public String toString() {
		return "name:" + name + " age:" + age;
	}
}

class Adult extends Person {
	public Adult(Person p) {
		super(p.getName(), p.getAge());
	}
}
时间: 2024-10-27 13:31:32

java8 lambda表达式与集合类批处理操作的相关文章

Java8 Lambda表达式教程

转自:http://blog.csdn.net/ioriogami/article/details/12782141   1. 什么是λ表达式 λ表达式本质上是一个匿名方法.让我们来看下面这个例子:     public int add(int x, int y) {        return x + y;    } 转成λ表达式后是这个样子:        (int x, int y) -> x + y; 参数类型也可以省略,Java编译器会根据上下文推断出来:     (x, y) ->

Java8 Lambda表达式详解及实例_java

第一个Lambda表达式 在Lambda出现之前,如果我们需要写一个多线程可能需要下面这种方式: Runnable runnable = new Runnable() { @Override public void run() { System.out.println("Hello runnable"); } }; ... thread.start(); 上面的例子如果改成使用Lambda就会简单许多: Runnable noArgs = ()->System.out.print

Java8 Lambda表达式之比较器

在这个例子中,我将向你展示如何使用Java8的lambda表达式写的比较器排序列表. 经典Comparator例子 Comparator<Developer> byName = new Comparator<Developer>() { @Override public int compare(Developer o1, Developer o2) { return o1.getName().compareTo(o2.getName()); } }; Lambda表达式方式 Com

java8: lambda表达式的参数

lambda表达式由三个部分组成: 一个括号,包括用逗号分隔的参数列表,如果只有一个参数,你可以省略括号.另外你可以省略参数的类型 一个箭头符号: ->  方法体:可以是表达式和代码块(即函数式接口里面方法的实现),如果是代码块,则必须用{}来包裹起来.注意若函数式接口里面方法返回值是void,则无需{} 下面的代码样例说明在使用lambda表达式时如何传递一个或多个参数. public class Main { public static void main(String[] args) {

Java SE 8: Lambda表达式

Java SE 8在6月13的版本中已经完全了全部的功能.在这些新的功能中,lambda表达式是推动该版本发布 的最重要新特性.因为Java第一次尝试引入函数式编程的相关内容.社区对于lambda表达式也期待已久. Lambda表达式的相关内容在JSR 335中定义,本文的内容基于最新的规范和JDK 8 Build b94. 开发环境使用 的是Eclipse. Lambda表达式 要理解lambda表达式,首先要了解的是函数式接口(functional interface).简单来说,函数式接口

java中lambda表达式语法说明_java

语法说明 一个lambda表达式由如下几个部分组成: 1. 在圆括号中以逗号分隔的形参列表.在CheckPerson.test方法中包含一个参数p,代表了一个Person类的实例.注意:lambda表达式中的参数的类型是可以省略的:此外,如果只有一个参数的话连括号也是可以省略的.比如上一节曾提到的代码: p -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25

java中lambda表达式简单用例_java

我对java中lambda表达式的看法是相当纠结的: 一个我这么想:lambda表达式降低了java程序的阅读体验.java程序一直不以表现力出众,正相反使Java流行的一个因素正是它的安全和保守--即使是初学者只要注意些也能写出健壮且容易维护的代码来.lambda表达式对开发人员的要求相对来说高了一层,因此也增加了一些维护难度. 另一个我这么想:作为一个码代码的,有必要学习并接受语言的新特性.如果只是因为它的阅读体验差就放弃它在表现力方面的长处,那么即使是三目表达式也有人觉得理解起来困难呢.语

Java Lambda 表达式详解及示例代码_java

Java Lambda 表达式是 Java 8 引入的一个新的功能,可以说是模拟函数式编程的一个语法糖,类似于 Javascript 中的闭包,但又有些不同,主要目的是提供一个函数化的语法来简化我们的编码. Lambda 基本语法 Lambda 的基本结构为 (arguments) -> body,有如下几种情况: 参数类型可推导时,不需要指定类型,如 (a) -> System.out.println(a) 当只有一个参数且类型可推导时,不强制写 (), 如 a -> System.o

快速入门Java中的Lambda表达式_java

Lambda简介 Lambda表达式是Java SE 8中一个重要的新特性.lambda表达式允许你通过表达式来代替功能接口. lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块). Lambda表达式还增强了集合库. Java SE 8添加了2个对集合数据进行批量操作的包: java.util.function 包以及 java.util.stream 包. 流(stream)就如同迭代器(iterator),但附加了许多