Java 8 JDK 1.8
一、速度更快
1、底层数据数据结构的改变
核心之一就是 HashMap,将原来的 数组-链表存储方式 转化为 数组–链表–红黑树,当链表长度超过 8,总容量超过 64,则将链表自动转化为红黑树。这将很大的提升数据的删改查以及当数组扩容后数据存放位置的速率。
相应改变的也有 ConcurrentHashMap,它取消了并发级别,而是改用 CAS 算法,它为底层支持的算法,效率要比锁高尚很多。
数组的扩容因子为 0.75,当数组 75% 的位置都存放有数据后,数组以二次幂的大小增长。而存放在红黑树中的数据无需通过哈希算法再次计算 Hash code 而是根据情况选择保持在原地或将其存放原索引+旧容量的位置。例如原存放位置为 5,原容量为16,扩展为 32 位,则现在存储的位置为 21。
2、内存结构的改变
将堆中的 永久区(加载类信息)的方法剥离出来,将其转换成 Metaspace(元空间),去除堆的永久区,元空间改为使用 物理内存。改变的同时 JVM 调优的两个参数–>PremGenSize 及 MaxPremGenSize 也替换成 MeatSpaceSize 及 MaxMetaSpaceSize。由于元空间使用物理内存,大大的增加了空间,而垃圾回收机制则是在空间将要满时,进行垃圾回收,这将很大的减小回收方法的几率,提高了垃圾回收机制的效率。OutOfMemoryError 发生的概率也同时减小,加快编程的效率。
二、接口的默认方法与静态方法
public interface DefaultFunctionInterface {
/**
* default 在接口中定义默认方法,也可以提供默认的实现。
*/
default String defaultFunction() {
return "default function";
}
/**
* static 在接口中定义静态方法,也可以提供实现。
* static 方法不能被继承,也不能被实现类调用,只能被自身调用。
*/
static String staticFunction() {
return "static function";
}
}
接口的默认方法和静态方法的引入,其实可以认为引入了 C++ 中抽象类的理念,以后再也不用在每个实现类中都写重复的代码了。
三、Lambda 表达式
Lambda 将 函数式编程 引入了 Java。Lambda 允许把函数作为一个方法的参数,或者把代码看成数据。
一个 Lambda 表达式可以由用逗号分隔的参数列表,–> 符号与函数体三部分表示。
Arrays.asList( "p", "k", "u","f", "o", "r","k").forEach( e -> System.out.println(e)); // System.out::println;
为了使现有函数更好的支持 Lambda 表达式,Java 8 引入了函数式接口的概念。函数式接口就是只有 一个方法 的普通接口。java.lang.Runnable 与 java.util.concurrent.Callable 是函数式接口最典型的例子。Java 8 增加了一种特殊的注解 @FunctionalInterface。
1、基本语法:
<函数式接口> <变量名> = (参数1,参数2...) -> {
//方法体
}
说明:(参数1,参数2…) 参数列表;-> Lambda (箭头)运算符;{} 方法体
- -> 右边的类型会根据左边的函数式接口类型自动推断;
- 如果形参列表为空,只需保留 ();
- 如果形参只有 1 个,() 可以省略,只需要参数的名称即可;
- 如果执行语句只有 1 条语句,且无返回值,{} 可以省略,若有返回值,则若想省去 {},则必须同时省略 return,且执行语句也保证只有 1 条语句;
- 形参列表的数据类型会自动推断;
- lambda 不会生成一个单独的内部类文件;
- lambda 表达式若访问了局部变量,则局部变量必须是 final 的,若是局部变量没有加 final
关键字,系统会自动添加,此后在修改该局部变量,会报错。
2、函数式接口
如果一个接口只有 一个抽象方法,则该接口称之为 函数式接口,因为 默认方法 不算抽象方法,所以也可以给函数式接口添加默认方法。
函数式接口可以使用 Lambda 表达式,lambda 表达式会被匹配到这个抽象方法上,可以将 lambda 表达式当作任意只包含一个抽象方法的接口类型,确保接口一定达到这个要求,只需要给接口添加 @FunctionalInterface 注解,编译器如果发现标注了这个注解的接口有多于一个抽象方法的时候会报错的。
@FunctionalInterface
interface Converter<F, T> {
T convert(F from);
}
Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
Integer converted = converter.convert("123");
System.out.println(converted); // 123
3、接口 – 接口实现类 – 匿名内部类 – Lambda 表达式
package gtb.java8.lambda;
import org.junit.Test;
import java.util.*;
import java.util.function.Predicate;
public class LambdaTest {
@Test
public void test1() {
// 匿名内部类
Comparator<Integer> com = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
// Lambda 表达式
Comparator<Integer> com2 = (x, y) -> Integer.compare(x, y);
Comparator<Integer> com3 = Comparator.comparingInt(x -> x);
// 方法引用
Comparator<Integer> com4 = Integer::compare;
}
// 需求:筛选满足条件的记录
// 0、每一种需求分别用 for 实现
@Test
public void test2() {
ArrayList<Employee> target = new ArrayList<>();
for (Employee e : employees) {
if (e.getAge() > 20) target.add(e);
}
target.forEach(System.out::println);
}
// 1、策略设计模式
@Test
public void test3() {
List<Employee> list = filterEmployees(employees, new FilterEmployeeByAge());
for (Employee employee : list) {
System.out.println(employee);
}
System.out.println("------------------------");
List<Employee> list1 = filterEmployees(employees, new FilterEmployeeBySalary());
for (Employee employee : list1) {
System.out.println(employee);
}
}
public List<Employee> filterEmployees(List<Employee> list, Predicate<Employee> mp) {
List<Employee> emps = new ArrayList<>();
for (Employee employee : list) {
if (mp.test(employee)) {
emps.add(employee);
}
}
return emps;
}
// 2、匿名内部类
@Test
public void test4() {
List<Employee> list = filterEmployees(employees, new Predicate<Employee>() {
@Override
public boolean test(Employee employee) {
return employee.getAge() >= 25;
}
});
for (Employee employee : list) {
System.out.println(employee);
}
}
// 3、lambda 表达式
@Test
public void test5() {
List<Employee> list = filterEmployees(this.employees, o -> o.getAge() >= 25);
for (Employee e : list) {
list.forEach(System.out::println);
}
}
// 4、stream
@Test
public void test6() {
employees.stream()
.filter((e) -> e.getSalary() >= 1200)
.limit(2)
.forEach(System.out::println);
employees.stream()
.map(Employee::getName)
.forEach(System.out::println);
}
List<Employee> employees = Arrays.asList(
new Employee("张三", 18, 1000),
new Employee("李四", 19, 2000),
new Employee("王五", 30, 1500),
new Employee("赵六", 35, 1800),
new Employee("田七", 24, 3000)
);
public class Employee {
private String name;
private int age;
private double salary;
public Employee() {
}
public Employee(String name, int age, double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public String toString() {
return "Employee{name = " + name + ", age = " + age + ", salary = " + salary + "}";
}
}
public class FilterEmployeeByAge implements Predicate<Employee> {
@Override
public boolean test(Employee t) {
return t.getAge() >= 25;
}
}
public class FilterEmployeeBySalary implements Predicate<Employee> {
@Override
public boolean test(Employee t) {
return t.getSalary() >= 1200;
}
}
}
3、FunctionalInterface
- Consumer 消费型接口 void accept(T t); 有去无回
- Supplier 供给型接口 T get(); 无中生有
- Function<T, R> 函数型接口 R apply(T t); 加工
- Predicate 断定型接口 boolean test(T t); 断定
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
public class LambdaTest5 {
// Consumer<T> 消费型接口 void accept(T t); 有去无回
@Test
public void test1() {
happy(10000, System.out::println);
}
public void happy(double money, Consumer<Double> con) {
con.accept(money);
}
// Supplier<T> 供给型接口 T get(); 无中生有
@Test
public void test2() {
List<Integer> numList = getNumList(10, () -> (int) (Math.random() * 100));
System.out.println(numList.toString());
}
// 需要:产生一些整数存入集合中
public List<Integer> getNumList(int num, Supplier<Integer> sup) {
ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < num; i++) {
Integer n = sup.get();
list.add(n);
}
return list;
}
// Function<T, R> 函数型接口 加工
@Test
public void test3() {
String s = strHandler("\t\t\t abc ", str -> str.trim());
System.out.println(s);
}
// 需要:用于处理字符串
public String strHandler(String str, Function<String, String> fun) {
return fun.apply((str));
}
// Predicate<T> 断言型接口 断定
@Test
public void test4() {
List<String> list = Arrays.asList("hello", "Lambda", "www", "ok");
List<String> strings = filterStr(list, s -> s.length() > 3);
strings.forEach(System.out::println);
}
// 需要:将满足条件的字符串放入集合中
public List<String> filterStr(List<String> list, Predicate<String> pre) {
ArrayList<String> strList = new ArrayList<>();
for (String s : list) {
if (pre.test(s)) {
strList.add(s);
}
}
return strList;
}
}
四、方法引用
若 Java 类或对象的方法已经实现了 Lambda 表达式的方法体 ,可以直接方法引用。
1、引用实例方法
<函数式接口> <变量名> = <实例>::<实例方法名>
//调用
<变量名>.接口方法([实际参数...])
将调用方法时的传递的实际参数,全部传递给引用的方法,执行引用的方法;
2、引用类方法
<函数式接口> <变量名> = <类>::<类方法名称>
//调用
<变量名>.接口方法([实际参数...])
将调用方法时的传递的实际参数,全部传递给引用的方法,执行引用的方法;
3、引用类的实例方法
定义、调用接口时,需要多传递一个参数,并且参数的类型与引用实例的类型一致。
//定义接口
interface <函数式接口>{
<返回值> <方法名>(<类><类名称>,[其他参数...]);
}
<函数式接口> <变量名> = <类>::<类实例方法名>
//调用
<变量名>.接口方法(类的实例,[实际参数...])
将调用方法时的传递的实际参数,从第二个参数开始(第一个参数指定的类的实例),全部传递给引用的方法,执行引用的方法;
4、引用构造器方法
<函数式接口> <变量名> = <类>::<new>
//调用
<变量名>.接口方法([实际参数...])
把方法的所有参数全部传递给引用的构造器,根据参数类型自动推断调用的构造器方法;
import org.junit.Test;
import java.io.PrintStream;
import java.util.Comparator;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public class MethodRefTest {
// 1.1 对象::实例方法名
@Test
public void test1() {
// Consumer<String> con = x -> System.out.println(x);
PrintStream ps = System.out;
Consumer<String> con = ps::println;
con.accept("abcdf");
}
@Test
public void test2() {
Employee emp = new Employee();
Supplier<String> sup = () -> emp.getName();
String s = sup.get();
System.out.println(s);
Supplier<Integer> sup2 = emp::getAge;
Integer i = sup2.get();
System.out.println(i);
}
// 1.2 类::静态方法名
@Test
public void test3() {
Comparator<Integer> com = (x, y)-> Integer.compare(x, y);
Comparator<Integer> com1 = Integer::compare;
}
// 1.3 类::实例方法名
// 实例方法用类名调用:第一个参数是实例方法的调用者,第二个参数是实例方法的参数时。
@Test
public void test4() {
BiPredicate<String, String> bp = (x, y) -> x.equals(y);
BiPredicate<String, String> bp2 = String::equals;
}
// 2 构造器引用
@Test
public void test5() {
Supplier<Employee> sup = () -> new Employee();
Supplier<Employee> sup1 = Employee::new;
Employee emp = sup1.get();
System.out.println(emp);
}
// 3 数组引用
@Test
public void test6() {
Function<Integer, String[]> fun = x -> new String[x];
String[] strs = fun.apply(10);
System.out.println(strs.length);
Function<Integer, String[]> fun1 = String[]::new;
String[] s = fun1.apply(20);
System.out.println(s.length);
}
}
// ArrayBuilder:
@FunctionalInterface
public interface ArrayBuilder {
// 创建 int 类型数组的方法,参数传递数组的长度,返回创建好的 int 类型数组
int[] builderArray(int length);
}
DemoArrayBuilder:
public class DemoArrayBuilder {
public static int[] createArray(int length, ArrayBuilder ab){
return ab.builderArray(length);
}
public static void main(String[] args) {
int[] arr1 = createArray(5, length -> new int[length]);
System.out.println(arr1.length);
int[] arr2 = createArray(10, int[]::new);
System.out.println(Arrays.toString(arr2));
System.out.println(arr2.length);
}
}
五、Stream API
Stream API 是把真正的 函数式编程 风格引入到 Java 中。她其实是一连串支持连续、并行聚集操作的元素。从语法上,很像 linux 的管道、或者 链式编程。
流是数据渠道,用于操作数据源(集合、数组等)生成的元素序列。
List<String> list = List.of("陈奕迅", "陈小春", "钟汉良", "陈七", "陈伟霆");
// 筛选陈开头,名字是三个字的
list.stream().filter(str->str.startsWith("陈"))
.filter(str->str.length() == 3)
.forEach(System.out::println);
Stream 特点
- Stream 按需计算;
- Stream 不改变源对象;
- Stream 延迟执行。
中间操作 – Pipelining:
中间操作都会返回流对象本身。 多个操作可以串联成一个管道,形成一条流水线。 可以对操作进行优化, 比如 延迟执行(laziness) 和短路( short-circuiting)。除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。
内部迭代:
以前对集合遍历都是通过 Iterator 或者增强 for 的方式,显式的在集合外部进行迭代, 这叫做 外部迭代。 Stream 提供了内部迭代的方式,流可以直接调用遍历方法。
Stream 代表的是任意 Java 对象的序列,且 stream 输出的元素可能并没有预先存储在内存中,而是实时计算出来的。它可以产生有限个或无限个元素。
Stream 操作的三个步骤
- 创建 Stream:一个数据源(例如:set 、list),获取一个流;
- 中间操作:一个中间操作连接,对数据源的数据进行处理;
- 终止操作: 一个终止操作,执行中间操作链,产生结果。
注意:“Stream 流”其实是一个集合元素的 函数模型,它并不是集合,也不是数据结构,其本身并不存储任何元素(或其地址值)。
1、获取流
java.util.stream.Stream 是 Java8 常用的流接口。
① Collection
java.util.Collection 接口中加入了 default 方法 stream 用来获取流,所有实现类均可获取流。
stream():获取串行流
parallelStream():获取并行流
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream(); // 返回一个顺序流
Set<String> set = new HashSet<>();
Stream<String> stream2 = set.stream();
Stream<String> parallelStream = list.parallelStream(); // 返回一个并行流(可多线程)
② Map
java.util.Map 接口不是 Collection 的子接口,且其 K-V 数据结构不符合流元素的单一特征,所以获取对应的流需要分 key、 value 或 entry 等情况:
Map<String,String> map = new HashMap<>();
Stream<Map.Entry<String, String>> stream1 = map.entrySet().stream();
Stream<String> stream2 = map.keySet().stream();
Stream<String> stream3 = map.values().stream();
③ 数组 和 静态方法 of
Arrays.stream() ,Stream.of() :
String[] array = {"陈奕迅","钟汉良","杨千嬅"};
Stream<String> stream1 = String.stream(array);
Stream<String> stream2 = Stream.of("陈奕迅","钟汉良","杨千嬅");
Stream<String> stream3 = Stream.of(array);
④ 获取无限流
Stream<Integer> stream1 = Stream.iterate(0, x -> x + 2); // 迭代 偶数序列
Stream<Double> stream2 = Stream.generate(() -> Math.random()); // 生成
所有的 Collection 集合都可以通过 stream 默认方法获取流;
Stream 接口的静态方法 of 可以获取数组对应的流,参数是一个可变参数,支持数组。。
2、流常用方法
延迟方法 (中间操作):返回值类型仍然是 Stream 接口自身类型的方法,支持链式调用。
map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered
终止操作:一个终止操作,执行中间操作链,产生结果。
forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator
2.1 筛选与切片
① 过滤:filter 使用 Predicate 接口
可以通过 filter 方法将一个流转换成另一个子集流。
Stream<T> filter(Predicate<? super T> predicate);
java.util.stream.Predicate 函数式接口唯一的抽象方法 boolean test(T t);
Stream<String> stream = Stream.of("陈奕迅", "陈伟霆", "陈七", "钟汉良")
.filter(str -> {
System.out.println("短路"); // 后面只迭代满足条件的
return str.startsWith("陈");
});
stream.forEach(System.out::println);
注意:Stream 属于管道流,只能被消费一次。
Stream 流调用完毕方法,数据就回流到下一个 Steam 上,而这时第一个 Stream 流已经使用完毕,就会关闭了,所以第一个 Stream 流就不能再调用方法了。
② 取用前几个:limit
Stream<T> limit(long maxSize);
String[] str = {"1","2","3","4","5"};
Stream.of(str)
.limit(3)
.forEach(System.out::println);
③ 跳过前几个:skip
Stream<T> skip(long n);
如果流的当前长度大于 n,则跳过前 n 个;否则将会得到一个长度为 0 的空流
Stream.of("1","2","3","4","5")
.skip(3)
.forEach(System.out::println);
④ distinct
筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素。
2.2 映射
⑤ 映射:map 使用 Function 接口
接收 lambda,将元素转换成其他形式或提取信息,接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。即将流中的元素映射到另一个流中。
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
java.util.stream.Function 函数式接口,其中唯一的抽象方法为: R apply(T t);
这可以将一种 T 类型转换成为 R 类型,而这种转换的动作,就称为“映射”。
Stream<String> stream = Stream.of("1", "2", "3");
Stream<Integer> integerStream = stream.map(str -> Integer.parseInt(str));
integerStream.forEach(i-> System.out.println(i));
List<Person> list = Arrays.asList(new Person("张三",24), new Person("李四",24));
list.stream().map(Person::getName).forEach(System.out::println);
⑥ flatMap
接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。即对 流扁平化处理,浅显一点解释就是把几个小的 list 转换到一个大的 list。
如:[[‘a’,‘b’],[‘c’,‘d’]] - > [‘a’,‘b’,‘c’,‘d’]
如果使用常用的 map() 方法获取的数据结构为:[[‘a’,‘b’,‘c’],[‘m’,‘d’,‘w’],[‘k’,‘e’,‘t’]]。
如果要得到如:[‘a’,‘b’,‘c’,‘m’,‘d’,‘w’,‘k’,‘e’,‘t’] 这样数据结构的数据,就需要使用 flatMap() 方法。
@Test
public void test() {
List<String> list = Arrays.asList("ab", "cd");
Stream<Stream<Character>> streamStream = list.stream().map(类名::filterCharacter);
streamStream.forEach((stream) -> stream.forEach(System.out::println));
// 使用 flatMap
Stream<Character> characterStream = list.stream().flatMap(类名::filterCharacter);
characterStream.forEach(System.out::println);
}
public static Stream<Character> filterCharacter(String str) {
List<Character> list = new ArrayList<>();
for (Character c : str.toCharArray()) {
list.add(c);
}
return list.stream();
}
mapToInt
stream() 中的 map(Function mapping) 返回给定函数应用于此流得元素得结果
stream() 中的 mapToInt(ToIntFunction mapper) 返回一个 IntStream 其中包含给定函数应用于此流得元素的结果
mapToInt 有 sum() 求和方法
2.3 排序
⑦ 排序:sorted
以自然序或着自定义 Comparator 接口排序规则来排序一个流。
1、sorted() 默认使用自然序排序, 其中的元素必须实现 Comparable 接口;
2、sorted(Comparator<? super T> comparator) :可以使用 lambada 来创建一个 Comparator 实例。
// 自然序排序一个 list
list.stream().sorted();
// 自然序逆序元素,使用 Comparator 提供的 reverseOrder() 方法
list.stream().sorted(Comparator.reverseOrder());
// 使用 Comparator 来排序一个 list 的顺序,使用 Comparator 提供的 reverseOrder() 方法
list.stream().sorted(Comparator.comparing(Student::getAge).reversed());
Stream.sorted 多字段排序
通过 Comparator.thenComparing(Comparator<? super T> other) 实现
- 先以年龄升序
- 当年龄相同时,在以零花钱升序
List<userInfo> userList = userList.stream()
.sorted(Comparator.comparing(userInfo::getAge)
.thenComparing(userInfo::getMoney))
.collect(Collectors.toList());
Stream.sorted 多字段排序区分降序和升序
通过 Comparator.thenComparing(Comparator<? super T> other) 实现多字段排序,并且使用Comparator.reverseOrder() 实现降序和升序
1.先以年龄升序
2.当年龄相同时,在以零花钱降序 Comparator.reverseOrder()
List<userInfo> userList = userList.stream()
.sorted(Comparator.comparing(userInfo::getAge)
.thenComparing(userInfo::getMoney, Comparator.reverseOrder()))
.collect(Collectors.toList());
2.4 Stream 终止操作
查找与匹配
allMatch 检查是否匹配所有元素
anyMatch 检查是否至少匹配一个元素
noneMatch 检查是否没有匹配所有元素
findFirst 返回第一个元素
findAny 返回当前流中的任意元素
count 返回流中元素的总个数
max 返回流中最大值
min 返回流中最小值
⑧ 归约 reduce
将流中元素反复结合起来,得到一个值
@Test
public void test() {
sum(1, 2, 3, 4, 5);
}
@Test
public void test1() {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Integer sum = list.stream().reduce(0, (x, y) -> x + y);
System.out.println(sum);
}
private static void sum(Integer... nums) {
Stream.of(nums).reduce(Integer::sum).ifPresent(System.out::println);
}
⑨ 组合:concat
如果有两个流,希望合并成为一个流,那么可以使用 Stream 接口的静态方法 concat
static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
注意:这是一个静态方法,与 java.lang.String 当中的 concat 方法不同
Stream<String> stream1 = Stream.of("陈奕迅", "陈伟霆", "陈七", "钟汉良");
String[] arr = {"1","2","3"};
Stream<String> stream2 = Stream.of(arr);
Stream<String> concatStream = Stream.concat(stream1, stream2);
concatStream.forEach(str-> System.out.println(str));
⑩ 逐一处理:forEach 终结方法 使用 Consumer 接口
void forEach(Consumer<? super T> action); // 方法签名
该方法接收一个 Consumer 接口函数,会将每一个流元素交给该函数进行处理。
Consumer 是一个消费型的函数式接口,可传递 lambda 表达式,消费数据。
Stream<String> stream = Stream.of("张三", "李四", "王五");
stream.forEach(str-> System.out.println(str));
⑪ 统计个数:count 终结方法
流提供 count 方法来数一数其中的元素个数,该方法返回一个 long 值代表元素个数。
System.out.println(Arrays.asList(1, 2, 3).stream().count());
⑪ Collect
将流转换为其他形式。接收一个 Collector 的实现,用于给 stream 中元素做汇总的方法
@Test
public void test() {
List<Integer> list = Stream.of(1, 2, 3, 4, 5).collect(Collectors.toList());
List<Integer> list1 = Stream.of(1, 2, 3, 4, 5).collect(LinkedList::new, List::add, List::addAll);
System.out.println(list.getClass()); // class java.util.ArrayList
System.out.println(list1.getClass()); // class java.util.LinkedList
}
@Test
public void test1() {
List<Person> list = Arrays.asList(new Person("张三", 24), new Person("李四", 25));
List<String> collect = list.stream().map(Person::getName).collect(Collectors.toList());
}
max() min()
1.Stream.min()
它根据提供的比较器(Comparator)返回此流的最小元素。还可使用 Stream.reduce 方法。
在 javadoc 中 min 方法声明
Optional<T> min(Comparator<? super T> comparator)
参数:传递比较器 (Comparator) 来比较元素。
返回:该方法返回提供最小元素或为空的选择。
异常:如果最小值为 null,则该方法抛出 NullPointerException。
2.Stream.max()
它根据提供的比较器(Comparator)返回此流的最大元素。
在 javadoc 中 max 方法声明
Optional<T> max(Comparator<? super T> comparator)
参数:传递比较器(Comparator)来比较元素。
返回:该方法返回提供最大元素或为空的选择。
异常:如果最大值为 null,则该方法抛出 NullPointerException。
3.在字符串(String)和整数(Integer)中如何使用 min 和 max 方法
下面是在字符串流和整数流中获取最小值和最大值的示例
List<Integer> numList = Arrays.asList(42, 44, 43, 41);
Comparator<Integer> comparator = Comparator.comparing(Integer::intValue);
Optional<Integer> minOptional = numList.stream().min(comparator);
minOptional.ifPresent(e -> System.out.println("Min: " + e));
Optional<Integer> maxOptional = numList.stream().max(comparator);
maxOptional.ifPresent(e -> System.out.println("Max: " + e));
System.out.println("---Min and Max for String---");
List<String> list = Arrays.asList("Mohit", "Nilesh", "Shankar", "Brajesh");
list.stream().min(Comparator.comparing(String::valueOf))
.ifPresent(e -> System.out.println("Min: " + e));
list.stream().max(Comparator.comparing(String::valueOf))
.ifPresent(e -> System.out.println("Max: " + e));
Min Max With Reduce
List<Integer> numList = Arrays.asList(42, 44, 43, 41);
//For min
numList.stream().reduce(Integer::min).ifPresent(s -> System.out.println(s)); //41
//For max
numList.stream().reduce(Integer::max).ifPresent(s -> System.out.println(s)); //44
System.out.println("---Min and Max for String---");
List<String> list = Arrays.asList("Mohit", "Nilesh", "Shankar", "Brajesh");
//For min
list.stream().reduce((s1, s2) -> {
if (s1.compareTo(s2) <= 0) {
return s1;
}
return s2;
}).ifPresent(s -> System.out.println(s)); //Brajesh
//For max
list.stream().reduce((s1, s2) -> {
if (s1.compareTo(s2) >= 0) {
return s1;
}
return s2;
}).ifPresent(s -> System.out.println(s)); //Shankar
4.在对象类型(Object)中使用Min和Max方法
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class MinMaxDemo {
public static void main(String[] args) {
List<User> users = Arrays.asList(
new User("Mahesh", 30),
new User("Krishna", 29),
new User("Virat", 28)
);
System.out.println("---Min and Max on the basis of user name---");
users.stream()
.min(Comparator.comparing(u -> u.getUserName()))
.ifPresent(e -> System.out.println("Min: " + e.getUserName()));
users.stream()
.max(Comparator.comparing(u -> u.getUserName()))
.ifPresent(e -> System.out.println("Max: " + e.getUserName()));
System.out.println("---Min and Max on the basis of age---");
users.stream()
.min(Comparator.comparing(User::getAge))
.ifPresent(e -> System.out.println("Min: " + e.getUserName()));
users.stream()
.max(Comparator.comparing(User::getAge))
.ifPresent(e -> System.out.println("Max: " + e.getUserName()));
}
}
class User {
private String userName;
private int age;
public User(String userName, int age) {
this.userName = userName;
this.age = age;
}
//Sets and Gets
}
5.在日期类型(Date)中使用Min和Max方法
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class MinMaxDemo {
public static void main(String[] args) {
LocalDate ld = LocalDate.parse("2020-05-15");
List<LocalDate> ldList = Arrays.asList(
ld.minus(10, ChronoUnit.DAYS), //2020-05-05
ld, //2020-05-15
ld.plus(15, ChronoUnit.DAYS) //2020-05-30
);
ldList.stream()
.min(Comparator.comparing(LocalDate::toEpochDay))
.ifPresent(e -> System.out.println("Min: " + e));
ldList.stream()
.max(Comparator.comparing(LocalDate::toEpochDay))
.ifPresent(e -> System.out.println("Max: " + e));
}
6.使用 IntStream, LongStream and DoubleStream
IntStream、LongStream 和 DoubleStream 的 min 和 max 方法在的 Javadoc 的声明。
For IntStream.
OptionalInt min()
OptionalInt max()
For LongStream.
OptionalLong min()
OptionalLong max()
For DoubleStream.
OptionalDouble min()
OptionalDouble max()
在上述方法中,不需要通过比较器。
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
public class MinMaxDemo4 {
public static void main(String[] args) {
System.out.println("--- Min and Max for IntStream ---");
IntStream.of(12, 20, 35, 48).min()
.ifPresent(i -> System.out.println(i));
IntStream.of(12, 20, 35, 48).max()
.ifPresent(i -> System.out.println(i));
System.out.println("--- Min and Max for LongStream ---");
LongStream.of(200, 300, 400).min()
.ifPresent(l -> System.out.println(l));
LongStream.of(200, 300, 400).max()
.ifPresent(l -> System.out.println(l));
System.out.println("--- Min and Max for DoubleStream ---");
DoubleStream.of(110.54, 230.57, 360.65).min()
.ifPresent(l -> System.out.println(l));
DoubleStream.of(110.54, 230.57, 360.65).max()
.ifPresent(l -> System.out.println(l));
}
}
4、sum()
流方式实现 基本数据类型 和 包装类型 的一位数组求和
public class Test {
public static void main(String[] args) {
// 基本数据类型 int long double
int[] arr2 = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int sum2 = Arrays.stream(arr2).sum();
long[] arr3 = new long[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
long sum3 = Arrays.stream(arr3).sum();
double[] arr5 = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
double sum5 = Arrays.stream(arr5).sum();
// 包装类型 Integer Long Double
Integer[] arr4 = new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
Integer sum4 = Arrays.stream(arr4).mapToInt(Integer::intValue).sum();
Long[] arr6 = new Long[]{1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L};
Long sum6 = Arrays.stream(arr6).mapToLong(Long::longValue).sum();
Double[] arr7 = new Double[]{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0};
Double sum7 = Arrays.stream(arr7).mapToDouble(Double::doubleValue).sum();
}
}
3、并行流和顺序流
并行流是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。
通过 parallel() 与 sequential() 可以实现并行流和顺序流之间的切换。
Fork/Join 框架:在必要的情况下,将一个大任务进行拆分(fork)成若干小任务(拆到不可再拆时),再将一个个小任务运算的结果进行 join 汇总。
@Test
public void test12() {
//顺序流
LongStream.rangeClosed(0,100).reduce(0, Long::sum);
//并行流
long reduce = LongStream.rangeClosed(0, 100).parallel().reduce(0, Long::sum);
//5050
System.out.println(reduce);
}
流(Stream)转换为数组(Array)
1.Using Stream.toArray(IntFunction)
2.Using Stream.toArray()
3.Using IntStream.toArray()
4.Using Collectors.toList()
最好的转换方法是使用 Stream.toArray(IntFunction) 方法,该方法将返回 Object[],然后将其更改为所需的数据类型。
IntStream.toArray() 将返回 int[];
LongStream.toArray() 返回 long[];
DoubleStream.toArray() 返回 double[]。
1.Using Stream.toArray(IntFunction)
toArray(IntFunction) 方法使用提供的生成器作为 IntFunction 返回包含此流元素的数组。这种方法是终端操作。
A[] toArray(IntFunction<A[]> generator)
参数:将生成器作为 IntFunction 传递,该函数生成所需类型和所提供长度的新数组。
返回:该方法返回由 stream 元素组成的数组。
异常:如果此流中任何元素的运行时类型不可分配给生成数组的运行时组件类型,则该方法将抛出 ArrayStoreException。
List<String> list = Arrays.asList("A", "B", "C", "D");
// 方法引用将 IntFunction 实例化为 toArray 方法中的生成器。
String[] strArray = list.stream().toArray(String[]::new);
// String[] strArray = list.stream().toArray(size -> new String[size]);
for(String s : strArray) {
System.out.println(s);
}
List<String> list = Arrays.asList("Krishna", "Mahesh", "Kush");
String[] strArray = list.stream()
.filter(e -> e.startsWith("K"))
.toArray(size -> new String[size]);
for(String s : strArray) {
System.out.println(s);
}
Integer[] arr = Stream.of("1","7","3","4")
.map(Integer::parseInt)
.sorted(Comparator.comparingInt(a -> a))
.toArray(Integer[]::new);
for (int i : arr) {
System.out.println(i);
}
2.Using Stream.toArray()
toArray() 方法返回一个包含此流元素的 Object 数组。这种方法是终端操作。
Object[] toArray()
例1:在这个例子中,我们将把一个字符串流转换成字符串数组。我们知道toArray()返回Object[],所以为了在我们需要的数据类型中转换它,我们可以使用
Object[] objArray = Stream.of("AA", "BB", "CC").toArray();
// 使用 Arrays.copyOf 方法,将数据类型转换。
String[] stArray = Arrays.copyOf(objArray, objArray.length, String[].class);
for(String s : stArray) {
System.out.println(s);
}
Object[] objArray = Stream.of(10, 20, 30, 40).toArray();
Integer[] intArray = Arrays.copyOf(objArray, objArray.length, Integer[].class);
for(Integer i : intArray) {
System.out.println(i);
}
3.Using IntStream.toArray()
IntStream 是 int-valued 元素的流。IntStream.toArray() 方法将 int 值流转换为 int 数组。
int[] toArray()
三种方式获得 IntStream 对象
IntStream intStream = IntStream.of(1,2,3,4,5);
IntStream intStream = IntStream.rangeClosed(1, 5);
IntStream intStream = Stream.of(4,5,6,7,8).mapToInt(i -> i);
int[] intArray = IntStream.of(10, 20, 30, 40).toArray();
for(Integer i : intArray) {
System.out.println(i);
}
int[] intArray = IntStream.rangeClosed(10, 15).toArray();
for(Integer i : intArray) {
System.out.println(i);
}
int[] intArray = Stream.of(3,4,5,6).mapToInt(i -> i * 2).toArray();
for(Integer i : intArray) {
System.out.println(i);
}
4.Using Collectors.toList()
可以将流转换为列表,然后将列表转换为数组。
collect(Collectors.toList()),流(Stream)上转化为 List;
List.toArray(), 列表(list)转换为数组(Array).
StreamToListToArray.java
package com.concretepage;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamToListToArray {
public static void main(String[] args) {
System.out.println("--- For String ---");
String[] ar = Stream.of("Java", "Angular", "Spring")
.collect(Collectors.toList())
.toArray(new String[0]);
for(String e : ar) {
System.out.println(e);
}
System.out.println("--- For Integer ---");
Integer[] intArray = Stream.of(15, 20, 30)
.map(e -> e * 2)
.collect(Collectors.toList())
.toArray(new Integer[0]);
for(Integer i : intArray) {
System.out.println(i);
}
}
}
参考文献
【1】Java doc: Stream
【2】Convert Java Stream to Array
————————————————
六、重复注解
在 Java 5 中使用注解有一个限制,即相同的注解在同一位置只能声明一次。Java 8 引入重复注解,这样相同的注解在同一地方也可以声明多次。重复注解机制本身需要用 @Repeatable 注解。Java 8 在编译器层做了优化,相同注解会以集合的方式保存,因此底层的原理并没有变化。
七、扩展注解的支持(类型注解)
Java 8 扩展了注解的上下文,几乎可以为任何东西添加注解,包括局部变量、泛型类、父类与接口的实现,连方法的异常也能添加注解。
private @NotNull String name;
八、Optional
Java 8 引入 Optional 类来防止空指针异常,Optional 类最先是由 Google 的 Guava 项目引入的。Optional 类实际上是个容器:它可以保存类型T的值,或者保存 null。使用 Optional 类就不用显式进行空指针检查了。
九、Date/Time API (JSR 310)
Java 8 新的 Date-Time API (JSR 310) 受 Joda-Time 的影响,提供了新的 java.time 包,可以用来替代 java.util.Date 和 java.util.Calendar。一般会用到 Clock、LocaleDate、LocalTime、LocaleDateTime、ZonedDateTime、Duration 这些类,对于时间日期的改进还是非常不错的。
十、JavaScript 引擎 Nashorn
Nashorn 允许在 JVM 上开发运行 JavaScript 应用,允许 Java 与 JavaScript 相互调用。
十一、Base64
在Java 8 中,Base64 编码成为了 Java 类库的标准。Base64 类同时还提供了对URL、MIME友好的编码器与解码器。
更好的类型推测机制
Java 8 在类型推测方面有了很大的提高,这就使代码更整洁,不需要太多的强制类型转换了。
编译器优化
Java 8 将方法的参数名加入了字节码中,这样在运行时通过反射就能获取到参数名,只需要在编译时使用 -parameters 参数。
并行(parallel)数组
支持对数组进行并行处理,主要是 parallelSort() 方法,它可以在多核机器上极大提高数组排序的速度。
并发(Concurrency)
在新增Stream机制与Lambda的基础之上,加入了一些新方法来支持聚集操作。
Nashorn 引擎 jjs
基于 Nashorn 引擎的命令行工具。它接受一些JavaScript源代码为参数,并且执行这些源代码。
类依赖分析器 jdeps
可以显示Java类的包级别或类级别的依赖。
JVM 的 PermGen 空间被移除
取代它的是Metaspace(JEP 122)。
Java8 Stream API 之 IntStream 用法全解
————————————————