Java8手记

Java8手记

一、Lambda表达式

1.1 Lambda表达式是什么?

Lambda表达式是Java8的最大特色。简单的Lambda表达式可以清晰的表达很多复杂的编程目的。Lambda表达式类似于如下表示格式:x->x+1。

其中x是一个没有参数类型的参数名,x+1为表达式,这里也可以用大括号括起来的代码块代替。他们之间使用->右箭头连接。

1.2 Lambda表达式可以做什么?

了解Lambda表达式可以做什么,也就是告诉了我们为什么要去学习Lambda表达式的原因。

其一,使用Lambda表达式,可以更清晰的表示程序意图。通常此类例子我们都以swing开发中的ActionListener接口的actionPerform方法作为说明。此处我们换用Runnable方法的run进行说明。如:

Runnable noArgs = () -> System.out.println(“无参调用”);

也许此处,您会觉得使用匿名函数同样可以达到效果,的确。对于简单的表达式来说,同种功能的匿名函数结果一致,阅读起来也不太费劲。但是细心的您能发现,这里表达式直接可以指出功能核心的实现,而匿名函数需要一堆“格式”,并且需要找到其中的功能核心部分。这点,就突出了Lambda表达式的优势。如:

Runnable noArgs = new Runnable(){
         public void run()
         {
                  System.out.println(“无参调用”);
         }
};

其二,使用Lambda表达式,可以使用Java8新引入的流操作,而告别以往需要多个for循环迭代的低效模式。如:针对于迭代寻找一个学生集合中的男生数量。我们先定义一个简单的学生类Student。这个类具有一个整形的sexual成员变量,0表示男性,1表示女性。不使用流操作的时候,我们通常这么写:

List<Student> students = getData();      //假设这里getData()方法用于获取学生集合
int count = 0;
for(Student stu:students)
{
         if(stu.getSexual() == 0)
         {
                  count ++;
         }
}
System.out.println("班里共有"+count+"个男生。");

而使用流操作时,通常只用几步就可以筛选出结果。如下:

List<Student> students = getData();      //假设这里getData()方法用于获取学生集合
int count = students.stream()->filter(stu -> stu.getSexual() == 0).count();
System.out.println("班里共有"+count+"个男生。");

这里可以看到,使用流操作不仅简明易懂,而且减少了代码量。不仅如此,对于流的内部实现来说,在调用count()方法前,实际上还没有开始内部迭代。当调用count()方法后,流才开始迭代,并且仅迭代一次。单独一层的循环可能无法突显出这种优势,当多层循环嵌套时,就可以明显看出差异了。

二、书写Lambda表达式

Lambda表达式可以直接书写,形如:

Runnable noArguments = ()->System.out.println();

也可以带有一个参数:

ActionListener oneArguments = event -> System.out.println("Button clicked");

也可以带有一个代码块:

Runnable multiStatement = () -> {
                  System.out.println(1);
                  System.out.println(2);
};

也可以返回一个参数:

BinaryOperator<Long> add = (x,y) -> x + y;

也可以显示指明参数类型:

BinaryOperator<Long> addExplicit = (Long x,Long y) -> x + y;

三、流操作

3.1 基本操作

流操作相当于将数据集合放入一个流中,根据流的一些条件过滤或者映射为不同的内容,最后迭代出结果。其中,流操作的方法有两类,一类为惰性求值方法,一类为及早求值方法

其中惰性求值可以理解为,在流中加入的一些限制条件,这些限制条件并不引发流的迭代求值操作。及早求值可以理解为,将流启动,引发流的迭代操作并获得流的最终结果。

下表中归纳了流操作的常用方法。

方法签名

类型

用法示例

特点

Stream<T> filter(Predicate<? super T> predicate) 

筛选出符合条件的流。

惰性

示例3.1

筛选

Stream<T> distinct() 

过滤掉重复元素后的流。

惰性

示例3.2

筛选

Stream<T> limit(long maxSize) 

将流按照最大长度截断。

惰性

示例3.3

筛选

Stream<T> skip(long n)

返回扔掉前n个元素后的流。

惰性

示例3.4

筛选

<R> Stream<R> map(Function<? super T,? extends R> mapper) 

将原流中的元素按照某种方法(mapper)映射为新的流。流中的原内容会根据mapper映射为新的内容。

惰性

示例3.5

映射

<R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper) 

如果源流中某一个变量含有多个元素(数组、列表等),则这个方法会将源流映射为含有这种多个元素项的合并集合。

惰性

示例3.6

映射

Stream<T> sorted() 

