概述
lambda表达式
- 3.lambda表达式高级拓展
- 方法引用
- 1)静态方法引用的使用
- 2)实例方法引用的使用
- 3)构造方法引用的使用
- Stream
- 1)概述及演示
- 2)Stream API
- 3)Stream对象对集合处理
- 由批量数据得到Stream对象
- Stream对象对基本数据类型的底层封装
- Stream对象转换得到指定数据类型
- Stream中常见API操作
- Stream中对数字运算的支持
- 4)Stream性能
- 5)并行Stream的线程安全
3.lambda表达式高级拓展
方法引用
方法引用是结合lambda表达式的一组语法特性,在开发过程中方法引用配合lambda表达式可以对代码进行简化,但是相应的会损失掉一些可读性。
方法引用具体分为静态方法引用、实例方法引用和构造方法引用。首先创建测试用类,
@Data
@AllArgsConstructor
@NoArgsConstructor
class Person {
private String name, gender;
private int age;
// 静态方法比较 age
public static int compareAge(Person p1, Person p2) {
return p1.getAge() - p2.getAge();
}
// 实例方法比较 Name
public static int compareName(Person p1, Person p2) {
return p1.getAge().hashCode() - p2.getAge().hashCode();
}
}
此处使用 lombok语法支持,增加所有属性的构造方法和空构造方法。
1)静态方法引用的使用
- 原始形式:
类名.静态方法名()
- 引用形式:
类名::静态方法名
public class Test() {
public static void main(String[] args) {
List<Person> personList = new ArrayList<>();
personList.add(new Person("a", "male", 16));
personList.add(new Person("c", "female", 32));
personList.add(new Person("de", "female", 34));
// 使用匿名内部类的方式对 personList进行排序
Collections.sort(personList, new Comparator() {
@override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
});
System.out.println(personList);
// lambda表达式实现
Collections.sort(personList, (p1, p2) -> p1.getAge() - p2.getAge());
System.out.println(personList);
// 静态方法引用
Collections.sort(personList, Person::compareAge);
System.out.println(personList);
}
}
2)实例方法引用的使用
- 原始形式:
类实例对象.实例方法名()
- 引用形式:
类实例对象::实例方法名
public class Test() {
public static void main(String[] args) {
Person p = new Person();
List<Person> personList = new ArrayList<>();
personList.add(new Person("a", "male", 16));
personList.add(new Person("c", "female", 32));
personList.add(new Person("de", "female", 34));
// 实例方法引用
Collections.sort(personList, p::compareName);
System.out.println(personList);
}
}
3)构造方法引用的使用
与实例方法和静态方法不同,构造方法的引用需要绑定函数式接口。
创建一个函数式接口,用于初始化 Person类对象,
@FunctionalInterface
interface IPerson {
Person initPerson(String name, String gender, int age);
}
public class Test() {
public static void main(String[] args) {
// 构造方法引用,绑定函数式接口 IPerson
IPerson ip = Person::new;
Person a = ip.initPerson("a", "male", 16);
}
}
Stream
1)概述及演示
Stream流的引入是针对存储多个数据的容器(如数组、集合等)在批量处理数据过程中的繁杂操作提出的API,可以通过结合lambda表达式通过串行和并行两种方式完成对批量数据的增强操作。
public class Test() {
public static void main(String[] args) {
List<String> accounts = new ArrayList<>();
accounts.add("tom");
accounts.add("jerry");
accounts.add("beta");
// 用户名大于3才算有效账号
// 传统方式遍历集合
for (String account: sccounts) {
if (account.length() > 3)
System.out.println(account);
}
// 通过迭代器进行处理(实际上for循环就是使用了迭代器)
Iterator<String> it = accounts.iterator();
while(it.hasNext()) {
String account = it.next();
if (account.length() > 3)
System.out.println(account);
}
// 通过Stream流方式简化代码
List validAccounts = accounts.stream().filter(s -> s.length()>3).collect(Collectors.toList());
System.out.println(validAccounts);
}
}
首先通过stream
方法获取相应数据的流对象,之后通过filter
方法配合 lambda表达式对流中的数据进行过滤。最后以List的形式返回结果。
2)Stream API
Stream的处理流程可总结为,
数
据
源
−
>
数
据
转
换
−
>
获
取
结
果
数据源 -> 数据转换 -> 获取结果
数据源−>数据转换−>获取结果
获取Stream对象的方式有多种,举例如下
- 从集合中获取,
集合对象.stream()
或集合对象.parallelStream()
方法可获取集合的普通Stream对象和支持并发处理的Stream对象 - 从数组中获取,
Arrays.stream(T[] t)
方法可获取数组的Stream对象 - 通过缓冲流获取Stream对象,如
BufferReader对象.lines()
方法 - 通过静态工厂方法获取Stream对象,如 java.util.stream包中对基本类型都创建了流对象的构造器。java.nio.file中也提供了流对象的构造方法
- 自定义流对象
Stream API大致可分为两个主要操作方式和一个辅助操作方式,
- 中间操作API,intermediate操作
可以理解为逻辑处理,操作结果是一个Steam对象,一个流程中可以有多个连续的中间操作。中间操作只记录操作方式,不做具体执行,直到结束操作发生时才对数据最终执行。
**i.**无状态:数据处理时不受之前的中间操作影响。主要包含map/filter/parallel/sequential/unorder
等操作
**ii.**有状态:数据处理时,受到前置中间操作影响。主要包含distinct/sorted/limit/skip
等操作 - 结束操作API,terminal操作
一个Stream流对象的处理流程只能有一个结束操作。一旦这个操作被触发,就会开启数据处理的整个中间过程,最终生成结果。该过程是不可逆的。
**i.**非短路操作:当前的Stream对象必须处理完集合中所有数据才能得到处理结果。注意包含forEach/forEachOrdweed/toArray/reduce/collect/min/max/count/iterator
等
**ii.**短路操作:当前的Stream对象在处理过程中一旦满足某个条件,就可以得到结果。主要包含anyMatch/allMatch/noneMatch/findFirst/findAny
等
3)Stream对象对集合处理
由批量数据得到Stream对象
public class Test {
public static void main(String[] args) {
// 多个数据得到stream
Stream stream1 = Stream.of("adad", "dadads", "dewf");
// 由数组得到Stream对象
String[] strArrays = new String[] {"a", "c", "f"};
Stream stream2 = Arrays.stream(strArrays);
// 由列表得到Stream对象
List<String> list = new ArrayList<>();
list.add("dsad");
list.add("dahdi");
Stream stream3 = list.stream();
// 由集合得到Stream对象
Set<String> set = new HashSet<>();
set.add("dsad");
set.add("dahdi");
Stream stream4 = set.stream();
// 由Map得到Stream对象
Map<String> map = new HashMap<>();
map.put("dsad", 1000);
map.put("dahdi", 1200);
Stream stream5 = map.entrySet().stream();
}
}
Stream对象对基本数据类型的底层封装
jdk8中目前只针对基本类型中最常使用的 int、long和double类型进行了封装。以 int类型为例,
public class Test {
public static void main(String[] args) {
Stream stream = IntStream.of(new int[] {20, 30, 40});
stream.foreach(System.out::println);
IntStream.range(1, 5).forEach(System.out::println);
// 输出 1,2,3,4
}
}
Stream对象转换得到指定数据类型
public class Test {
public static void main(String[] args) {
// 由Stream对象得到数组
String[] strArrays = new String[] {"a", "c", "f"};
Stream stream2 = Arrays.stream(strArrays);
String strArr = stream2.toArray(String::new);
// 由Stream对象得到字符串,拼接对象中的元素
strArrays = new String[] {"a", "c", "f"};
stream2 = Arrays.stream(strArrays);
String str = stream2.collect(Collectors.joining()).toString();
// 由Stream对象得到列表
List<String> list = new ArrayList<>();
list.add("dsad");
list.add("dahdi");
Stream stream3 = list.stream();
List<String> strList = (List<String>) stream3.collect(Collectors.toList());
// 由Stream对象得到集合
Set<String> set = new HashSet<>();
set.add("dsad");
set.add("dahdi");
Stream stream4 = set.stream();
set<String> strSet = (Set<String>) stream3.collect(Collectors.toSet());
// 由Stream对象得到Map
strArrays = new String[] {"a", "c", "f"};
stream2 = Arrays.stream(strArrays);
Map<String, Integer> strMap = Map<String> stream2.collect(Collectors.toMap(x->x, y->"haha:"+y));
System.out.println(strMap);
// 得到 ["a"="haha:a", "b"="haha:b", "c"="haha:c"]
}
}
Stream中常见API操作
public class Test {
public static void main(String[] args) {
List<String> accountList = new ArrayList<>();
accountList.add("songjiang");
accountList.add("linchong");
accountList.add("luzhishen");
// map()中间操作,接收一个FunctionalInterface,对数据逐个操作
accountList = accountList.stream().map(x->"梁山好汉:"+x).collect(Collectors.toList());
// filter()过滤
accountList = accountList.stream().filter(x->x.length()>5).collect(Collectors.toList());
// forEach 增强for循环
accountList.forEach(x->System.out.println("forEach->"+x));
// 如果需要多次循环建议不要使用多次的forEach,因为多次调用forEach实际上是开启了多次Stream操作
// 建议使用peek()完成多次循环遍历,在一次遍历过程中完成所有步骤的操作(将多次循环合并)
accountList.stream().peek(x->"peek1:"+x)
.peek(x->"peek2:"+x).forEach(System.out::println);
}
}
Stream中对数字运算的支持
public class Test {
public static void main(String[] args) {
List<Integer> intList = new ArrayList<>();
intList.add(20);
intList.add(19);
intList.add(7);
intList.add(8);
intList.add(86);
intList.add(11);
intList.add(3);
intList.add(20);
// skip跳过部分数据
intList.stream().skip(3).forEach(System.out::println); // 跳过前三个数据
// limit显著输出数据数目
intList.stream().skip(3).limit(2).forEach(System.out::println);
// 跳过前三个数据且只对之后两个数据进行处理
// distinct剔除重复数据
intList.stream().distinct().forEach(System.out::println);
// sorted排序
intList.stream().sorted().forEach(System.out::println);
// max、min获取极值,需传入一个Comparator
Optional optional = intList.stream().max((x, y) -> x - y);
System.out.println(optional.get());
// reduce进行合并操作
optional = intList.stream().reduce((cumSum, x) -> cumSum + x);
System.out.println(optional.get());
}
}
4)Stream性能
- 在基本数据类型的操作上(如 ArrayList<Integer>)建议使用迭代器、增强for循环和普通for循环。如果是多核环境,可以使用
parallelStream()
并行处理能够有效提升性能 - 当面对复杂数据的处理时(如 ArrayList<className>),并行Stream在多核环境下可以有效提升数据处理的性能
5)并行Stream的线程安全
并行Stream对象底层原理是将大任务拆分为多个子任务执行。
public class Test {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for (int i=0; i<1000; i++) {
list.add(i);
}
// 将list中的元素复制到另一个List
// 串行Stream
List list2 = new ArrayList<>();
list.stream().forEach(x -> list2.add(x));
System.out.println(list.size());
System.out.println(list2.size());
// 并行Stream
List list3 = new ArrayList<>();
list.parallelStream().forEach(x -> list3.add(x));
System.out.println(list3.size());
}
}
上述代码的运行结果为,
1000
1000
995
出现了数据丢失的情况,原因是 ArrayList类并非是线程安全的类,且 Stream API中明确写明forEach
方法并不是线程安全的。解决这一问题可以使用 Stream API中提供的,在官方文档中明确说明的并行情况下保证线程安全的方法。如
public class Test {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for (int i=0; i<1000; i++) {
list.add(i);
}
// collect方法用于收集最终的Stream处理结果,是线程安全的方法
List<Integer> list4 = list.parallelStream().collect(Collectors.toList());
System.out.println(list4.size());
}
}
此外更简单的解决方法是使用线程安全的并发集合类。
最后
以上就是顺心小刺猬为你收集整理的Java8新特性:lambda表达式(下)3.lambda表达式高级拓展的全部内容,希望文章能够帮你解决Java8新特性:lambda表达式(下)3.lambda表达式高级拓展所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复