Java8 新特性
Stream 流
概念
流是数据通道,用于操作处理(查找、过滤、筛选)数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算”。
- Stream 不会存储数据,也不会改变源对象;采用的内部迭代操作,会返回一个持有结果的新Stream;
- Stream 操作是延迟执行(Lazy),直到调用终止操作才会开始执行计算;
特点
- 流对数据源进行各种操作完成后,只能遍历一遍;
- 流对数据源的迭代操作与使用迭代器的显示迭代不同,而是采用内部迭代操作;
List<String> title = Arrays.asList("Java8", "In", "Action");
Stream<String> s = title.stream();
s.forEach(System.out::println);
s.forEach(System.out::println);
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.sourceStageSpliterator(AbstractPipeline.java:279)
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
at com.hp.blog.test.interfacetest.InterfaceTest.main(InterfaceTest.java:25)
操作
分为中间操作 和 终端操作。
-
中间操作
当数据源中的数据上了流水线后,这个过程对数据进行的所有操作都称为“中间操作”。
中间操作仍然会返回一个流对象,因此多个中间操作可以串连起来形成一个流水线。 -
终端操作
当所有的中间操作完成后,若要将数据从流水线上拿下来,则需要执行“终端操作”。
终端操作将返回一个执行结果,这就是你想要的数据。
代码示例
- 中间操作
public static void main(String[] args) {
List<Integer> intList = Arrays.asList(1,2,1,3,3,4,5,6,7,8,9,10);
List<String> strList = Arrays.asList("aaa","bbb","ccc","ddd","eee");
// filter() --> 接收Lambda,从流中排除某些元素
intList.stream()
.filter(e -> e >= 2)
.forEach(e -> System.out.print(e + "\t"));
// distinct() --> 筛选通过流产生元素的 hashcode() 和 equal() 去除重复元素
intList.stream()
.distinct()
.forEach(e -> System.out.print(e + "\t"));
// limit(long maxSize) --> 截断流,使其元素不超过给定数量
intList.stream()
.limit(4)
.forEach(e -> System.out.print(e + "\t"));
// skip(long n) --> 跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补
intList.stream()
.skip(5)
.forEach(e -> System.out.print(e + "\t"));
// map() --> 接收Lambda,将流中元素转换成其他类型或提取数据
strList.stream()
.map(String::toUpperCase)
.forEach(e -> System.out.print(e + "\t"));
// mapToInt、mapToDouble、mapToLong
intList.stream()
.mapToDouble(Integer::doubleValue)
.forEach(e -> System.out.print(e + "\t"));
// flatMap() --> 接收一个函数作为参数,将流中的每个值都转成另一个流,然后把所有流连接成一个流???
strList.stream()
.flatMap(testStreamAPI2::filterCharacter);
// sorted() --> 自然排序
intList.stream()
.sorted()
.forEach(e -> System.out.print(e + "\t"));
// sorted(Comparator com) --> 定制排序
intList.stream()
.sorted((x,y) -> y.compareTo(x))
.forEach(e -> System.out.print(e + "\t"));
}
- 终止操作
public static void main(String[] args) {
List<Integer> intList = Arrays.asList(1,2,1,3,3,4,5,6,7,8,9,10);
// forEach(Consumer c) --> 内部迭代
// min(Comparator c) --> 返回流中最小值
Integer min = intList.stream()
.filter(a -> a > 1)
.min(Integer::compareTo)
.get();
System.out.println(min);
// max(Comparator c) --> 返回流中的最大值
Integer max = intList.stream()
.filter(a -> a <= 3)
.max((Integer a, Integer b) -> a.compareTo(b))
.get();
System.out.println(max);
// findFirst --> 返回第一个元素
Integer first = intList.stream()
.findFirst()
.get();
System.out.println(first);
// findAny --> 返回当前流中的任意元素
Integer any = intList.stream()
.findAny()
.get();
System.out.println(any);
// count() --> 返回流中元素的总个数
Long count = intList.stream()
.filter(a -> a > 1)
.count();
System.out.println(count);
// reduce() --> 归约,将流中元素反复结合起来,得到一个值
Integer sum = intList.stream()
.reduce(0, (x, y) -> x + y);
System.out.println(sum);
// collect(Collector c) --> 收集,将流转化为其他形式,接收一个Collectors接口的实现,用于Stream中元素做汇总的方法
// Collectors接口中方法的实现决定了如何对流执行收集操作,静态方法toList(),toSet(),toCollection()
Set<Integer> collect = intList.stream()
.filter(e -> e > 4)
.collect(Collectors.toSet());
Iterator<Integer> iterator = collect.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
public static void main(String[] args) {
List<Person> personList = new ArrayList<>();
personList.add(new Person("Tom", 8900, 23, "male", "New York"));
personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
personList.add(new Person("Anni", 8200, 24, "female", "New York"));
personList.add(new Person("Owen", 9500, 25, "male", "New York"));
personList.add(new Person("Alisa", 7900, 26, "female", "New York"));
List<String> salaryNameList = personList.stream().filter(x -> x.getSalary() > 8000).map(Person::getName).collect(Collectors.toList());
System.out.println("薪资是否大于8000的员工姓名集合:" + salaryNameList);
Map<?, Person> salaryToMap = personList.stream().filter(x -> x.getSalary() > 8000).collect(Collectors.toMap(Person::getName, person -> person));
System.out.println("员工薪资大于8000的员工信息:" + salaryToMap);
Map<Boolean, List<Person>> salaryPart = personList.stream().collect(Collectors.partitioningBy(x -> x.getSalary() > 8000));
System.out.println("员工按薪资是否大于8000分两区域:" + salaryPart);
Map<String, List<Person>> sexGroup = personList.stream().collect(Collectors.groupingBy(Person::getSex));
System.out.println("员工按性别分组:" + sexGroup);
Map<String, List<Person>> areaGroup = personList.stream().collect(Collectors.groupingBy(Person::getArea));
System.out.println("员工按区域分组:" + areaGroup);
Map<String, Map<String, List<Person>>> sexAreaGroup = personList.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.groupingBy(Person::getArea)));
System.out.println("员工按性别分组后再按区域分组:" + sexAreaGroup);
}
public class Person {
private String name; // 姓名
private int salary; // 薪资
private int age; // 年龄
private String sex; //性别
private String area; // 地区
}
函数式接口
函数式接口:指接口只有一个抽象函数,接口的默认方法和静态方法不会破坏函数式接口定义,接口通过 @FunctionalInterface 注解标识,这样的接口可以隐式转化为 Lambda 表达式;
@FunctionalInterface
public interface Functional {
void method();
}
@FunctionalInterface
public interface FunctionalDefaultMethods {
void method();
default void defaultMethod() {
}
}
Lambda 表达式
Lambda 表达式(也称闭包)它允许开发者将函数当做方法参数传递给某个方法,或者函数本身作为数据处理操作,使得代码更加简洁。Lambda表达式是一个可传递的代码块,可以被一次或多次调用执行。
Lambda表达式 ( 参数 ) -> { 方法体 }
其中 () 用来描述参数列表,{} 用来描述方法体,-> Lambda运算符,读作goes to。
Arrays.asList("a", "b", "c").forEach(e -> System.out.println(e));
// 显式指定参数类型
Arrays.asList("a", "b", "d").forEach((String e) -> System.out.println(e));
// Lambda 执行更复杂的语句块,用 {} 包括起来,类似Java中函数体
Arrays.asList("a", "b", "d").forEach(e -> {
System.out.println(e);
System.out.print(e + "\t");
})
// Lambda 表达式可以引用类成员和局部变量,并将这些变量隐式转换为 final 修饰)
// Error: java: 从lambda 表达式引用的本地变量必须是最终变量或实际上的最终变量
String separator = ",";
separator = ".";
Arrays.asList("a", "b", "d").forEach(
(String e) -> System.out.print(e + separator));
// Lambda表达式有返回值,当方法体只有一行代码时,return语句可省略,返回值类型有Java编辑器推理得出
Arrays.asList("a", "b", "c").forEach(e1, e2 -> e1.compartTo(e2));
Arrays.asList("a", "b", "c").forEach(e1, e2 -> {
int result = e1.compartTo(e2);
return result;
});
接口的默认和静态方法
默认方法
- 默认方法允许开发者在接口提供默认方法实现(方法体),默认方法允许在不打破现有继承体系的基础上改进接口,默认方法可以使用实现类对象直接调用,或在实现类中对其进行覆盖重写后调用;
- 主要作用:1.接口升级,可以避免改变其他实现类。2.函数拼接
- 格式:public default 返回值方法名() {}
静态方法
- 因为静态方法不可以实例化,在接口中也是一样的,不需要实例化,直接使用,节省内存空间。
- 格式:public static 返回值 方法名() {}
- 注意:接口中静态方法和类中静态方法一样,只能通过接口.静态方法名的方式调用
public interface DefaultMethodInterface {
default String defaultTest() {
return "Default Implementation";
}
static String staticTest() {
return "Static Test";
}
}
public class DefaultMethodInterfaceImpl implements DefaultMethodInterface {}
public class OverridableImpl implements DefaultMethodInterface {
@Override
public String defaultTest() {
return "Override Implementation";
}
}
public class InterfaceTest {
public static void main(String[] args) {
// 默认方法
DefaultMethodInterfaceImpl defaulable = new DefaultMethodInterfaceImpl();
System.out.println(defaulable.defaultTest());
OverridableImpl overridable = new OverridableImpl();
System.out.println(overridable.defaultTest());
// 静态方法
System.out.println(DefaultMethodInterface.staticTest());
}
}