资讯详情

7.8 W 字总结!Java 8—Java 10 特性详解

Java现在发布的版本很快,一年两个,但三年一个真的会大规模使用。TLS版本。

  • 每三年发布一次TLS,长期维护版本。Java 8 ,Java 11, Java 17 可大规模使用。

  • 3月和9月每年发布两个正式版本。

Java 8 特性详解

知识体系

函数编程

在Java在世界上,面向对象或主流思想,对于习惯面向对象编程的开发者来说,抽象的概念并不陌生。面向对象编程是对数据的抽象,而函数编程是对行为的抽象。在现实世界中,数据和行为并存,程序也是如此,所以我们必须学习这两种编程方法。

这种新的抽象方法还有其他好处。许多人并不总是优先编写性能代码。对这些人来说,函数编程的好处尤为明显。程序员可以编写更容易阅读的代码——它更多地表达业务逻辑,而不是如何实现机制。易读代码也易于维护,更可靠,更不容易出错。

编写回调函数和事件处理器时,程序员不必纠缠于匿名内部类别的复杂性和可读性。函数编程使事件处理系统更容易。方便地传输函数也使编写惰性代码更容易。只有在真正需要时才能初始化变量值。

对象编程是对数据的抽象;函数编程是对行为的抽象。

核心思想: 使用不可变值和函数,函数对一个值进行处理,映射成另一个值。

核心类库的改进主要包括集合类API新引入的流量Stream。流程使程序员能够在更高的抽象层次上操作集合。

lambda表达式

  • lambda以下代码只能放入表达式: 使用了预定义 @Functional 注释的函数接口带有抽象函数的方法,或SAM(Single Abstract Method 单抽象方法)类型。这些称为lambda表达式目标类型可用作返回类型或lambda参数的目标代码。例如,如果一种方法被接收Runnable、Comparable或者 Callable 接口有单个抽象方法,可以引入lambda表达式。类似地,如果一种方法接受声明 java.util.function 例如,包中的接口 Predicate、Function、Consumer 或 Supplier,然后可以传给它lambda表达式。

  • lambda可用于表达式方法引用,不修改方法lambda表达式提供的参数。lambda表达式可以换成方法引用,因为它只是一种简单的方法,参数相同。

list.forEach(n->System.out.println(n)); list.forEach(System.out::println);//引用使用方法 

但是,如果对参数进行任何修改,则不能引用该方法,而需要完全输入lambda表达式如下:

list.forEach((Strings)->System.out.println("*" s "*")); 

事实上,这里可以省略lambda编译器可以从列表的类属性中推测参数的类型声明。

  • lambda静态、非静态和局部变量可用于内部,称为lambda内部变量捕获。

  • Lambda表达式在Java也叫闭包或匿名函数,所以如果有同事叫它闭包,不要惊讶。

  • Lambda该方法在编译器内翻译成私有方法,并分发 invokedynamic 调用字节码指令。可以使用JDK中的 javap 反编译工具class文件。使用 javap -p 或 javap -c -v 看命令lambda表达式生成的字节码。大致应该是这样的:

privatestaticjava.lang.Objectlambda$0(java.lang.String); 
  • lambda表达式有一个限制,只能引用 final 或 final 局部变量,也就是说不能lambda内部修改定义在域外的变量。

List<Integer>primes=Arrays.asList(newInteger[]{2,3,5,7}); intfactor=2; primes.forEach(element->{factor  ;}); 

Compile time error : "local variables referenced from a lambda expression must be final or effectively final" 另外,不修改就可以访问它,如下所示:

List<Integer>primes=Arrays.asList(newInteger[]{2,3,5,7}); intfactor=2; primes.forEach(element->{System.out.println(factor*element);}); 

分类

惰性求值法

lists.stream().filter(f->f.getName().equals("p1")) 

例如,代码没有做任何实际的工作,filter只是描述了Stream,没有新的集合。

如果是多个条件组合,可以使用代码块{}

早期求值法

List<Person>list2=lists.stream().filter(f->f.getName().equals("p1")).collect(Collectors.toList()); 

如上示例,collect最终会从Stream产生新值,终止操作。

理想的方法是形成一个惰性的价值链,最后使用早期的价值操作来返回预期的结果。与施工模式类似,施工模式首先使用一系列操作来设置属性和配置,最后调用build方法,创建对象。

stream & parallelStream

每个Stream有两种模式: 顺序并行执行。

顺序流:

List<Person>people=list.getStream.collect(Collectors.toList()); 

并行流:

List<Person>people=list.getStream.parallel().collect(Collectors.toList()); 

顾名思义,当使用顺序去遍历时,每一个item读完后再读下一个item。并行使用时,数组将被分成多个部分,每个部分在不同的线程中处理,然后一起输出结果。

parallelStream原理:

