从JAVA5到JAVA10新特性总结

本文参考或者摘抄:https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/

目录

JDK5新特性

1:自动装箱与拆箱:

2:枚举

3:静态导入

4:可变参数(Varargs)

5:内省(Introspector)

6:泛型(Generic)

7:For-Each循环

JDK 6新特性

1:Desktop类和SystemTray类

2:使用JAXB2来实现对象与XML之间的映射

3:理解StAX

4.使用Compiler API

5:轻量级Http Server API

6:插入式注解处理API(Pluggable Annotation Processing API)

7:用Console开发控制台程序

8:对脚本语言的支持如: ruby, groovy, javascript

9:Common Annotations

JDK 7 新特性

1:switch中可以使用字符串

2:”<>”的运用List tempList = new ArrayList<>(); 即泛型实例化类型自动推断。

3:自定义自动关闭类 

4:在try catch异常扑捉中,一个catch可以写多个异常类型,用”|”隔开**

JDK 8新特性

Lambda表达式

1.什么是Lambda?

函数式接口

默认方法

Stream

流的构成

flatMap

filter

limit-skip

sorted

min/max/distinct

Match

Collectors

统计

toArray

结束语


JDK5新特性

1:自动装箱与拆箱:

自动装箱的过程:每当需要一种类型的对象时,这种基本类型就自动地封装到与它相同类型的包装类中。

 

自动拆箱的过程:每当需要一个值时,被包装对象中的值就被自动地提取出来,没必要再去调用intValue()和doubleValue()方法。

 

自动装箱,只需将该值赋给一个类型包装器引用,java会自动创建一个对象。

 

自动拆箱,只需将该对象值赋给一个基本类型即可。

 

类型包装器有:Double,Float,Long,Integer,Short,Character和Boolean

 

2:枚举

 

把集合里的对象元素一个一个提取出来。枚举类型使代码更具可读性,理解清晰,易于维护。枚举类型是强类型的,从而保证了系统安全性。而以类的静态字段实现的类似替代模型,不具有枚举的简单性和类型安全性。

 

简单的用法:JavaEnum简单的用法一般用于代表一组常用常量,可用来代表一类相同类型的常量值。

 

复杂用法:Java为枚举类型提供了一些内置的方法,同事枚举常量还可以有自己的方法。可以很方便的遍历枚举对象。

 

3:静态导入

 

通过使用 import static,就可以不用指定 Constants 类名而直接使用静态成员,包括静态方法。

 

import xxxx 和 import static xxxx的区别是前者一般导入的是类文件如import java.util.Scanner;后者一般是导入静态的方法,import static java.lang.System.out。

 

4:可变参数(Varargs)

 

可变参数的简单语法格式为:

 

methodName([argumentList], dataType… argumentName);

 

5:内省(Introspector)

 

内省是Java语言对Bean类属性、事件的一种缺省处理方法。例如类A中有属性name,那我们可以通过getName,setName来得到其值或者设置新的值。通过getName/setName来访问name属性,这就是默认的规则。Java中提供了一套API用来访问某个属性的getter /setter方法,通过这些API可以使你不需要了解这个规则(但你最好还是要搞清楚),这些API存放于包java.beans中。

 

