Stream
- Java 8 中Stream API详解
- Stream的作用
- Stream的性质
- 什么是聚合操作
- 举例:
- Stream总览
- 什么是流
- 流的构成
- 流的操作类型
- 流的使用详解
- 流的构造与转换
- 流的操作
- 筛选与切片
- 示例
- 映射
- 示例
- 排序
Java 8 中Stream API详解
Stream的作用
- Java8 中的Stream是对集合对象功能的增强,专注于对集合对象进行各种非常便利、高效的聚合操作,或者大批量数据操作
- Stream API借助于同样新出的Lambda表达式,极大的提高了编程效率和程序的可读性
- 同时提供串行和并行两种模式进行汇聚操作,并发模式可以充分利用多核处理器的优势,使用fork/join并行方式来拆分任务和加速处理过程。
- 无需编写一行多线程代码就可以方便的写出高性能的并发程序
Stream的性质
- 流(Stream):就是数据渠道,用于操作数据源(集合、数组)所生成的元素序列
- 集合注重的数据,流注重与计算
- Stream不会自己存储数据
- Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream
- Stream操作是延迟执行的。这意味着会等到需要结果的时候才执行。
什么是聚合操作
在传统J2EE的应用中,java代码经常不得不依赖于关系型数据库的聚合操作来完成诸如:
- 每日平均消费金额
- 最受欢迎的商品
- 取一定量的数据样本作为首页推荐
这类的操作。而在java集合API中,仅仅有极少量的辅助型方法,更多的时候需要用Iterator来遍历集合,完成相关的聚合应用逻辑。
举例:
获取数组最大值
public static void main(String[] args) {
int[] nums={1,2,3,4,5,6,7};
int max = Arrays.stream(nums).max().getAsInt();
System.out.println(max);
}
Stream总览
什么是流
Stream不是集合元素,不是数据结构并不保存数据,是有关算法和计算的,类似于一个高版本的Iterator。我们只需要给出需要对其包含的元素执行什么操作,Stream就会隐式地在内部进行遍历,做出相应的数据转换。
Stream如同一个迭代器,单向,不可往复,数据只能遍历一次,遍历一次后即用尽了。
Stream可以并行化操作,迭代器只能命令式地、串行化操作。
Stream的一大特点就是数据源本身可以是无限的。
流的构成
分为三个基本步骤
获取一个数据源——数据转化——执行操作获取先要的结构
每次转换原有的Stream对象不改变,返回一个新的Stream对象(可以有多次转化),这就允许对其操作可以向链条一样排列,变成一个管道
有多种方式生成Stream Source:
- 从Collection和数组
- Collection.stream()
- Collection.oarallelStream()
- Arrays.stream(T array)or Strean.of()
- 从BufferReader
- java.io.BUfferedReader.lines()
- 静态工程
- 自己创建
流的操作类型
- Intermediate:其主要目的是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。仅仅调用到这类方法,并没有真正开始流的遍历
- Terminal:一个流只能有一个terminal操作,其操作的执行才是真正开始流的遍历,并生成一个结果
流的使用详解
流的构造与转换
- 构造流的几种常见方法
public static void main(String[] args) {
// 1. Individual values
Stream stream = Stream.of("a", "b", "c");
// 2. Arrays
String [] strArray = new String[] {"a", "B", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
// 3. Collections
List<String> list = Arrays.asList(strArray);
stream = list.stream();
}
- 数值流的构造
IntStream.of(new int[]{1, 2, 3}).forEach(System.out::println);
IntStream.range(1, 3).forEach(System.out::println);
IntStream.rangeClosed(1, 3).forEach(System.out::println);
- 流转换为其他数据结构
// 1. Array
String[] strArray1 = stream.toArray(String[]::new);
// 2. Collection
List<String> list1 = stream.collect(Collectors.toList());
List<String> list2 = stream.collect(Collectors.toCollection(ArrayList::new));
Set set1 = stream.collect(Collectors.toSet());
Stack stack1 = stream.collect(Collectors.toCollection(Stack::new));
// 3. String
String str = stream.collect(Collectors.joining()).toString();
流的操作
- 多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何处理,而在终止操作时一次性全部处理。即惰性求值。Stream的中间操作时不会有任何结果数据输出的。
- Stream的中间操作可以在整体上分为:筛选与切片、映射、排序
筛选与切片
方法 | 描述 |
filter(Predicate p) | 接收Lambda表达式,从流中排除某些元素 |
distinct() | 筛选,通过流所生成元素的hashCode()和equals()去除重复元素 |
limit(long maxSize) | 截断流,使其元素不能超过给定数量 |
skip(long n) | 跳过元素,返回一个扔掉了前n个元素的流。若流中的元素不足n个,则返回一个空流与limit(n)互补 |
示例
- Employee类
public class Employee implements Serializable {
private String name;
private Integer age;
private Double salary;
}
- 构件对象数组
protected List<Employee> list = Arrays.asList(
new Employee("张三", 18, 9999.99),
new Employee("李四", 38, 5555.55),
new Employee("王五", 60, 6666.66),
new Employee("赵六", 8, 7777.77),
new Employee("田七", 58, 3333.33)
);
- Filter()方法
主要是接收Lambda表达式,从流中排除某些元素
//内部迭代:在此过程中没有进行过迭代,由Stream api进行迭代
//中间操作:不会执行任何操作
Stream<Person> stream = list.stream().filter((e) -> {
System.out.println("Stream API 中间操作");
return e.getAge() > 30;
});
- limit()方法
主要作用为:截断流,使其元素不超过给定数量
//过滤之后取2个值
list.stream().filter((e) -> e.getAge() >30 ).limit(2).forEach(System.out :: println);
在其中我们可以配合其他的中间操作,并截断流,使我们可以取得相应个数的元素。
- skip()方法
跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补
//跳过前2个值
list.stream().skip(2).forEach(System.out :: println);
- distinct()方法
筛选,通过流所生成元素的hashCode和equals()去除重复元素
list.stream().distinct().forEach(System.out :: println);
distict需要在实体中重写hashCode和equal方法才能使用
映射
方法 | 描述 |
map(Function f) | 接收一个函数,该函数会被引用到每个元素上,并将其映射成一个新的元素 |
mapToDouble(ToDoubleFunction f) | 接收一个函数,该函数会被引用到每个元素上,产生一个新的DoubleStream |
mapToInt(ToIntFunction f) | 接收一个函数,该函数会被引用到每个元素上,产生一个新的IntStream |
mapToLong(ToLongFunction f) | 接收一个函数,该函数会被引用到每个元素上,产生一个新的LongStream |
flagMap(function f) | 接收一个函数,将流中的每个值都换成另一个流,然后把所有流,连接成一个流。 |
示例
- map() 方法
//将流中每一个元素都映射到map的函数中,每个元素执行这个函数,再返回
List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
list.stream().map((e) -> e.toUpperCase()).forEach(System.out::printf);
//获取Person中的每一个人得名字name,再返回一个集合
List<String> names = this.list.stream().map(Person :: getName).collect(Collectors.toList());
- flagMap()
/**
* flatMap —— 接收一个函数作为参数,将流中的每个值都换成一个流,然后把所有流连接成一个流
*/
@Test
public void testFlatMap () {
StreamAPI_Test s = new StreamAPI_Test();
List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
list.stream().flatMap((e) -> s.filterCharacter(e)).forEach(System.out::println);
//如果使用map则需要这样写
list.stream().map((e) -> s.filterCharacter(e)).forEach((e) -> {
e.forEach(System.out::println);
});
}
/**
* 将一个字符串转换为流
*/
public Stream<Character> filterCharacter(String str){
List<Character> list = new ArrayList<>();
for (Character ch : str.toCharArray()) {
list.add(ch);
}
return list.stream();
}
其实map方法相当于Collection的add方法,如果add的是个集合的话会变为二维数组,而flatMap相当于Collection的addAll犯法
排序
方法 | 描述 |
sorted() | 产生一个新流,其按自然顺序排序 |
sort(Comparator comp) | 产生一个新流,其中按比较器书序排序 |
// 自然排序
List<Employee> persons = list.stream().sorted().collect(Collectors.toList());
//定制排序
List<Employee> persons1 = list.stream().sorted((e1, e2) -> {
if (e1.getAge() == e2.getAge()) {
return 0;
} else if (e1.getAge() > e2.getAge()) {
return 1;
} else {
return -1;
}
}).collect(Collectors.toList());