一、默认方法
默认方法:接口提供默认的实现方法,可以不需要实现类去实现。
-
为什么要有这个特性?
在给接口添加新方法时,会影响所有实现该接口的实现类,如果提供了默认方法,如无必要,就不需要在实现类中去实现该方法。
-
语法
public interface Vehicle {
// 默认接口方法
default void print() {
System.out.println("一辆车");
}
}
-
子类调用默认方法
接口名.super.默认方法名()
public interface Vehicle {
default void print(){
System.out.println("我是一辆车!");
}
}
public interface FourWheeler {
default void print(){
System.out.println("我是一辆四轮车!");
}
}
// 子类可以使用接口中定义的默认方法
public class Car implements Vehicle, FourWheeler {
public void print(){
Vehicle.super.print();
}
}
- 静态默认方法
public interface Vehicle {
// 静态默认方法, 通过接口名直接调用
static void warning(){
System.out.println("按喇叭!");
}
}
二、Supplier 和 Consumer
两个函数是接口,可以看成是一对逆运算。一个作为生产工厂生产对象(Supplier),另一个作为消费者去消费对象(Consumer)。
-
Supplier接口
接口就一个抽象方法get方法,不用传入任何参数,直接返回一个泛型T的实例。
@FunctionalInterface
public interface Supplier<T> {
T get();
}
- Consumer接口
- accept() 抽象方法,接收一个参数,没有返回值。
- andThen() 在执行完调用者方法后再执行传入参数的方法。
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
- 实例
public static void main(String[] args) {
Consumer<Integer> consumer = (x) -> {
int num = x * 2;
System.out.println(num);
};
Consumer<Integer> consumer1 = (x) -> {
int num = x * 3;
System.out.println(num);
};
consumer.andThen(consumer1).accept(10);
}
// Result
20
30
三、方法引用
通过方法的名称来指向一个方法,方法引用使用一对冒号 :: 。
- 示例
class Car {
public static Car create(final Supplier<Car> supplier) {
return supplier.get();
}
public static void collide(final Car car) {
System.out.println("Collided " + car.toString());
}
public void follow(final Car another) {
System.out.println("Following the " + another.toString());
}
public void repair() {
System.out.println("Repaired " + this.toString());
}
}
-
构造方法引用
语法:Class::new 或者 Class< T >::new
final Car car = Car.create( Car::new );
final List<Car> cars = Arrays.asList( car );
-
静态方法引用
语法:Class::static_method
cars.forEach( Car::collide );
-
特定类的任意对象的方法引用
语法:Class::method
cars.forEach( Car::repair );
-
特定对象的方法引用
语法: instance::method
final Car police = Car.create( Car::new );
cars.forEach( police::follow );
四、 Optional 类
1. 一个可以为null的容器对象,如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
2. Optional 类的引入很好的解决空指针异常。
- 常用方法
方法 | 描述 |
---|---|
T get() | 如果包含值返回值,否则抛出:NoSuchElementException |
void ifPresent(Consumer<? super T> consumer) | 如果值存在则使用该值调用 consumer , 否则不做任何事情 |
boolean isPresent() | 如果值存在则方法会返回true,否则返回 false。 |
static<T> Optional<T> of(T value) | 返回一个指定非null值的Optional |
T orElse(T other) | 如果存在该值,返回值, 否则返回 other |
五、Stream
1、定义
A sequence of elements supporting sequential and parallel aggregate operations.
可以把Stream当成一个高级版本的Iterator。原始的 Iterator,用户只能一个一个的遍历元素并对其执行某些操作;高级的 Stream,用户只要给出需要对其包含的元素执行什么操作,具体这些操作如何应用到每个元素上,就给Stream就好!
- 语法
// 创建 转换 聚合
list.stream() .filter(num -> num != null) .count();
2、创建Stream
- 静态工厂创建
- of() 方法
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4); // 可变参数
Stream<String> stringStream = Stream.of("Hello"); // 单一参数
- generator() 方法
// 原型
public static<T> Stream<T> generate(Supplier<T> s);
// 三种使用方式
// 1. 匿名函数
Stream.generate(new Supplier<Double>() {
@Override
public Double get() {
return Math.random();
}
});
// 2. lambda表达式
Stream.generate(() -> Math.random());
// 3. 方法引用
Stream.generate(Math::random);
- iterate() 方法
// 原型 -- 元素的生成是重复对给定的种子值(seed)调用用户指定函数来生成的。
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f);
// 示例 -- 获取无限长的正数集,并打印前5个(注意 limit限制长度)
Stream.iterate(1, item -> item + 1).limit(5).forEach(System.out::println);
-
Collection子类获取Stream
Collection的子类可以通过顶层接口 Collection 接口中 实现的默认方法 stream() 来获取。
// Collection 默认方法stream
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
3、转换Stream
把一个Stream通过某些行为转换成一个新的Stream,在 Stream 接口中定义了许多的转换方法,常见的如下。
-
distinct()
流中元素去重
-
filter()
过滤符合条件的元素
-
map()
元素映射转换,类似的有 mapToInt,mapToLong 和 mapToDouble
-
flatMap()
把子Stream中的元素压缩到父集合中
-
peek()
生成一个包含原Stream的所有元素的新Stream,同时会提供一个消费函数(Consumer实例),新Stream每个元素被消费的时候都会执行给定的消费函数
-
limit()
截断操作,获取其前N个元素
-
skip()
丢弃前N个元素
-
sorted()
对流进行排序
Random random = new Random();
random.ints().limit(10).sorted().forEach(System.out::println);
- 示例
List<Integer> nums = Arrays.asList(1,1,null,2,3,4,null,5,6,7,8,9,10);
System.out.println("sum is:" + nums.stream()
.filter(num -> num != null)
.distinct()
.mapToInt(num -> num * 2)
.peek(System.out::print)
.skip(2)
.limit(4)
.sum()
);
// Result
24681012sum is:36
-
效率问题
转换操作都是lazy的,所有的转换都是一次循环完成。
4、汇聚Stream
-
可变汇聚
把输入的元素们累积到一个可变的容器中,如Collection或者StringBuilder
- collect() 方法
// 原型
<R> R collect(Supplier<R> supplier, // 1. 工厂函数,用来生成一个新的容器
BiConsumer<R, ? super T> accumulator, // 2. 函数,把元素添加到结果容器中
BiConsumer<R, R> combiner); // 3. 函数,把中间状态的多个结果容器合并成为一个(并发的时候会用到)
// 示例
List<Integer> nums = Arrays.asList(1,1,null,2,3,4,null,5,6,7,8,9,10);
List<Integer> numsWithoutNull = nums.stream()
.filter(num -> num != null)
.collect(() -> new ArrayList<Integer>(), // 1
(list, item) -> list.add(item), // 2
(list1, list2) -> list1.addAll(list2)); // 3
- 生成一个新的ArrayList实例
- 接受两个参数,第一个是前面生成的ArrayList对象,二个是stream中包含的元素,函数体就是把stream中的元素加入ArrayList对象中。第二个函数被反复调用直到原stream的元素被消费完毕;
- 接受两个参数,这两个都是ArrayList类型的,函数体就是把第二个ArrayList全部加入到第一个中(并发的时候会用到)。
**简化**
使用 Collector 工具类对上述汇聚函数进行简化。
// 原型
<R, A> R collect(Collector<? super T, A, R> collector);
// 上个例子的简化版
List<Integer> numsWithoutNull = nums.stream().
filter(num -> num != null)
.collect(Collectors.toList());
- 其他汇聚
-
reduce() 方法
接受的函数有两个参数,第一个参数是上次函数执行的返回值(也称为中间结果),第二个参数是stream中的元素,这个函数把这两个值相加,得到的和会被赋值给下次执行这个函数的第一个参数。
// 原型
Optional<T> reduce(BinaryOperator<T> accumulator);
T reduce(T identity, BinaryOperator<T> accumulator);
// 示例1 -- 没有初值,第一次执行时第一个参数是Stream的第一个元素,第二个参数是Stream的第二个元素
List<Integer> ints = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
System.out.println("ints sum is:" + ints.stream()
.reduce((sum, item) ->sum + item)
.get()); // Optional类型,get()获取值
// 示例2 -- 有初值,第一次执行时第一个参数的值,第二个参数是Stream的第一个元素
List<Integer> ints = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
System.out.println("ints sum is:" + ints.stream()
.reduce(0, (sum, item) -> sum + item));
-
count() 方法
获取Stream中元素的个数
List<Integer> ints = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
System.out.println("ints sum is:" + ints.stream().count());
- 统计
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = numbers.stream()
.mapToInt((x) -> x).summaryStatistics();
System.out.println("列表中最大的数 : " + stats.getMax());
System.out.println("列表中最小的数 : " + stats.getMin());
System.out.println("所有数之和 : " + stats.getSum());
System.out.println("平均数 : " + stats.getAverage());
-
更多处理
-
参考