一 般的做法是通过类Introspector来获取某个对象的BeanInfo信息,然后通过BeanInfo来获取属性的描述器 (PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的getter/setter方法,然后我们就可以通过反射机制来调用这些方法。

 

6:泛型(Generic)

 

C++ 通过模板技术可以指定集合的元素类型,而Java在1.5之前一直没有相对应的功能。一个集合可以放任何类型的对象,相应地从集合里面拿对象的时候我们也不得不对他们进行强制得类型转换。引入了泛型,它允许指定集合里元素的类型,这样你可以得到强类型在编译时刻进行类型检查的好处。

 

7:For-Each循环

 

For-Each循环得加入简化了集合的遍历。假设我们要遍历一个集合对其中的元素进行一些处理。

 

 

JDK 6新特性

1:Desktop类和SystemTray类

 

在JDK6中 ,AWT新增加了两个类:Desktop和SystemTray。

 

前者可以用来打开系统默认浏览器浏览指定的URL,打开系统默认邮件客户端给指定的邮箱发邮件,用默认应用程序打开或编辑文件(比如,用记事本打开以txt为后缀名的文件),用系统默认的打印机打印文档;后者可以用来在系统托盘区创建一个托盘程序.

 

2:使用JAXB2来实现对象与XML之间的映射

 

JAXB是Java Architecture for XML Binding的缩写,可以将一个Java对象转变成为XML格式,反之亦然。

 

我们把对象与关系数据库之间的映射称为ORM, 其实也可以把对象与XML之间的映射称为OXM(Object XML Mapping). 原来JAXB是Java EE的一部分,在JDK6中,SUN将其放到了Java SE中,这也是SUN的一贯做法。JDK6中自带的这个JAXB版本是2.0, 比起1.0(JSR 31)来,JAXB2(JSR 222)用JDK5的新特性Annotation来标识要作绑定的类和属性等,这就极大简化了开发的工作量。

 

实 际上,在Java EE 5.0中,EJB和Web Services也通过Annotation来简化开发工作。另外,JAXB2在底层是用StAX(JSR 173)来处理XML文档。除了JAXB之外,我们还可以通过XMLBeans和Castor等来实现同样的功能。

 

3:理解StAX

 

StAX(JSR 173)是JDK6.0中除了DOM和SAX之外的又一种处理XML文档的API。

 

StAX 的来历 :在JAXP1.3(JSR 206)有两种处理XML文档的方法:DOM(Document Object Model)和SAX(Simple API for XML).

 

由 于JDK6.0中的JAXB2(JSR 222)和JAX-WS 2.0(JSR 224)都会用到StAX,所以Sun决定把StAX加入到JAXP家族当中来,并将JAXP的版本升级到1.4(JAXP1.4是JAXP1.3的维护版本). JDK6里面JAXP的版本就是1.4. 。

 

StAX是The Streaming API for XML的缩写,一种利用拉模式解析(pull-parsing)XML文档的API.StAX通过提供一种基于事件迭代器(Iterator)的API让程序员去控制xml文档解析过程,程序遍历这个事件迭代器去处理每一个解析事件,解析事件可以看做是程序拉出来的,也就是程序促使解析器产生一个解析事件,然后处理该事件,之后又促使解析器产生下一个解析事件,如此循环直到碰到文档结束符;

 

SAX也是基于事件处理xml文档,但却是用推模式解析,解析器解析完整个xml文档后,才产生解析事件,然后推给程序去处理这些事件;DOM 采用的方式是将整个xml文档映射到一颗内存树,这样就可以很容易地得到父节点和子结点以及兄弟节点的数据,但如果文档很大,将会严重影响性能。

 

4.使用Compiler API

 

现在我们可以用JDK6 的Compiler API(JSR 199)去动态编译Java源文件,Compiler API结合反射功能就可以实现动态的产生Java代码并编译执行这些代码,有点动态语言的特征。

 

这个特性对于某些需要用到动态编译的应用程序相当有用,比如JSP Web Server,当我们手动修改JSP后,是不希望需要重启Web Server才可以看到效果的,这时候我们就可以用Compiler API来实现动态编译JSP文件,当然,现在的JSP Web Server也是支持JSP热部署的,现在的JSP Web Server通过在运行期间通过Runtime.exec或ProcessBuilder来调用javac来编译代码,这种方式需要我们产生另一个进程去做编译工作,不够优雅而且容易使代码依赖与特定的操作系统;Compiler API通过一套易用的标准的API提供了更加丰富的方式去做动态编译,而且是跨平台的。

 

5:轻量级Http Server API

 

JDK6 提供了一个简单的Http Server API,据此我们可以构建自己的嵌入式Http Server,它支持Http和Https协议,提供了HTTP1.1的部分实现,没有被实现的那部分可以通过扩展已有的Http Server API来实现,程序员必须自己实现HttpHandler接口,HttpServer会调用HttpHandler实现类的回调方法来处理客户端请求,在 这里,我们把一个Http请求和它的响应称为一个交换,包装成HttpExchange类,HttpServer负责将HttpExchange传给 HttpHandler实现类的回调方法.

 

6:插入式注解处理API(Pluggable Annotation Processing API)

 

插入式注解处理API(JSR 269)提供一套标准API来处理Annotations(JSR 175)

 

实 际上JSR 269不仅仅用来处理Annotation,我觉得更强大的功能是它建立了Java 语言本身的一个模型,它把method, package, constructor, type, variable, enum, annotation等Java语言元素映射为Types和Elements(两者有什么区别?), 从而将Java语言的语义映射成为对象, 我们可以在javax.lang.model包下面可以看到这些类. 所以我们可以利用JSR 269提供的API来构建一个功能丰富的元编程(metaprogramming)环境.

 

JSR 269用Annotation Processor在编译期间而不是运行期间处理Annotation, Annotation Processor相当于编译器的一个插件,所以称为插入式注解处理.如果Annotation Processor处理Annotation时(执行process方法)产生了新的Java代码,编译器会再调用一次Annotation Processor,如果第二次处理还有新代码产生,就会接着调用Annotation Processor,直到没有新代码产生为止.每执行一次process()方法被称为一个”round”,这样整个Annotation processing过程可以看作是一个round的序列.

 

JSR 269主要被设计成为针对Tools或者容器的API. 举个例子,我们想建立一套基于Annotation的单元测试框架(如TestNG),在测试类里面用Annotation来标识测试期间需要执行的测试方法

 

7:用Console开发控制台程序

 

JDK6 中提供了java.io.Console 类专用来访问基于字符的控制台设备. 你的程序如果要与Windows下的cmd或者Linux下的Terminal交互,就可以用Console类代劳. 但我们不总是能得到可用的Console, 一个JVM是否有可用的Console依赖于底层平台和JVM如何被调用. 如果JVM是在交互式命令行(比如Windows的cmd)中启动的,并且输入输出没有重定向到另外的地方,那么就可以得到一个可用的Console实例.

 

8:对脚本语言的支持如: ruby, groovy, javascript

 

9:Common Annotations

 

Common annotations原本是Java EE 5.0(JSR 244)规范的一部分,现在SUN把它的一部分放到了Java SE 6.0中.

 

随 着Annotation元数据功能(JSR 175)加入到Java SE 5.0里面,很多Java 技术(比如EJB,Web Services)都会用Annotation部分代替XML文件来配置运行参数(或者说是支持声明式编程,如EJB的声明式事务), 如果这些技术为通用目的都单独定义了自己的Annotations,显然有点重复建设, 所以,为其他相关的Java技术定义一套公共的Annotation是有价值的,可以避免重复建设的同时,也保证Java SE和Java EE 各种技术的一致性.

 

下面列举出Common Annotations 1.0里面的10个Annotations Common Annotations

 

Annotation Retention Target Description

 

Generated Source ANNOTATION_TYPE, CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE 用于标注生成的源代码

 

Resource Runtime TYPE, METHOD, FIELD 用于标注所依赖的资源,容器据此注入外部资源依赖,有基于字段的注入和基于setter方法的注入两种方式

 

Resources Runtime TYPE 同时标注多个外部依赖,容器会把所有这些外部依赖注入

 

PostConstruct Runtime METHOD 标注当容器注入所有依赖之后运行的方法,用来进行依赖注入后的初始化工作,只有一个方法可以标注为PostConstruct

 

PreDestroy Runtime METHOD 当对象实例将要被从容器当中删掉之前,要执行的回调方法要标注为PreDestroy RunAs Runtime TYPE 用于标注用什么安全角色来执行被标注类的方法,这个安全角色必须和Container 的Security角色一致的。RolesAllowed Runtime TYPE, METHOD 用于标注允许执行被标注类或方法的安全角色,这个安全角色必须和Container 的Security角色一致的

 

PermitAll Runtime TYPE, METHOD 允许所有角色执行被标注的类或方法

 

DenyAll Runtime TYPE, METHOD 不允许任何角色执行被标注的类或方法,表明该类或方法不能在Java EE容器里面运行

 

DeclareRoles Runtime TYPE 用来定义可以被应用程序检验的安全角色,通常用isUserInRole来检验安全角色

注意:

 

1.RolesAllowed,PermitAll,DenyAll不能同时应用到一个类或方法上

 

2.标注在方法上的RolesAllowed,PermitAll,DenyAll会覆盖标注在类上的RolesAllowed,PermitAll,DenyAll

 

3.RunAs,RolesAllowed,PermitAll,DenyAll和DeclareRoles还没有加到Java SE 6.0上来

 

4.处理以上Annotations的工作是由Java EE容器来做, Java SE 6.0只是包含了上面表格的前五种Annotations的定义类,并没有包含处理这些Annotations的引擎,这个工作可以由Pluggable Annotation Processing API(JSR 269)来做

 

改动的地方最大的就是java GUI界面的显示了,JDK6.0(也就是JDK1.6)支持最新的windows vista系统的Windows Aero视窗效果,而JDK1.5不支持!!!

 

你要在vista环境下编程的话最好装jdk6.0,否则它总是换到windows basic视窗效果.

 

 

JDK 7 新特性

1:switch中可以使用字符串

public static void testSwitch() {

    String s = "test";

    switch (s) {

    case "test":

       System.out.println("test");

       break;

    case "test1":

       System.out.println("test1");

       break;

    default:

       System.out.println("break");

       break;

    }

}

 

2:”<>”的运用List tempList = new ArrayList<>(); 即泛型实例化类型自动推断。

public static void testFX() {

    // JDK 7之前

    List<String> lst1 = new ArrayList<String>();

    for (String item : lst1) {

        System.out.println(item);

    }

    // JDK 7 supports limited type inference for generic instance creation

    List<String> lst2 = new ArrayList<>();

    lst1.add("Mon");

    lst1.add("Tue");

    lst2.add("Wed");

    lst2.add("Thu");

    for (String item : lst2) {

        System.out.println(item);

    }

}

 

3:自定义自动关闭类 

以下是jdk7 api中的接口 ,该方法在try-with-resources语句中会被自动调用,用于自动释放资源。

 

public interface AutoCloseable {

    /**

     * Closes this resource, relinquishing any underlying resources.

     * This method is invoked automatically on objects managed by the

     * try-with-resources statement.

     *

     * <p>While this interface method is declared to throw {@code

     * Exception}, implementers are <em>strongly</em> encouraged to

     * declare concrete implementations of the {@code close} method to

     * throw more specific exceptions, or to throw no exception at all

     * if the close operation cannot fail.

     *

     * <p> Cases where the close operation may fail require careful

     * attention by implementers. It is strongly advised to relinquish

     * the underlying resources and to internally <em>mark</em> the

     * resource as closed, prior to throwing the exception. The {@code

     * close} method is unlikely to be invoked more than once and so

     * this ensures that the resources are released in a timely manner.

     * Furthermore it reduces problems that could arise when the resource

     * wraps, or is wrapped, by another resource.

     *

     * <p><em>Implementers of this interface are also strongly advised

     * to not have the {@code close} method throw {@link

     * InterruptedException}.</em>

     *

     * This exception interacts with a thread's interrupted status,

     * and runtime misbehavior is likely to occur if an {@code

     * InterruptedException} is {@linkplain Throwable#addSuppressed

     * suppressed}.

     *

     * More generally, if it would cause problems for an

     * exception to be suppressed, the {@code AutoCloseable.close}

     * method should not throw it.

     *

     * <p>Note that unlike the {@link java.io.Closeable#close close}

     * method of {@link java.io.Closeable}, this {@code close} method

     * is <em>not</em> required to be idempotent.  In other words,

     * calling this {@code close} method more than once may have some

     * visible side effect, unlike {@code Closeable.close} which is

     * required to have no effect if called more than once.

     *

     * However, implementers of this interface are strongly encouraged

     * to make their {@code close} methods idempotent.

     *

     * @throws Exception if this resource cannot be closed

     */

    void close() throws Exception;

}

只要实现该接口,在该类对象销毁时自动调用close方法,你可以在close方法关闭你想关闭的资源,例子如下

public class TestAutoClose implements AutoCloseable {

   

    public void eat() {

       System.out.println("call eat() method.........");

    }

 

    @Override

    public void close() throws Exception {

       System.out.println("call close() method.........");

    }

}

public static void testClose() throws Exception {

    try (TestAutoClose test = new TestAutoClose();) {

       test.eat();

    } catch (Exception ex) {

       ex.printStackTrace();

    }

    // test.close();

}

 

 

这个特性一般用于 IO,如InputStream,Writes,Sockets,Sql classes等。这个新的语言特性允许try语句本身申请更多的资源,这些资源作用于try代码块,并自动关闭。

以前的写法:

BufferedReader br = new BufferedReader(new FileReader(path));

try {

    return br.readLine();

  } finally {

          br.close();

  }

现在:

try (BufferedReader br = new BufferedReader(new FileReader(path)) {

     return br.readLine();

 }

 

 

4:在try catch异常扑捉中,一个catch可以写多个异常类型,用”|”隔开**

    public static void testClose() throws Exception {

       try (TestAutoClose test = new TestAutoClose();) {

           test.eat();

       } catch (IOException | ClassNotFoundException ex) {

           ex.printStackTrace();

       }

    }

 

JDK 8新特性

参考这里http://www.runoob.com/java/java8-new-features.html

 

Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本。 Oracle 公司于 2014 年 3 月 18 日发布 Java 8 ,它支持函数式编程,新的 JavaScript 引擎,新的日期 API,新的Stream API 等。

Java8 新增了非常多的特性,我们主要讨论以下几个:

  • Lambda 表达式 − Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中。
  • 方法引用 − 方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
  • 默认方法 − 默认方法就是一个在接口里面有了一个实现的方法。
  • 新工具 − 新的编译工具,如:Nashorn引擎 jjs、 类依赖分析器jdeps。
  • Stream API −新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。
  • Date Time API − 加强对日期与时间的处理。
  • Optional  − Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。
  • Nashorn, JavaScript 引擎 − Java 8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用。

 

Lambda表达式

Lambda表达式是Java SE 8中一个重要的新特性。lambda表达式允许你通过表达式来代替功能接口。 lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块)。

Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。

使用 Lambda 表达式可以使代码变的更加简洁紧凑。

 

1.什么是Lambda?

我们知道,对于一个Java变量,我们可以赋给其一个“值”。 
 

 

函数式接口

函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。

函数式接口可以被隐式转换为 lambda 表达式。

Lambda 表达式和方法引用(实际上也可认为是Lambda表达式)上。

如定义了一个函数式接口如下:

 

@FunctionalInterface
interface GreetingService 
{
    void sayMessage(String message);
}

那么就可以使用Lambda表达式来表示该接口的一个实现(注:JAVA 8 之前一般是用匿名类实现的)

GreetingService greetService1 = message -> System.out.println("Hello " + message);

 

默认方法

Java 8 新增了接口的默认方法。

简单说,默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法。

我们只需在方法名前面加个 default 关键字即可实现默认方法。

为什么要有这个特性?

首先,之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,

缺陷是,当需要新增接口时候,需要修改全部实现该接口的类,目前的 java 8 之前的集合框架没有 foreach 方法,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。

 

语法:

 

public interface Animal {

    void eat();

    default void eatmouse() {

       System.out.println("吃老鼠");

    }

}

public class Cat implements Animal {

 

    @Override

    public void eat() {

    }

 

    @Override

    public void eatmouse() {

       Animal.super.eatmouse();

    }

 

}

public class Dog implements Animal {

    @Override

    public void eat() {

    }

}

 

 

Stream

Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念。它也不同于 StAX 对 XML 解析的 Stream,也不是 Amazon Kinesis 对大数据实时处理的 Stream。Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。

 

Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。

 

而和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。顾名思义,当使用串行方式去遍历时,每个 item 读完后再读下一个 item。而使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。

 

什么是 Stream

Stream(流)是一个来自数据源的元素队列并支持聚合操作

  1. 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
  2. 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
  3. 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

 

和以前的Collection操作不同, Stream操作还有两个基础的特征:

  1. Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
  2. 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

 

流的构成

当我们使用一个流的时候,通常包括三个基本步骤:

获取一个数据源(source数据转换执行操作获取想要的结果,每次转换原有 Stream 对象不改变,返回一个新的 Stream 对象(可以有多次转换),这就允许对其操作可以像链条一样排列,变成一个管道,如下图所示。

 

 

flatMap

flatMap 把 input Stream 中的层级结构扁平化,就是将最底层元素抽出来放到一起,最终 output 的新 Stream 里面已经没有 List 了,都是直接的数字。

    /**

    * @Title: testFlatMap

    * @author: lance.lan

    * @Description: 结果:[1,2,3,4,5,6]

    * @return void

    */

    private static void testFlatMap() {

       Stream<List<Integer>> inputStream = Stream.of(Arrays.asList(1), Arrays.asList(2, 3), Arrays.asList(4, 5, 6));

       Stream<Integer> outputStream = inputStream.flatMap((childList) -> childList.stream());

       System.out.println(JSONObject.toJSONString(outputStream.toArray()));

    }

 

 

 

filter

filter 方法用于通过设置的条件过滤出元素。以下代码片段使用 filter 方法过滤出空字符串:

    private static void testFilter() {

       List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");

       // 获取空字符串的数量

       //2

       long count = strings.stream().filter(string -> string.isEmpty()).count();

       System.out.println(count);

       //留下长度大于3

       //["abcd"]

       List<String> finalStr = strings.stream().filter(string -> string.length()>3).collect(Collectors.toList());

       System.out.println(JSONObject.toJSONString(finalStr));

    }

 

 

limit-skip

limit 返回 Stream 的前面 n 个元素;skip 则是扔掉前 n 个元素(它是由一个叫 subStream 的方法改名而来)

/**

* @Title: testLimitAndSkip

* @author: lance.lan

* @Description: limit 返回 Stream 的前面 n 个元素;skip 则是扔掉前 n 个元素

* @return void

*/

public static void testLimitAndSkip() {

    List<Person> persons = new ArrayList<>();

    for (int i = 1; i <= 10000; i++) {

       Person person = new Person("name" + i, i);

       persons.add(person);

    }

    List<String> personList2 = persons.stream().map(Person::getName).limit(10).skip(3).collect(Collectors.toList());

    //[name4, name5, name6, name7, name8, name9, name10]

    //这是一个有 10000 个元素的 Stream,但在 short-circuiting 操作 limit skip 的作用下,

    //管道中 map 操作指定的 getName() 方法的执行次数为 limit 所限定的 10 次,而最终返回结果在跳过前 3 个元素后只有后面 7 个返回。

    //也就是getName执行了7. 也就是这个map循环执行了7

    System.out.println(personList2);

}

 

 

limit 和 skip 对 sorted 后的运行次数无影响

有一种情况是 limit/skip 无法达到 short-circuiting 目的的,就是把它们放在 Stream 的排序操作后,原因跟 sorted 这个 intermediate 操作有关:此时系统并不知道 Stream 排序后的次序如何,所以 sorted 中的操作看上去就像完全没有被 limit 或者 skip 一样。

public static void testLimitAndSkip() {

       List<Person> persons = new ArrayList();

        for (int i = 1; i <= 5; i++) {

        Person person = new Person(i, "name" + i);

        persons.add(person);

        }

       List<Person> personList2 = persons.stream().sorted((p1, p2) ->

       p1.getName().compareTo(p2.getName())).limit(2).collect(Collectors.toList());

       System.out.println(personList2);

    }

 

 

 

sorted

sorted 方法用于对流进行排序。以下代码片段使用 sorted 方法对输出的 10 个随机数进行排序:

public static void testSort() {

    //简单排序

    Random random = new Random();

    random.ints().limit(10).sorted().forEach(System.out::println);

    //对象排序

    List<Person> persons = new ArrayList<>();

    for (int i = 1; i <= 5; i++) {

       Person person = new Person("name" + i, i);

       persons.add(person);

    }

    List<Person> personList2 = persons.stream().sorted((p1, p2) -> p1.getName().compareTo(p2.getName())).limit(2)

           .collect(Collectors.toList());

    // [{"age":1,"name":"name1"},{"age":2,"name":"name2"}]

    // 这里会先对5个元素进行排序,在limit操作。

    // testLimitAndSkip有区别

    System.out.println(JSONObject.toJSONString(personList2));

}

 

min/max/distinct

public static void testDistinct() {

    List<String> list = Arrays.asList("a", "b", "c", "a", "c");

    list = list.stream().distinct().collect(Collectors.toList());

    //[a, b, c]

    System.out.println(list);

}

 

public static void testMax() {

    List<String> list = Arrays.asList("ab", "bbb", "cbbb", "a", "c");

    int longest = list.stream().mapToInt(item -> item.length()).max().getAsInt();

    //4

}  

 

Match

Stream 有三个 match 方法,从语义上说:

  1. allMatch:Stream 中全部元素符合传入的 predicate,返回 true
  2. anyMatch:Stream 中只要有一个元素符合传入的 predicate,返回 true
  3. noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true

它们都不是要遍历全部元素才能返回结果。例如 allMatch 只要一个元素不满足条件,就 skip 剩下的所有元素,返回 false。

public static void testMatch() {

    List<Person> persons = new ArrayList<>();

    persons.add(new Person("name" + 1, 10));

    persons.add(new Person("name" + 2, 21));

    persons.add(new Person( "name" + 3, 34));

    persons.add(new Person( "name" + 4, 6));

    persons.add(new Person( "name" + 5, 55));

    boolean isAllAdult = persons.stream().allMatch(p -> p.getAge() > 18);

    System.out.println("All are adult? " + isAllAdult);

    boolean isThereAnyChild = persons.stream().anyMatch(p -> p.getAge() < 12);

    System.out.println("Any child? " + isThereAnyChild);

}

 

 

Collectors

Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串:

private static void testCollectors() {

    List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");

    List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());

     

    System.out.println("筛选列表: " + filtered);

    String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));

    System.out.println("合并字符串: " + mergedString);

}