ListoriginalList=someData; split1=originalList(0,mid);//把数据分成小部分 split2=originalList(mid,end); newRunnable(split1.process() newRunnable(split2.process()); ListrevisedList=split1 split二、//合并结果 

大家对hadoop了解一点就知道了,里面 MapReduce 它本身就是一个软件框架,用于并行处理大数据集 处理大数据的核心思想是大数据化小,分配到不同机器去运行map,最终通过reduce将所有机器的结果结合起来得到一个最终结果,与MapReduce不同,Stream则是利用多核技术可将大数据通过多核并行处理,而MapReduce则可以分布式的。

stream与parallelStream性能测试对比

如果是多核机器,理论上并行流则会比顺序流快上一倍,下面是测试代码

long t0 = System.nanoTime();

//初始化一个范围100万整数流,求能被2整除的数字,toArray()是终点方法

int a[]=IntStream.range(0, 1_000_000).filter(p -> p % 2==0).toArray();

long t1 = System.nanoTime();

//和上面功能一样,这里是用并行流来计算

int b[]=IntStream.range(0, 1_000_000).parallel().filter(p -> p % 2==0).toArray();

long t2 = System.nanoTime();

//我本机的结果是serial: 0.06s, parallel 0.02s,证明并行流确实比顺序流快

System.out.printf("serial: %.2fs, parallel %.2fs%n", (t1 - t0) * 1e-9, (t2 - t1) * 1e-9);

Stream中常用方法如下:

  • stream(), parallelStream()

  • filter()

  • findAny() findFirst()

  • sort

  • forEach void

  • map(), reduce()

  • flatMap() - 将多个Stream连接成一个Stream

  • collect(Collectors.toList())

  • distinct, limit

  • count

  • min, max, summaryStatistics

看下所有API:

常用例子

匿名类简写

new Thread( () -> System.out.println("In Java8, Lambda expression rocks !!") ).start();

// 用法
(params) -> expression
(params) -> statement
(params) -> { statements }

forEach

// forEach
List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
features.forEach(n -> System.out.println(n));
 
// 使用Java 8的方法引用更方便,方法引用由::双冒号操作符标示,
features.forEach(System.out::println);

方法引用

构造引用

// Supplier<Student> s = () -> new Student();
Supplier<Student> s = Student::new;

对象::实例方法 Lambda表达式的(形参列表)与实例方法的(实参列表)类型,个数是对应

// set.forEach(t -> System.out.println(t));
set.forEach(System.out::println);

类名::静态方法

// Stream<Double> stream = Stream.generate(() -> Math.random());
Stream<Double> stream = Stream.generate(Math::random);

类名::实例方法

//  TreeSet<String> set = new TreeSet<>((s1,s2) -> s1.compareTo(s2));
/*  这里如果使用第一句话,编译器会有提示: Can be replaced with Comparator.naturalOrder,这句话告诉我们
  String已经重写了compareTo()方法,在这里写是多此一举,这里为什么这么写,是因为为了体现下面
  这句编译器的提示: Lambda can be replaced with method reference。好了,下面的这句就是改写成方法引用之后: 
*/
TreeSet<String> set = new TreeSet<>(String::compareTo);

Filter & Predicate

常规用法

public static void main(args[]){
    List languages = Arrays.asList("Java", "Scala", "C++", "Haskell", "Lisp");
 
    System.out.println("Languages which starts with J :");
    filter(languages, (str)->str.startsWith("J"));
 
    System.out.println("Languages which ends with a ");
    filter(languages, (str)->str.endsWith("a"));
 
    System.out.println("Print all languages :");
    filter(languages, (str)->true);
 
    System.out.println("Print no language : ");
    filter(languages, (str)->false);
 
    System.out.println("Print language whose length greater than 4:");
    filter(languages, (str)->str.length() > 4);
}
 
public static void filter(List names, Predicate condition) {
    names.stream().filter((name) -> (condition.test(name))).forEach((name) -> {
        System.out.println(name + " ");
    });
}

多个Predicate组合filter

// 可以用and()、or()和xor()逻辑函数来合并Predicate,
// 例如要找到所有以J开始,长度为四个字母的名字,你可以合并两个Predicate并传入
Predicate<String> startsWithJ = (n) -> n.startsWith("J");
Predicate<String> fourLetterLong = (n) -> n.length() == 4;
names.stream()
    .filter(startsWithJ.and(fourLetterLong))
    .forEach((n) -> System.out.print("nName, which starts with 'J' and four letter long is : " + n));

Map&Reduce

map将集合类(例如列表)元素进行转换的。还有一个 reduce() 函数可以将所有值合并成一个

List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
double bill = costBeforeTax.stream().map((cost) -> cost + .12*cost).reduce((sum, cost) -> sum + cost).get();
System.out.println("Total : " + bill);

Collectors