返回按照自然顺序排序后的流。

惰性

示例3.7

排序

boolean anyMatch(Predicate<? super T> predicate) 

查找流中是否至少包含一个符合条件的元素。

及早

示例3.8

查找

boolean allMatch(Predicate<? super T> predicate) 

查找流中所有元素是否都匹配某条件。

及早

示例3.9

查找

boolean noneMatch(Predicate<? super T> predicate) 

查找流中无元素与条件相匹配。

及早

示例3.10

查找

Optional<T> findAny() 

返回流中的元素。

及早

示例3.11

查找

Optional<T> findFirst() 

返回流中第一个元素。

及早

示例3.12

查找

T reduce(T identity, BinaryOperator<T> accumulator) 

Optional<T> reduce(BinaryOperator<T> accumulator) 

<U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner) 

此方法用于进行迭代求值,并返回迭代结果。共有3中重载方法。

及早

示例3.13

归约

<R,A> R collect(Collector<? super T,A,R> collector) 

把流归约成一个集合。

及早

示例3.14

归约

Optional<T> max(Comparator<? super T> comparator) 

查找出流中的最大的元素。

及早

示例3.15

归约

Optional<T> min(Comparator<? super T> comparator) 

查找出流中最小的元素。

及早

示例3.16

归约

long count() 

返回流中元素的数量和。

及早

示例3.17

归约

void forEach(Consumer<? super T> action) 

遍历流中的元素,并做出针对于每个元素的具体操作。

及早

示例3.18

遍历

3.2 Optional类

在操作列表中,涉及到了一个Optional类。因为null关键字带来的诸多问题,在Java8中使用了一个Optional类。这个类用于优雅的代替null。

使用工厂方法of,可以从某个值创建出一个Optional对象。

使用工厂方法empty,可以创建一个空的Optional对象。

使用工厂方法ofNullable,可以创建一个允许为空的Optional对象。

可以通过isPresent()方法判断值是否为空。

使用get()方法获取Optional对象里的值。

为了更加简洁,通常在使用时,使用orElse或者orElseGet方法。当Optional对象没有值时,会提供一个备选值。

3.3 收集器

收集器用作一种高级归约,将流中的数据转换为需要的的数据集合类型。下面列出了所有收集器:

工厂方法

返回类型

用于

toList

List<T>

把流中所有的项目收集到一个List中。

toSet

Set<T>

把流中所有的项目收集到一个Set中,删除重复项。

toCollection

Collection<T>

把流中所有的项目收集到指定的供应源创建的集合。

counting

Long

计算流中元素的个数。

summingInt

Integer

对流中项目的一个整数属性求和。

averagingInt

Double

计算流中项目具有Integer属性的平均值。

summarizingInt

IntSummaryStatistics

收集关于流中项目Integer属性的统计值。如最大、最小、总和与平均值。

joining

String

连接对流中每个项目调用toString()方法生成的字符串。

maxBy

Optional<T>

返回流中按照给定比较器选出的最大元素Optional。

minBy

Optional<T>

返回流中按照给定比较器选出的最小元素Optional。

reducing

归约类型

从一个做为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而将流归约为单个值。

collectingAndThen

转换函数返回类型

包裹另一个收集器,对其结果应用转换函数。

groupingBy

Map<K,List<T>>

根据项目一个属性的值对流中的项目分组,并将属性值作为结果Map的键。

partitioningBy

Map<Boolean, List<T>>

根据对流中每个项目应用谓词的结果来对项目进行分区。

mapping

映射函数返回类型

将收集器中的元素进行映射

四、默认方法

默认方法是在接口中,用default关键字标识的一种方法。如:

public interface Readable
{
    public default void sing()
    {
        System.out.println("Start!");
    }
}

接口的默认方法会带来多重继承等问题,因此这里记录下使用原则:

  • 类胜于接口。
  • 子类胜于父类。
  • 其他。(解决方法:要么子类实现该方法;要么将该方法声明为抽象方法)

五、并行化

Java8中的并行化仅仅是对一个Stream对象,调用其parallel方法或集合创建流时,调用parallelStream代替stream方法即可。

虽然用法很简单,但是决定何时用并行化何时用串行化并不简单。并行化并不一定会提升效率。在处理大量(几万以上)数据时,用并行化也许会提升计算效率。否则一般来说串行化的计算效率是高于并行化的。并且并行化的效率提升有一个限值,根据阿姆达尔定律,并行化最大能提升到2倍速度。

同学们可以下载本手记附属的代码,依次运行,获得更深刻的体会。点我下载示例代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值