筛选列表: [abc, bc, efg, abcd, jkl]

合并字符串: abc, bc, efg, abcd, jkl

 

统计

另外,一些产生统计结果的收集器也非常有用。它们主要用于int、double、long等基本类型上,它们可以用来产生类似如下的统计结果。

private static void tongji() {

    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());

}

列表中最大的数 : 7

列表中最小的数 : 2

所有数之和 : 25

平均数 : 3.5714285714285716

 

toArray

private static void testToArray() {

    List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);

    Object[] numberObj = numbers.stream().toArray();

    Integer[] numberStr = numbers.stream().toArray(Integer[] :: new);

}

 

结束语

总之,Stream 的特性可以归纳为:

  • 不是数据结构
  • 它没有内部存储,它只是用操作管道从 source(数据结构、数组、generator functionIO channel)抓取数据。
  • 它也绝不修改自己所封装的底层数据结构的数据。例如 Stream filter 操作会产生一个不包含被过滤元素的新 Stream,而不是从 source 删除那些元素。
  • 所有 Stream 的操作必须以 lambda 表达式为参数
  • 不支持索引访问
  • 你可以请求第一个元素,但无法请求第二个,第三个,或最后一个。不过请参阅下一项。
  • 很容易生成数组或者 List
  • 惰性化
  • 很多 Stream 操作是向后延迟的,一直到它弄清楚了最后需要多少数据才会开始。
  • Intermediate 操作永远是惰性化的。
  • 并行能力
  • 当一个 Stream 是并行化的,就不需要再写多线程代码,所有对它的操作会自动并行进行的。
  • 可以是无限的
    • 集合有固定大小,Stream 则不必。limit(n) findFirst() 这类的 short-circuiting 操作可以对无限的 Stream 进行运算并很快完成。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章