java8特性
Java8发布了挺久了,而且有很大的变动。这里是一篇迟来的部分Java8新特性的总结。
接口
接口提供默认方法的实现,非static方法前必须有default
关键字。
default void print(Object a){
...
}
从这个角度来说,Java的接口更加接近Scala语法中的trait了。
lambda
lambda表达式也不是非常新的东西,在Python中早就引入过。现在Java引入lamda之后,很多代码都能够简化了。
之前在算法题中经常需要写比较Comparator的实现,现在可以直接写一个lambda就行了:
Arrays.sort(students,new Comparator<Student>(){
@Override
public int compare(Student s1, Student s2) {
return s1.getName().compareTo(s2.getName());
}
});
//用lambda可以写为:
Arrays.sort(students,(a,b)->(a.getName().compareTo(b.getName());
lambda的作用域和匿名方法相同,可以访问:
- 外面的final本地变量(未显式声明为final的,会被隐式当做final来处理,在IDEA编辑器中会红色显式)。
- 所有的对象实例变量和类变量,并且可以修改它们
- 不可以访问接口的默认方法
函数式接口
函数式接口是只包含了一个抽象方法的接口(默认方法个数无限制)。
函数式接口的声明和使用
声明函数式接口可以用@FunctionalInterface
注解,也可以不用:
@FunctionalInterface
public interface Comparator<T> {
}
使用函数式接口,通过把函数赋值给接口对象,实现把函数当做对象用:
Comparator comp=(a,b)->a.val.compareTo(b.val);
res=comp.compare(student1,student2);
对已有方法、构造函数的引用:
conv=Integer::valueOf;//static函数
conv=std::startWith;//对象函数
//构造函数P::new与工厂
interface PFactory<P>{
P create(String s1,String s2);
}
PFactory pf=P::new;
P p1=pf.create("a","b");//调用P的构造函数中有两个String的那个
//P p2=pf.create();假如工厂中的create是无参函数,那么调用这个能自动去调用P中所有构造函数中无参的那个
Java8中常用的函数式接口
Predicate
Predicate<参数>:判断指定类型的参数是否满足某种条件,返回bool。支持实例方法的与或非操作和静态方法的相等、取非操作。
p1=String::isEmpty
p2=p1.negative();
p3=p1.and(p5);
p4=p2.or(p5);
p7=Predicate.isEqual(p3,p4);
p8=Predicate.not(p4);
抽象方法为test,还有一些其他默认方法,其实现如下:
@FunctionalInterface
public interface Predicate<T> {
boolean test(T var1);
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> {
return this.test(t) && other.test(t);
};
}
default Predicate<T> negate() {
return (t) -> {
return !this.test(t);
};
}
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> {
return this.test(t) || other.test(t);
};
}
static <T> Predicate<T> isEqual(Object targetRef) {
return null == targetRef ? Objects::isNull : (object) -> {
return targetRef.equals(object);
};
}
static <T> Predicate<T> not(Predicate<? super T> target) {
Objects.requireNonNull(target);
return target.negate();
}
Function、BiFunction
Function<参数,返回>:通过指定类型的参数对象返回另一个指定类型的对象
Function的抽象方法为apply,把T类型转化为R类型返回。另外的compose
和andThen
是提供把两个Function组合起来。compose是从后往前,andThen是从前往后。例子如下:
Function<Integer, Integer> times2 = i -> i*2;
Function<Integer, Integer> squared = i -> i*i;
System.out.println(times2.apply(4)); //2*4=8
System.out.println(squared.apply(4));//4*4=16
System.out.println(times2.compose(squared).apply(4)); //4*4=16,16*2=32
System.out.println(times2.andThen(squared).apply(4)); //4*2=8,8*8=64
Function的实现如下:
@FunctionalInterface
public interface Function<T, R> {
R apply(T var1);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {//先执行里面的apply,再执行自己的apply
Objects.requireNonNull(before);
return (v) -> {
return this.apply(before.apply(v));
};
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {//先执行自己的apply,再执行里面的apply
Objects.requireNonNull(after);
return (t) -> {
return after.apply(this.apply(t));
};
}
static <T> Function<T, T> identity() {
return (t) -> {
return t;
};
}
BiFunction<参数,参数,返回>:通过指定类型的两个参数对象返回另一个指定类型的对象。和Function的差别在于输入参数的个数多了一个。
它只有一个默认方法andThen。它的参数是一个Function,先执行BiFunction,然后执行这个参数Function。
BiFunction<Integer,Integer,Integer> add=(a,b)->a+b;
Function<Integer, Integer> times2 = i -> i*2;
System.out.println(add.andThen(times2).apply(2,4)); //2+4=6,2*6=12
实现如下:
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T var1, U var2);
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (t, u) -> {
return after.apply(this.apply(t, u));
};
}
}
Supplier、Consumer
Supplier<返回>:生产者:生成实例,用get获取
Consumer<参数>:消费者:消费实例,用accept消费
使用:
Supplier<Student> ss=Student::new;
s1=ss.get();
Consumer<Student> cs=(a1)->System.out.println(a1.name));
s1.accept();
//Consumer有一个andThen(Consumer),从前往后消费指定对象
cs1.andThen((a2)->System.out.println(a1.age)).apply(s1);
实现如下:
@FunctionalInterface
public interface Supplier<T> {
T get();
}
@FunctionalInterface
public interface Consumer<T> {
void accept(T var1);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (t) -> {
this.accept(t);
after.accept(t);
};
}
}
Comparator
Comparator<参数>:比较两个指定类型的对象,返回int
Comparator的抽象方法是compare,还支持reversed获取逆序对应的Comparator。
Comparator<Integer> intCmp=(a,b)->a-b;
Comparator<Integer> intCmpRev=intCmp.reversed();
Optional
Optional<参数>:可以看做容器,装null或者指定类型的对象。
流
流和函数式接口一起使用。用来对集合进行操作。
容器类和流的转化
把Collection容器类(List、Set,注意Map不是Collection)转为stream有两种方式,其中并发是用多线程实现:
list.stream();
list.parallelStream()
它们的实现是在Collection类中:
default Stream<E> stream() {
return StreamSupport.stream(this.spliterator(), false);
}
default Stream<E> parallelStream() {
return StreamSupport.stream(this.spliterator(), true);
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, 0);//产生Spliterator的实现类IteratorSpliterator(Spliterators工具类的内部类)
}
}
关于Stream、Spliterator的实现稍微有点多,需要单独开一篇博客讲吧。
把流转化会普通集合是用collect:
.collect(Collectors.toList());
.collect(Collectors.toSet());
//如果Collectors里面没有提供实现,则用三个参数的方法
.collect(() -> new HashSet<>(),
(set,elem)->set.add(elem),
(setA,setB)->setA.addAll(setB)
.collect(HashSet::new,
HashSet::add,
HashSet::addAll
)
流对基本类型的数组和Iterable类的支持:
int[] arr={1,3,2,1};
Arrays.stream(arr)//int数组也可以产生流
//并不是支持所有Iterable类
SerialException serialException=new SerialException();
serialException.stream();//错误,没有stream方法
流的转化
流的特性和Spark的rdd转化过程很像。
- 在流的转化过程中,是不会修改原来的流,而是产生新的流。
- 惰性执行,map等中间操作不会触发执行,只有最终操作才会触发执行。使用了最终操作后,流就被消费掉了,就不能再接着串联流的操作。(类似Scala中的action算子)
Stream类支持这些函数,并且很多函数返回新的Stream,使得这些函数能够串联执行。
中间操作:
filter(Predicate):产生的新的流只包含满足指定条件的元素
sorted()/sorted(Comparator)
match(Predicate)
map/flatMap(Function):另外还有mapToInt,mapToLong,mapToDouble这三种操作及对应的flatMap操作(flatMapToInt等),把流转化成指定类型的流。flatMap是针对元素映射成集合或者流的时候,结果会所有集合的集合压平成一个流。
最终操作:
count()
limit(k)
forEach(Consumer)
max/min(Comparator):产生最大值和最小值,返回Optional类型。
reduce(BiFunction)/reduce(初始值,BiFunction累加器)/reduce(初始值,BiFunction,BiOperator combiner):指把集合中所有元素规约生成一个元素。max和min是一种特殊的reduce操作。带combiner的reduce操作和parallelStream结合,每个线程内部调用累加器Accumulator,得到结果后线程之间汇总用combiner得到最终结果。
reduce的三种形式:
- 未定义初始值,则第一次执行的时候第一个参数的值是Stream的第一个元素,第二个参数是Stream的第二个元素
- 定义了初始值,则第一次执行的时候第一个参数的值是初始值,第二个参数是Stream的第一个元素
- 定义了初始值和combiner,则第三个参数只会作用于parallelStream。
使用流的例子:
Stream<Integer> stream = lists.stream();
Optional<Integer> min = stream.min(Integer::compareTo);//最小值,可以指定比较器Comparator
if (min.isPresent()) {
System.out.println(min.get());
}
lists.stream().max(Integer::compareTo).ifPresent(System.out::println);
Optional<Integer> sum = lists.stream().reduce((a, b) -> a + b);//reduce求和
Integer sum2 = lists.stream().reduce(0, (a, b) -> a + b);//指定初始值的reduce
Integer product = lists.parallelStream().reduce(1, (a, b) -> a * (b * 2),(a, b) -> a * b);//
lists.stream().sorted().forEach(elem -> System.out.print(elem + " "));
lists.stream()
.filter(elem -> elem > 3)
.forEach(elem -> System.out.print(elem + " "));
citys.stream().flatMap(mCities->Arrays.stream(mCities.split(" "))).forEach(System.out::println);```