// 将字符串换成大写并用逗号链接起来
List<String> G7 = Arrays.asList("USA", "Japan", "France", "Germany", "Italy", "U.K.","Canada");
String G7Countries = G7.stream().map(x -> x.toUpperCase()).collect(Collectors.joining(", "));
System.out.println(G7Countries);
  • Collectors.joining(", ")

  • Collectors.toList()

  • Collectors.toSet() ,生成set集合

  • Collectors.toMap(MemberModel::getUid, Function.identity())

  • Collectors.toMap(ImageModel::getAid, o -> IMAGE_ADDRESS_PREFIX + o.getUrl())

flatMap

将多个Stream连接成一个Stream

List<Integer> result= Stream.of(Arrays.asList(1,3),Arrays.asList(5,6)).flatMap(a->a.stream()).collect(Collectors.toList());

结果: [1, 3, 5, 6]

distinct

去重

List<LikeDO> likeDOs=new ArrayList<LikeDO>();
List<Long> likeTidList = likeDOs.stream().map(LikeDO::getTid)
                .distinct().collect(Collectors.toList());

count

计总数

int countOfAdult=persons.stream()
                       .filter(p -> p.getAge() > 18)
                       .map(person -> new Adult(person))
                       .count();

Match

boolean anyStartsWithA =
    stringCollection
        .stream()
        .anyMatch((s) -> s.startsWith("a"));

System.out.println(anyStartsWithA);      // true

boolean allStartsWithA =
    stringCollection
        .stream()
        .allMatch((s) -> s.startsWith("a"));

System.out.println(allStartsWithA);      // false

boolean noneStartsWithZ =
    stringCollection
        .stream()
        .noneMatch((s) -> s.startsWith("z"));

System.out.println(noneStartsWithZ);      // true

min,max,summaryStatistics

最小值,最大值

List<Person> lists = new ArrayList<Person>();
lists.add(new Person(1L, "p1"));
lists.add(new Person(2L, "p2"));
lists.add(new Person(3L, "p3"));
lists.add(new Person(4L, "p4"));
Person a = lists.stream().max(Comparator.comparing(t -> t.getId())).get();
System.out.println(a.getId());

如果比较器涉及多个条件,比较复杂,可以定制

 Person a = lists.stream().min(new Comparator<Person>() {

      @Override
      public int compare(Person o1, Person o2) {
           if (o1.getId() > o2.getId()) return -1;
           if (o1.getId() < o2.getId()) return 1;
           return 0;
       }
 }).get();

summaryStatistics

//获取数字的个数、最小值、最大值、总和以及平均值
List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29);
IntSummaryStatistics stats = primes.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("Highest prime number in List : " + stats.getMax());
System.out.println("Lowest prime number in List : " + stats.getMin());
System.out.println("Sum of all prime numbers : " + stats.getSum());
System.out.println("Average of all prime numbers : " + stats.getAverage());

peek

可以使用peek方法,peek方法可只包含一个空的方法体,只要能设置断点即可,但有些IDE不允许空,可以如下文示例,简单写一个打印逻辑。

注意,调试完后要删掉。

List<Person> lists = new ArrayList<Person>();
lists.add(new Person(1L, "p1"));
lists.add(new Person(2L, "p2"));
lists.add(new Person(3L, "p3"));
lists.add(new Person(4L, "p4"));
System.out.println(lists);

List<Person> list2 = lists.stream()
     .filter(f -> f.getName().startsWith("p"))
                .peek(t -> {
                    System.out.println(t.getName());
                })
                .collect(Collectors.toList());
System.out.println(list2);

FunctionalInterface

理解注解 @FunctionInterface

