java8特性

Posted Fanny123

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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的作用域和匿名方法相同,可以访问:

  1. 外面的final本地变量(未显式声明为final的,会被隐式当做final来处理,在IDEA编辑器中会红色显式)。
  2. 所有的对象实例变量和类变量,并且可以修改它们
  3. 不可以访问接口的默认方法

函数式接口

函数式接口是只包含了一个抽象方法的接口(默认方法个数无限制)。

函数式接口的声明和使用

声明函数式接口可以用@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类型返回。另外的composeandThen是提供把两个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转化过程很像。

  1. 在流的转化过程中,是不会修改原来的流,而是产生新的流。
  2. 惰性执行,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的三种形式:

  1. 未定义初始值,则第一次执行的时候第一个参数的值是Stream的第一个元素,第二个参数是Stream的第二个元素
  2. 定义了初始值,则第一次执行的时候第一个参数的值是初始值,第二个参数是Stream的第一个元素
  3. 定义了初始值和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);```

以上是关于java8特性的主要内容,如果未能解决你的问题,请参考以下文章

利用Idea重构功能及Java8语法特性——优化深层嵌套代码

java8新特性——Lambda表达式

2020了你还不会Java8新特性?Java 8新特性介绍

Java8新特性

Java8新特性

Java8常用新特性实践