方法引用:
方法引用是结合lambda表达式的一种语法特性,方法引用通过方法的名字来指向一个方法。方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
方法引用可划分为:
- 静态方法引用
- 实例方法引用
- 构造方法引用
具体使用实例如下:
public class UserInfo {
/**
* 1、静态方法引用
* 2、实例方法引用
* 3、构造方法引用
*/
public static void main(String[] args) {
List<User> users = new ArrayList<>();
users.add(new User("lili", 18, "女"));
users.add(new User("tom", 17, "男"));
users.add(new User("lilei", 28, "男"));
users.add(new User("大张飞", 38, "男"));
users.add(new User("李逵", 19, "男"));
users.add(new User("貂蝉", 18, "女"));
// 匿名内部类实现的排序
Collections.sort(users, new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
return o1.getAge() - o2.getAge();
}
});
// lambda方式的实现排序
Collections.sort(users, (o1, o2) -> o1.getAge() - o2.getAge());
// 静态方法引用
// 类型名称 方法名称 -> 类型名称:方法名称
Collections.sort(users, User::compareByAge);
//实例方法引用
// 创建对应的对象 -> 对象的运用:方法名称
Collections.sort(users, new User()::compareByNameHashCode);
// 构造方法引用 需要绑定一个构造方法
IUser user = User::new;
User initUser = user.initUser("花和尚", 50, "男");
System.out.println(users);
}
}
class User {
private String name;
private int age;
private String gender;
// 定义一个静态方法
public static int compareByAge(User o, User o2) {
return o.age - o2.age;
}
// 定义一个普通的方法
public int compareByNameHashCode(User o1, User o2) {
return o1.getName().hashCode() - o2.getName().hashCode();
}
public User() {
}
public User(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
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 String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
'}';
}
}
interface IUser {
User initUser(String name, int age, String gender);
}
Stream的使用
Stream:Stream是java 8 中提供的一种新特性,新的抽象称为流Stream,可以让你以一种声明的方式处理数据。通过下面的代码可以 更好的体验Stream的好处。
public class App2 {
public static void main(String[] args) {
// 添加测试数据
List<Integer> nums = new ArrayList<>();
nums.add(1000);
nums.add(500);
nums.add(1500);
nums.add(600);
nums.add(550);
nums.add(700);
nums.add(650);
nums.add(800);
nums.add(30000);
// 使用普通的for循环来完成(底层是使用Iterator实现的迭代器)
for (Integer num : nums) {
if (num > 600) {
System.out.println(num);
}
}
// 使用普通的迭代是实现的
Iterator<Integer> it = nums.iterator();
while (it.hasNext()) {
Integer num = it.next();
if (num > 600) {
System.out.println(num);
}
}
// 使用lambda表达式实现的循环过滤
List<Integer> collect = nums.stream().filter(o -> o > 600).collect(Collectors.toList());
System.out.println(collect);
}
}
通过以上的循环过滤操作使用普通的for 和迭代器与实际操作的代码很少代码冗余较多,而使用stream和lambda表达式的结合可以较少代码冗余(仅仅只是编写与实际业务逻辑的代码)
Stream的Api详解
可以根据对数据操作影响分为
- intermeidadte中间|记录操作(有状态或无状态)中间操作的结果是一个stream对象,所以可以是一个或者多个连续的中间操作,中间操作只是做记录操作,不做具体的执行,直到数据结束操作时,才能获取数据的最终执行。
根据状态可分为:
1、有状态:数据处理过程中,受前置中间操作结果影响(distinct/sorted/limit/skip
)
2、无状态:数据处理过程中,不受前置中间操作结果的影响(map/filter/parallet/sequential/unodered
) - terminal终结|结束操作(非短路或短路)一个终结操作只能包含一个stream对象,一旦发生就会真正的操作数据,得到最终结果,并且结果还是不可逆的过程。
根据执行的过程可分为
1、非短路操作:当前的stream对象必须要处理完当前的所有数据才能够返回最终的处理结果。forEach/forEachOrdered/toArray/reduce/collect/max/min/count/iterator
2、短路操作:当前的stream对象不需要处理所有的数据,只要满足一定的条件就可以返回最终的结果。anyMatch/allMatch/noneMatch/findFirst/findAny
使用短路操作的场景就是将一个无线大的stream对象,返回成一个有限大的stream对象。
stream处理数据流程为:获取数据源 -> 数据转换(可以一次或多次) ->最终结果
如何获取stream对象
- 从集合或数组中获取
Collection.stream 如:nums.stream 获取一个并行的stream Collection.paralletStream 从数组中获取 Arrays.stream(T t)
- 从缓冲区中获取BufferRead
BufferReader.lines() -> stream
- 静态工厂
java.util.stream.IntStream.range() java.nio.file.Files.walk()
- 自定义构建
java.util.Spliterator
public class App3 {
public static void main(String[] args) {
// 如何获取stream对象
// 1 通过批量数据
Stream stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9);
// 2 通过数组
Integer[] arr = new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9};
Stream arrStream = Arrays.stream(arr);
// 通过列表
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
Stream<Integer> listStream = list.stream();
// 集合
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
Stream<Integer> setStream = set.stream();
// map
Map<Integer, Integer> map = new HashMap<>();
map.put(1, 1);
map.put(2, 2);
Stream<Map.Entry<Integer, Integer>> entryStream = map.entrySet().stream();
// 基本的数据类型
IntStream.of(new int[]{1, 2}).forEach(System.out::println);
IntStream.range(1, 10).forEach(System.out::println);
}
}
通过stream对象转换成指定的数据类型:
Stream stream = Stream.of("str", "ing", "hello", "world");
//转换成数组
Object[] arr = stream.toArray(Integer[]::new);
// 转成字符串
String str = stream.collect(Collectors.joining()).toString();
// 转成列表
List<String> strs = (List<String>) stream.collect(Collectors.toList());
// 转成集合
Set<String> set = (Set<String>) stream.collect(Collectors.toSet());
// 转成map
Map<String, String> map = (Map<String, String>) stream.collect(Collectors.toMap(x -> x, y -> y));
stream线程安全问题
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for (int i =0; i < 100000; i++) {
list.add(i);
}
List<Integer> list2 = new ArrayList<>();
// 串行的stream
list.stream().forEach(x -> list2.add(x));
System.out.println(list.size());
System.out.println(list2.size());
// 并行的stream
List<Integer> list3 = new ArrayList<>();
list.parallelStream().forEach(x -> list3.add(x));
System.out.println(list.size());
System.out.println(list3.size());
}
100000
100000
100000
39836
通过上面的代码,使用parallelStream时会出现线程之间相互竞争的问题,线程是不安全的。
stream的性能分析可以参考下面这篇文章。
https://www.cnblogs.com/carpenterlee/p/6675568.html