/**
 * An informative annotation type used to indicate that an interface
 * type declaration is intended to be a <i>functional interface</i> as
 * defined by the Java Language Specification.
 *
 * Conceptually, a functional interface has exactly one abstract
 * method.  Since {@linkplain java.lang.reflect.Method#isDefault()
 * default methods} have an implementation, they are not abstract.  If
 * an interface declares an abstract method overriding one of the
 * public methods of {@code java.lang.Object}, that also does
 * <em>not</em> count toward the interface's abstract method count
 * since any implementation of the interface will have an
 * implementation from {@code java.lang.Object} or elsewhere.
 *
 * <p>Note that instances of functional interfaces can be created with
 * lambda expressions, method references, or constructor references.
 *
 * <p>If a type is annotated with this annotation type, compilers are
 * required to generate an error message unless:
 *
 * <ul>
 * <li> The type is an interface type and not an annotation type, enum, or class.
 * <li> The annotated type satisfies the requirements of a functional interface.
 * </ul>
 *
 * <p>However, the compiler will treat any interface meeting the
 * definition of a functional interface as a functional interface
 * regardless of whether or not a {@code FunctionalInterface}
 * annotation is present on the interface declaration.
 *
 * @jls 4.3.2. The Class Object
 * @jls 9.8 Functional Interfaces
 * @jls 9.4.3 Interface Method Body
 * @since 1.8
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface{}
  • interface做注解的注解类型,被定义成java语言规范

  • 一个被它注解的接口只能有一个抽象方法,有两种例外

  • 第一是接口允许有实现的方法,这种实现的方法是用default关键字来标记的(java反射中java.lang.reflect.Method#isDefault()方法用来判断是否是default方法)

  • 第二如果声明的方法和java.lang.Object中的某个方法一样,它可以不当做未实现的方法,不违背这个原则: 一个被它注解的接口只能有一个抽象方法, 比如: java public interface Comparator<T> { int compare(T o1, T o2); boolean equals(Object obj); }

  • 如果一个类型被这个注解修饰,那么编译器会要求这个类型必须满足如下条件:

    • 这个类型必须是一个interface,而不是其他的注解类型、枚举enum或者类class

    • 这个类型必须满足function interface的所有要求,如你个包含两个抽象方法的接口增加这个注解,会有编译错误。

  • 编译器会自动把满足function interface要求的接口自动识别为function interface,所以你才不需要对上面示例中的 ITest接口增加@FunctionInterface注解。

自定义函数接口

@FunctionalInterface
public interface IMyInterface {
    void study();
}

package com.isea.java;
public class TestIMyInterface {
    public static void main(String[] args) {
        IMyInterface iMyInterface = () -> System.out.println("I like study");
        iMyInterface.study();
    }
}

内置四大函数接口

  • 消费型接口:Consumer< T> void accept(T t)有参数,无返回值的抽象方法;

比如: map.forEach(BiConsumer<A, T>)

Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);
greeter.accept(new Person("Luke", "Skywalker"));
  • 供给型接口:Supplier < T> T get()无参有返回值的抽象方法;

stream().collect(Collector<? super T, A, R> collector)为例:

比如:

Supplier<Person> personSupplier = Person::new;
personSupplier.get();   // new Person

再如:

// 调用方法
<R, A> R collect(Collector<? super T, A, R> collector)

// Collectors.toSet
public static <T>
 Collector<T, ?, Set<T>> toSet() {
     return new CollectorImpl<>((Supplier<Set<T>>) HashSet::new, Set::add,
                               (left, right) -> { left.addAll(right); return left; },
                               CH_UNORDERED_ID);
}

// CollectorImpl
private final Supplier<A> supplier;
private final BiConsumer<A, T> accumulator;
private final BinaryOperator<A> combiner;
private final Function<A, R> finisher;
private final Set<Characteristics> characteristics;

CollectorImpl(Supplier<A> supplier,
              BiConsumer<A, T> accumulator,
              BinaryOperator<A> combiner,
              Function<A,R> finisher,
              Set<Characteristics> characteristics) {
    this.supplier = supplier;
    this.accumulator = accumulator;
    this.combiner = combiner;
    this.finisher = finisher;
    this.characteristics = characteristics;
}

CollectorImpl(Supplier<A> supplier,
              BiConsumer<A, T> accumulator,
              BinaryOperator<A> combiner,
              Set<Characteristics> characteristics) {
    this(supplier, accumulator, combiner, castingIdentity(), characteristics);
}

// collect()方法实现
public final <R, A> R collect(Collector<? super P_OUT, A, R> collector) {
    A container;
    if (isParallel()
            && (collector.characteristics().contains(Collector.Characteristics.CONCURRENT))
            && (!isOrdered() || collector.characteristics().contains(Collector.Characteristics.UNORDERED))) {
        container = collector.supplier().get();
        BiConsumer<A, ? super P_OUT> accumulator = collector.accumulator();
        forEach(u -> accumulator.accept(container, u));
    }
    else {
        container = evaluate(ReduceOps.makeRef(collector));
    }
    return collector.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)
           ? (R) container
           : collector.finisher().apply(container);
}
  • 断定型接口:Predicate<T> boolean test(T t):有参,但是返回值类型是固定的boolean

比如: steam().filter()中参数就是Predicate

Predicate<String> predicate = (s) -> s.length() > 0;

predicate.test("foo");              // true
predicate.negate().test("foo");     // false

Predicate<Boolean> nonNull = Objects::nonNull;
Predicate<Boolean> isNull = Objects::isNull;

Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNotEmpty = isEmpty.negate();
  • 函数型接口: Function<T,R> R apply(T t)有参有返回值的抽象方法;

比如: steam().map()中参数就是Function<? super T, ? extends R>;reduce()中参数BinaryOperator<T> (ps: BinaryOperator<T> extends BiFunction<T,T,T>)

Function<String, Integer> toInteger = Integer::valueOf;
Function<String, String> backToString = toInteger.andThen(String::valueOf);

backToString.apply("123");     // "123"

一些例子

  • 输出 年龄>25的女程序员中名字排名前3位的姓名

javaProgrammers.stream()
          .filter((p) -> (p.getAge() > 25))
          .filter((p) -> ("female".equals(p.getGender())))
          .sorted((p, p2) -> (p.getFirstName().compareTo(p2.getFirstName())))
          .limit(3)
          //.forEach(e -> e.setSalary(e.getSalary() / 100 * 5 + e.getSalary()))//涨工资
          .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
  • 工资最高的 Java programmer

Person person = javaProgrammers
          .stream()
          .max((p, p2) -> (p.getSalary() - p2.getSalary()))
          .get()
  • 将 Java programmers 的 first name 存放到 TreeSet

TreeSet<String> javaDevLastName = javaProgrammers
          .stream()
          .map(Person::getLastName)
          .collect(toCollection(TreeSet::new))
  • 计算付给 Java programmers 的所有money

int totalSalary = javaProgrammers
          .parallelStream()
          .mapToInt(p -> p.getSalary())
          .sum();
  • Comparator多属性排序: 先按名字不分大小写排,再按GID倒序排,最后按年龄正序排

public static void main(String[] args) {
 List<Person> personList = getTestList();
 personList.sort(Comparator.comparing(Person::getName, String.CASE_INSENSITIVE_ORDER)
   .thenComparing(Person::getGid, (a, b) -> b.compareTo(a))
   .thenComparingInt(Person::getAge));
 personList.stream().forEach(System.out::println);
}

public static List<Person> getTestList() {
 return Lists.newArrayList(new Person("dai", "301", 10), new Person("dai", "303", 10),
   new Person("dai", "303", 8), new Person("dai", "303", 6), new Person("dai", "303", 11),
   new Person("dai", "302", 9), new Person("zhang", "302", 9), new Person("zhang", "301", 9),
   new Person("Li", "301", 8));
}

// 输出结果
// Person [name=dai, gid=303, age=6]
// Person [name=dai, gid=303, age=8]
// Person [name=dai, gid=303, age=10]
// Person [name=dai, gid=303, age=11]
// Person [name=dai, gid=302, age=9]
// Person [name=dai, gid=301, age=10]
// Person [name=Li, gid=301, age=8]
// Person [name=zhang, gid=302, age=9]
// Person [name=zhang, gid=301, age=9]
  • 处理字符串

两个新的方法可在字符串类上使用: join和chars。第一个方法使用指定的分隔符,将任何数量的字符串连接为一个字符串。

String.join(":", "foobar", "foo", "bar");
// => foobar:foo:bar

第二个方法chars从字符串所有字符创建数据流,所以你可以在这些字符上使用流式操作。

"foobar:foo:bar"
    .chars()
    .distinct()
    .mapToObj(c -> String.valueOf((char)c))
    .sorted()
    .collect(Collectors.joining());
// => :abfor

不仅仅是字符串,正则表达式模式串也能受益于数据流。我们可以分割任何模式串,并创建数据流来处理它们,而不是将字符串分割为单个字符的数据流,像下面这样:

Pattern.compile(":")
    .splitAsStream("foobar:foo:bar")
    .filter(s -> s.contains("bar"))
    .sorted()
    .collect(Collectors.joining(":"));
// => bar:foobar

此外,正则模式串可以转换为谓词。这些谓词可以像下面那样用于过滤字符串流:

Pattern pattern = Pattern.compile(".*@gmail\\.com");
Stream.of("bob@gmail.com", "alice@hotmail.com")
    .filter(pattern.asPredicate())
    .count();
// => 1

上面的模式串接受任何以@gmail.com结尾的字符串,并且之后用作Java8的Predicate来过滤电子邮件地址流。

  • Local Cache实现

public class TestLocalCache {

 private static ConcurrentHashMap<Integer, Long> cache = new ConcurrentHashMap<>();

 static long fibonacci(int i) {
  if (i == 0)
   return i;

  if (i == 1)
   return 1;

  return cache.computeIfAbsent(i, (key) -> {
   System.out.println("Slow calculation of " + key);

   return fibonacci(i - 2) + fibonacci(i - 1);
  });
 }
 
 public static void main(String[] args) {
  // warm up
  for (int i = 0; i < 101; i++)
         System.out.println(
             "f(" + i + ") = " + fibonacci(i));
  
  // read -> cal
  long current = System.currentTimeMillis();
  System.out.println(fibonacci(100));
  System.out.println(System.currentTimeMillis()-current);
 }
}
  • 集合--》取元素的一个属性--》去重---》组装成List--》返回

List<LikeDO> likeDOs=new ArrayList<LikeDO>();
List<Long> likeTidList = likeDOs.stream().map(LikeDO::getTid)
                .distinct().collect(Collectors.toList());
  • 集合--》按表达式过滤--》遍历、每个元系处理--》放入预先定义的集合中

  Map<String, StkProduct> newStockName2Product = Maps.newConcurrentMap();
        stockProducts.stream().filter(stkProduct -> stkProduct.enabled).forEach(stkProduct -> {
            String newName = BCConvert.bj2qj(StringUtils.replace(stkProduct.name, " ", ""));
            newStockName2Product.put(newName, stkProduct);
        });
 Set<String> qjStockNames;
 qjStockNames.stream().filter(name -> !acAutomaton.getKey2link().containsKey(name)).forEach(name -> {
            String value = "";
            StkProduct stkProduct = stockNameQj2Product.get(name);
            if (stkProduct != null) {
                value = stkProduct.name;
            }
            acAutomaton.getKey2link().put(name, value);
        });
  • 集合--》map

List<ImageModel> imageModelList = null;
Map<Long, String> imagesMap = null;
imagesMap = imageModelList.stream().collect(Collectors.toMap(ImageModel::getAid, o -> IMAGE_ADDRESS_PREFIX + o.getUrl()));
              
             

Map<String, String> kvMap = postDetailCacheList.stream().collect(Collectors.toMap((detailCache) ->
                getBbsSimplePostKey(detailCache.getTid()), JSON::toJSONString));


Map<Long, Long> pidToTid;
List<String> pidKeyList = pidToTid.entrySet().stream().map((o) -> getKeyBbsReplyPid(o.getValue(), o.getKey())).collect(Collectors.toList());

  • DO模型---》Model模型

List<AdDO> adDOList;
adDOList.stream().map(adDo -> convertAdModel(adDo))
                .collect(Collectors.toList());
  • phones 是一个List<String>,将相同的元素分组、归类

List<String> phones=new ArrayList<String>();
        phones.add("a");
        phones.add("b");
        phones.add("a");
        phones.add("a");
        phones.add("c");
        phones.add("b");
        Map<String, List<String>> phoneClassify = phones.stream().collect(Collectors.groupingBy(item -> item));
        System.out.println(phoneClassify);
返回结果: 
{a=[a, a, a], b=[b, b], c=[c]}

Optional类深度解析

Optional类包含的方法

of

为非null的值创建一个Optional。

of方法通过工厂方法创建Optional类。需要注意的是,创建对象时传入的参数不能为null。如果传入参数为null,则抛出NullPointerException 。

//调用工厂方法创建Optional实例
Optional<String> name = Optional.of("Sanaulla");
//传入参数为null,抛出NullPointerException.
Optional<String> someNull = Optional.of(null);
ofNullable

为指定的值创建一个Optional,如果指定的值为null,则返回一个空的Optional。

ofNullable与of方法相似,唯一的区别是可以接受参数为null的情况。示例如下:

//下面创建了一个不包含任何值的Optional实例
//例如,值为'null'
Optional empty = Optional.ofNullable(null);
isPresent

非常容易理解:如果值存在返回true,否则返回false。

类似下面的代码:

//isPresent方法用来检查Optional实例中是否包含值
if (name.isPresent()) {
  //在Optional实例内调用get()返回已存在的值
  System.out.println(name.get());//输出Sanaulla
}
get

如果Optional有值则将其返回,否则抛出NoSuchElementException。

上面的示例中,get方法用来得到Optional实例中的值。下面我们看一个抛出NoSuchElementException的例子:

//执行下面的代码会输出: No value present 
try {
  //在空的Optional实例上调用get(),抛出NoSuchElementException
  System.out.println(empty.get());
} catch (NoSuchElementException ex) {
  System.out.println(ex.getMessage());
}
ifPresent

如果Optional实例有值则为其调用consumer,否则不做处理

要理解ifPresent方法,首先需要了解Consumer类。简答地说,Consumer类包含一个抽象方法。该抽象方法对传入的值进行处理,但没有返回值。Java8支持不用接口直接通过lambda表达式传入参数。

如果Optional实例有值,调用ifPresent()可以接受接口段或lambda表达式。类似下面的代码:

//ifPresent方法接受lambda表达式作为参数。
//lambda表达式对Optional的值调用consumer进行处理。
name.ifPresent((value) -> {
  System.out.println("The length of the value is: " + value.length());
});
orElse

如果有值则将其返回,否则返回指定的其它值。

如果Optional实例有值则将其返回,否则返回orElse方法传入的参数。示例如下:

//如果值不为null,orElse方法返回Optional实例的值。
//如果为null,返回传入的消息。
//输出: There is no value present!
System.out.println(empty.orElse("There is no value present!"));
//输出: Sanaulla
System.out.println(name.orElse("There is some value!"));
orElseGet

orElseGet与orElse方法类似,区别在于得到的默认值。orElse方法将传入的字符串作为默认值,orElseGet方法可以接受Supplier接口的实现用来生成默认值。示例如下:

//orElseGet与orElse方法类似,区别在于orElse传入的是默认值,
//orElseGet可以接受一个lambda表达式生成默认值。
//输出: Default Value
System.out.println(empty.orElseGet(() -> "Default Value"));
//输出: Sanaulla
System.out.println(name.orElseGet(() -> "Default Value"));
orElseThrow

如果有值则将其返回,否则抛出supplier接口创建的异常。

在orElseGet方法中,我们传入一个Supplier接口。然而,在orElseThrow中我们可以传入一个lambda表达式或方法,如果值不存在来抛出异常。示例如下:

try {
  //orElseThrow与orElse方法类似。与返回默认值不同,
  //orElseThrow会抛出lambda表达式或方法生成的异常 

  empty.orElseThrow(ValueAbsentException::new);
} catch (Throwable ex) {
  //输出: No value present in the Optional instance
  System.out.println(ex.getMessage());
}

ValueAbsentException定义如下:

class ValueAbsentException extends Throwable {

  public ValueAbsentException() {
    super();
  }

  public ValueAbsentException(String msg) {
    super(msg);
  }

  @Override
  public String getMessage() {
    return "No value present in the Optional instance";
  }
}
map

map方法文档说明如下:

如果有值,则对其执行调用mapping函数得到返回值。如果返回值不为null,则创建包含mapping返回值的Optional作为map方法返回值,否则返回空Optional。

map方法用来对Optional实例的值执行一系列操作。通过一组实现了Function接口的lambda表达式传入操作。如果你不熟悉Function接口,可以参考我的这篇博客。map方法示例如下:

//map方法执行传入的lambda表达式参数对Optional实例的值进行修改。
//为lambda表达式的返回值创建新的Optional实例作为map方法的返回值。
Optional<String> upperName = name.map((value) -> value.toUpperCase());
System.out.println(upperName.orElse("No value found"));
flatMap

如果有值,为其执行mapping函数返回Optional类型返回值,否则返回空Optional。flatMap与map(Funtion)方法类似,区别在于flatMap中的mapper返回值必须是Optional。调用结束时,flatMap不会对结果用Optional封装。

flatMap方法与map方法类似,区别在于mapping函数的返回值不同。map方法的mapping函数返回值可以是任何类型T,而flatMap方法的mapping函数必须是Optional。

参照map函数,使用flatMap重写的示例如下:

//flatMap与map(Function)非常类似,区别在于传入方法的lambda表达式的返回类型。
//map方法中的lambda表达式返回值可以是任意类型,在map函数返回之前会包装为Optional。 
//但flatMap方法中的lambda表达式返回值必须是Optionl实例。 
upperName = name.flatMap((value) -> Optional.of(value.toUpperCase()));
System.out.println(upperName.orElse("No value found"));//输出SANAULLA
filter

filter个方法通过传入限定条件对Optional实例的值进行过滤。文档描述如下:

如果有值并且满足断言条件返回包含该值的Optional,否则返回空Optional。

读到这里,可能你已经知道如何为filter方法传入一段代码。是的,这里可以传入一个lambda表达式。对于filter函数我们应该传入实现了Predicate接口的lambda表达式。如果你不熟悉Predicate接口,可以参考这篇文章。

现在我来看看filter的各种用法,下面的示例介绍了满足限定条件和不满足两种情况:

//filter方法检查给定的Option值是否满足某些条件。
//如果满足则返回同一个Option实例,否则返回空Optional。
Optional<String> longName = name.filter((value) -> value.length() > 6);
System.out.println(longName.orElse("The name is less than 6 characters"));//输出Sanaulla

//另一个例子是Optional值不满足filter指定的条件。
Optional<String> anotherName = Optional.of("Sana");
Optional<String> shortName = anotherName.filter((value) -> value.length() > 6);
//输出: name长度不足6字符
System.out.println(shortName.orElse("The name is less than 6 characters"));
一些例子
  • 一个综合例子

public class OptionalDemo {

  public static void main(String[] args) {
    //创建Optional实例,也可以通过方法返回值得到。
    Optional<String> name = Optional.of("Sanaulla");

    //创建没有值的Optional实例,例如值为'null'
    Optional empty = Optional.ofNullable(null);

    //isPresent方法用来检查Optional实例是否有值。
    if (name.isPresent()) {
      //调用get()返回Optional值。
      System.out.println(name.get());
    }

    try {
      //在Optional实例上调用get()抛出NoSuchElementException。
      System.out.println(empty.get());
    } catch (NoSuchElementException ex) {
      System.out.println(ex.getMessage());
    }

    //ifPresent方法接受lambda表达式参数。
    //如果Optional值不为空,lambda表达式会处理并在其上执行操作。
    name.ifPresent((value) -> {
      System.out.println("The length of the value is: " + value.length());
    });

    //如果有值orElse方法会返回Optional实例,否则返回传入的错误信息。
    System.out.println(empty.orElse("There is no value present!"));
    System.out.println(name.orElse("There is some value!"));

    //orElseGet与orElse类似,区别在于传入的默认值。
    //orElseGet接受lambda表达式生成默认值。
    System.out.println(empty.orElseGet(() -> "Default Value"));
    System.out.println(name.orElseGet(() -> "Default Value"));

    try {
      //orElseThrow与orElse方法类似,区别在于返回值。
      //orElseThrow抛出由传入的lambda表达式/方法生成异常。
      empty.orElseThrow(ValueAbsentException::new);
    } catch (Throwable ex) {
      System.out.println(ex.getMessage());
    }

    //map方法通过传入的lambda表达式修改Optonal实例默认值。 
    //lambda表达式返回值会包装为Optional实例。
    Optional<String> upperName = name.map((value) -> value.toUpperCase());
    System.out.println(upperName.orElse("No value found"));

    //flatMap与map(Funtion)非常相似,区别在于lambda表达式的返回值。
    //map方法的lambda表达式返回值可以是任何类型,但是返回值会包装成Optional实例。
    //但是flatMap方法的lambda返回值总是Optional类型。
    upperName = name.flatMap((value) -> Optional.of(value.toUpperCase()));
    System.out.println(upperName.orElse("No value found"));

    //filter方法检查Optiona值是否满足给定条件。
    //如果满足返回Optional实例值,否则返回空Optional。
    Optional<String> longName = name.filter((value) -> value.length() > 6);
    System.out.println(longName.orElse("The name is less than 6 characters"));

    //另一个示例,Optional值不满足给定条件。
    Optional<String> anotherName = Optional.of("Sana");
    Optional<String> shortName = anotherName.filter((value) -> value.length() > 6);
    System.out.println(shortName.orElse("The name is less than 6 characters"));

  }
}

上述代码输出如下:

Sanaulla
No value present
The length of the value is: 8
There is no value present!
Sanaulla
Default Value
Sanaulla
No value present in the Optional instance
SANAULLA
SANAULLA
Sanaulla
The name is less than 6 characters
  • 在 Java 8 中提高 Null 的安全性

假设我们有一个像这样的类层次结构:

class Outer {
    Nested nested;
    Nested getNested() {
        return nested;
    }
}
class Nested {
    Inner inner;
    Inner getInner() {
        return inner;
    }
}
class Inner {
    String foo;
    String getFoo() {
        return foo;
    }
}

解决这种结构的深层嵌套路径是有点麻烦的。我们必须编写一堆 null 检查来确保不会导致一个 NullPointerException:

Outer outer = new Outer();
if (outer != null && outer.nested != null && outer.nested.inner != null) {
    System.out.println(outer.nested.inner.foo);
}

我们可以通过利用 Java 8 的 Optional 类型来摆脱所有这些 null 检查。map 方法接收一个 Function 类型的 lambda 表达式,并自动将每个 function 的结果包装成一个 Optional 对象。这使我们能够在一行中进行多个 map 操作。Null 检查是在底层自动处理的。

Optional.of(new Outer())
    .map(Outer::getNested)
    .map(Nested::getInner)
    .map(Inner::getFoo)
    .ifPresent(System.out::println);

还有一种实现相同作用的方式就是通过利用一个 supplier 函数来解决嵌套路径的问题:

Outer obj = new Outer();
resolve(() -> obj.getNested().getInner().getFoo());
    .ifPresent(System.out::println);

调用 obj.getNested().getInner().getFoo()) 可能会抛出一个 NullPointerException 异常。在这种情况下,该异常将会被捕获,而该方法会返回 Optional.empty()。

public static <T> Optional<T> resolve(Supplier<T> resolver) {
    try {
        T result = resolver.get();
        return Optional.ofNullable(result);
    }
    catch (NullPointerException e) {
        return Optional.empty();
    }
}

请记住,这两个解决方案可能没有传统 null 检查那么高的性能。不过在大多数情况下不会有太大问题。

翻译: ImportNew.com - 高俊阳  译文链接: http://www.importnew.com/6675.html

default方法解析

什么是默认方法,为什么要有默认方法

先上例子

一个接口A,Clazz类实现了接口A。

public interface A {
    default void foo(){
       System.out.println("Calling A.foo()");
    }
}

public class Clazz implements A {
    public static void main(String[] args){
       Clazz clazz = new Clazz();
       clazz.foo();//调用A.foo()
    }
}

代码是可以编译的,即使Clazz类并没有实现foo()方法。在接口A中提供了foo()方法的默认实现。

什么是默认方法

简单说,就是接口可以有实现方法,而且不需要实现类去实现其方法。只需在方法名前面加个default关键字即可。

为什么出现默认方法

为什么要有这个特性? 首先,之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类,目前的java 8之前的集合框架没有foreach方法,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。

java 8抽象类与接口对比

这一个功能特性出来后,很多同学都反应了,java 8的接口都有实现方法了,跟抽象类还有什么区别? 其实还是有的,请看下表对比。。

多重继承的冲突

由于同一个方法可以从不同接口引入,自然而然的会有冲突的现象,默认方法

标签: zh1032d胀差变送器装置10v重量变送器0zh200重量变送器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台