一、什么是Stream流
Stream(流)是一个来自数据源的元素队列并支持聚合操作
【注意要点】
①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
二、如何使用Stream流?
操作三个步骤
1、创建流
2、中间操作
3、终止操作
创建流
1、Collection集合框架提供两个方法 stram()和 parallelStream()
2、通过Arrays中的 stream()获取一个数据流
3、通过Stream类中提供的静态方法 Stream.of()
4、创建无限流
迭代流
生成流
// 创建Stream流
@Test
public void test1(){
// Collection集合系列 提供两个方法 stream() 与 parallelStream()
List<String> stringList = Arrays.asList("a", "b", "c");
Stream<String> stream = stringList.stream();
Stream<String> stringStream = stringList.parallelStream();
// 通过Arrays中的 stream()获取一个数组流
Integer[] num = new Integer[10];
Stream<Integer> stream1 = Arrays.stream(num);
// 通过Stream类中提供的静态方法 of
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6);
// 创建无限流
// 迭代
Stream<Integer> iterate = Stream.iterate(0, (x) -> x + 2).limit(5);
iterate.forEach(System.out::println);
// 生成
Stream.generate(Math::random).limit(2).forEach(System.out::println);
}
中间操作
1)筛选与切片
public class Employee {
private int id;
private String name;
private int age;
private double salary;
public Employee() {
}
public Employee(String name) {
this.name = name;
}
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
public Employee(int id, String name, int age, double salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
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 show() {
return "测试方法引用!";
}
public void getLength(Employee e){
System.out.println(e.getName().length());
}
public void getLength2(){
System.out.println(2);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
long temp;
temp = Double.doubleToLongBits(salary);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
if (age != other.age)
return false;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (Double.doubleToLongBits(salary) != Double.doubleToLongBits(other.salary))
return false;
return true;
}
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", age=" + age + ", salary=" + salary + "]";
}
}
注意:如果没有终止操作的话,那么stream流的中间操作是不会执行的。给大家举个例子蛤
因此,需要终止操作蛤。forEach直接打印即可
Filter()过滤方法,过滤通过一个predicate接口来过滤并只保留符合条件的元素,该操作属于中间操作。
List<Employee> emps = Arrays.asList(
new Employee(102, "李四", 59, 6666.66),
new Employee(101, "张三", 18, 9999.99),
new Employee(103, "王五", 28, 3333.33),
new Employee(104, "赵六", 8, 7777.77),
new Employee(104, "赵六", 8, 7777.77),
new Employee(104, "赵六", 8, 7777.77),
new Employee(105, "田七", 38, 5555.55)
);
/**
* 演示中间操作
*/
@Test
public void test2(){
// 需求:使用filter, 过滤出年纪在30岁之上的
// 1、创建流 stream() 2、调用filter过滤,filter的参数是一个断言型函数接口,直接写入过滤条件
// 2、终止操作
emps.stream().filter((e)-> {
System.out.println("筛选年纪在30岁纸上的, 当前人员is:" + e.toString());
return e.getAge() > 30;
}).forEach(employee -> System.out.println(employee));
// 如果使用方法引用中,则变成
emps.stream().filter((e)-> {
System.out.println("筛选年纪在30岁纸上的, 当前人员is:" + e.toString());
return e.getAge() > 30;
}).forEach(System.out::println);
}
【注意】这是第一个案例,所以注释比较清晰,在后面的案例将直接使用方法引用,则不说明咯,如果不清楚方法引用和函数式接口的,可以看看
distinct():去除重复,通过流所生成的HashCode()和equals()方法去除重复
@Test
public void test(){
// 需求:去除emp中重复的元素
emps.stream().distinct().forEach(System.out::println);
}
注意:在Employee中重写了 hashcode和equal,如果你调用distince()方法不生效的话,请检查自己的hashcode和equal方法。
limit():对一个Stream进行截断操作,获取其前N个元素,如果原Stream中包含的元素个数小于N,那就获取其所有的元素;
skpi():返回一个丢弃原Stream的前N个元素后剩下元素组成的新Stream,如果原Stream中包含的元素个数小于N,那么返回空Stream;
@Test
public void test(){
// 需求:过滤年纪在20岁以上,去重、并且只获取前两个,打印输出
System.out.println("======演示limit()方法 start===========");
emps.stream().filter(employee -> employee.getAge() > 20).distinct().limit(2).forEach(System.out::println);
System.out.println("======演示limit()方法 end===========");
// 需求:过滤年纪在20岁以上,去重、跳过前面1个,打印所有
System.out.println("======演示skip()方法 start===========");
emps.stream().filter(employee -> employee.getAge() > 20).distinct().skip(1).forEach(System.out::println);
System.out.println("======演示limit()方法 end===========");
}
2)映射
对于Stream中包含的元素使用给定的转换函数进行转换操作,新生成的Stream只包含转换生成的元素。这个方法有三个对于原始类型的变种方法,分别是:mapToInt,mapToLong和mapToDouble。这三个方法也比较好理解,比如mapToInt就是把原始Stream转换成一个新的Stream,这个新生成的Stream中的元素都是int类型。之所以会有这样三个变种方法,可以免除自动装箱/拆箱的额外消耗;
代码演示:
@Test
public void test4(){
// 以map为例子,接受一个Function,函数具体实现,将处理的结果映射成一个新的Stream
// 需求:将字符串变成大写,然后收集
List<String> stringList = Arrays.asList("a", "b", "c", "d", "e");
Stream<String> stringStream = stringList.stream().map(String::toUpperCase);
stringStream.forEach(System.out::println);
}
其他的案例,具体看代码即可
@Test
public void test5(){
// 演示 map(Function f): 提取出薪资
emps.stream().map(employee -> employee.getSalary()).distinct().forEach(System.out::println);
// 演示 mapToInt(ToIntFunction f)
int sum = emps.stream().mapToInt(emps -> emps.getId()).distinct().sum();
System.out.println(sum);
// mapToLong
OptionalDouble average = emps.stream().mapToDouble(emps -> emps.getSalary()).distinct().average();
System.out.println(average);
}
@Test
public void test6(){
List<String> stringList = Arrays.asList("a", "b", "c", "d", "e");
Stream<Stream<Character>> stream2 = stringList.stream()
.map(TestStreamAPI::filterCharacter);
stream2.forEach((sm) -> {
sm.forEach(System.out::println);
});
System.out.println("---------------------------------------------");
Stream<Character> stream3 = stringList.stream()
.flatMap(TestStreamAPI::filterCharacter);
stream3.forEach(System.out::println);
}
区别在于:
map:如果说映射的对象是一个流,则保留流的形式,结果是多个流
floatMap:如果说映射的对象是一个流,则合并成一个流,结果是一个流
sorted()——自然排序
sorted(Comparator com)——定制排序
排序是一个中间操作,返回的是排序好后的Stream。如果你不指定一个自定义的Comparator则会使用默认排序。
@Test
public void test22(){
emps.stream()
.map(Employee::getName)
.sorted()
.forEach(System.out::println);
System.out.println("------------------------------------");
emps.stream()
.sorted((x, y) -> {
if(x.getAge() == y.getAge()){
return x.getName().compareTo(y.getName());
}else{
return Integer.compare(x.getAge(), y.getAge());
}
}).forEach(System.out::println);
}
终止操作
1)查找与匹配
List<Employee> emps = Arrays.asList(
new Employee(102, "李四", 59, 6666.66),
new Employee(101, "张三", 18, 9999.99),
new Employee(103, "王五", 28, 3333.33),
new Employee(104, "赵六", 8, 7777.77),
new Employee(105, "田七", 38, 5555.55)
);
@Test
public void test1(){
// 需求1: AllMatch 判断所有的员工是否成年
boolean b = emps.stream().allMatch(employee -> employee.getAge() > 18);
System.out.println("AllMatch 判断所有的员工是否成年, result is : " + b);
// 需求2: anyMatch 判断是否有一个员工工资大于9600
boolean b1 = emps.stream().anyMatch(employee -> employee.getSalary() > 9600);
System.out.println("anyMatch 判断是否有一个员工工资大于9600 result is : " + b1);
// 需求3: noneMatch(Predicate p) 判断所有员工是否没有大于18岁的
boolean b2 = emps.stream().noneMatch(employee -> employee.getAge() > 18);
System.out.println("noneMatch(Predicate p) 判断所有员工是否没有大于18岁的 result is : " + b2);
// 需求4 findFirst() 返回第一个元素
Optional<Employee> first = emps.stream().findFirst();
System.out.println("findFirst() 返回第一个元素 result is : " + first.get());
// 需求4 findAny() 返回当前流中的任意元素
Optional<Employee> any = emps.stream().findAny();
System.out.println("findAny() 返回任意元素 result is : " + any.get());
}
2)规约操作
@Test
public void test1(){
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum = list.stream()
.reduce(0, (x, y) -> x + y);
System.out.println(sum);
System.out.println("----------------------------------------");
Optional<Double> op = emps.stream()
.map(Employee::getSalary)
.reduce(Double::sum);
System.out.println(op.get());
}
//需求:搜索名字中 “六” 出现的次数
@Test
public void test2(){
Optional<Integer> sum = emps.stream()
.map(Employee::getName)
.flatMap(TestStreamAPI1::filterCharacter)
.map((ch) -> {
if(ch.equals('六'))
return 1;
else
return 0;
}).reduce(Integer::sum);
System.out.println(sum.get());
}
3)收集:collect——将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:
@Test
public void test3(){
List<String> list = emps.stream()
.map(Employee::getName)
.collect(Collectors.toList());
list.forEach(System.out::println);
System.out.println("----------------------------------");
Set<String> set = emps.stream()
.map(Employee::getName)
.collect(Collectors.toSet());
set.forEach(System.out::println);
System.out.println("----------------------------------");
HashSet<String> hs = emps.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(HashSet::new));
hs.forEach(System.out::println);
}
@Test
public void test4(){
Optional<Double> max = emps.stream()
.map(Employee::getSalary)
.collect(Collectors.maxBy(Double::compare));
System.out.println(max.get());
Optional<Employee> op = emps.stream()
.collect(Collectors.minBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
System.out.println(op.get());
Double sum = emps.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(sum);
Double avg = emps.stream()
.collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println(avg);
Long count = emps.stream()
.collect(Collectors.counting());
System.out.println(count);
System.out.println("--------------------------------------------");
DoubleSummaryStatistics dss = emps.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(dss.getMax());
}
//分组
@Test
public void test5(){
Map<Status, List<Employee>> map = emps.stream()
.collect(Collectors.groupingBy(Employee::getStatus));
System.out.println(map);
}
//多级分组
@Test
public void test6(){
Map<Status, Map<String, List<Employee>>> map = emps.stream()
.collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
if(e.getAge() >= 60)
return "老年";
else if(e.getAge() >= 35)
return "中年";
else
return "成年";
})));
System.out.println(map);
}
//分区
@Test
public void test7(){
Map<Boolean, List<Employee>> map = emps.stream()
.collect(Collectors.partitioningBy((e) -> e.getSalary() >= 5000));
System.out.println(map);
}
//
@Test
public void test8(){
String str = emps.stream()
.map(Employee::getName)
.collect(Collectors.joining("," , "----", "----"));
System.out.println(str);
}
@Test
public void test9(){
Optional<Double> sum = emps.stream()
.map(Employee::getSalary)
.collect(Collectors.reducing(Double::sum));
System.out.println(sum.get());
}
至此Stream流的相关操作就已经学会了,希望对你们有帮助。
三、练习
//交易类
public class Transaction {
private Trader trader;
private int year;
private int value;
public Transaction() {
}
public Transaction(Trader trader, int year, int value) {
this.trader = trader;
this.year = year;
this.value = value;
}
public Trader getTrader() {
return trader;
}
public void setTrader(Trader trader) {
this.trader = trader;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
@Override
public String toString() {
return "Transaction [trader=" + trader + ", year=" + year + ", value="
+ value + "]";
}
}
//交易员类
public class Trader {
private String name;
private String city;
public Trader() {
}
public Trader(String name, String city) {
this.name = name;
this.city = city;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
public String toString() {
return "Trader [name=" + name + ", city=" + city + "]";
}
}
public class TestStreamAPI {
/*
1. 给定一个数字列表,如何返回一个由每个数的平方构成的列表呢?
,给定【1,2,3,4,5】, 应该返回【1,4,9,16,25】。
*/
@Test
public void test1(){
Integer[] nums = new Integer[]{1,2,3,4,5};
Arrays.stream(nums)
.map((x) -> x * x)
.forEach(System.out::println);
}
/*
2. 怎样用 map 和 reduce 方法数一数流中有多少个Employee呢?
*/
List<Employee> emps = Arrays.asList(
new Employee(102, "李四", 59, 6666.66, Status.BUSY),
new Employee(101, "张三", 18, 9999.99, Status.FREE),
new Employee(103, "王五", 28, 3333.33, Status.VOCATION),
new Employee(104, "赵六", 8, 7777.77, Status.BUSY),
new Employee(104, "赵六", 8, 7777.77, Status.FREE),
new Employee(104, "赵六", 8, 7777.77, Status.FREE),
new Employee(105, "田七", 38, 5555.55, Status.BUSY)
);
@Test
public void test2(){
Optional<Integer> count = emps.stream()
.map((e) -> 1)
.reduce(Integer::sum);
System.out.println(count.get());
}
}
public class TestTransaction {
List<Transaction> transactions = null;
@Before
public void before(){
Trader raoul = new Trader("Raoul", "Cambridge");
Trader mario = new Trader("Mario", "Milan");
Trader alan = new Trader("Alan", "Cambridge");
Trader brian = new Trader("Brian", "Cambridge");
transactions = Arrays.asList(
new Transaction(brian, 2011, 300),
new Transaction(raoul, 2012, 1000),
new Transaction(raoul, 2011, 400),
new Transaction(mario, 2012, 710),
new Transaction(mario, 2012, 700),
new Transaction(alan, 2012, 950)
);
}
//1. 找出2011年发生的所有交易, 并按交易额排序(从低到高)
@Test
public void test1(){
transactions.stream()
.filter((t) -> t.getYear() == 2011)
.sorted((t1, t2) -> Integer.compare(t1.getValue(), t2.getValue()))
.forEach(System.out::println);
}
//2. 交易员都在哪些不同的城市工作过?
@Test
public void test2(){
transactions.stream()
.map((t) -> t.getTrader().getCity())
.distinct()
.forEach(System.out::println);
}
//3. 查找所有来自剑桥的交易员,并按姓名排序
@Test
public void test3(){
transactions.stream()
.filter((t) -> t.getTrader().getCity().equals("Cambridge"))
.map(Transaction::getTrader)
.sorted((t1, t2) -> t1.getName().compareTo(t2.getName()))
.distinct()
.forEach(System.out::println);
}
//4. 返回所有交易员的姓名字符串,按字母顺序排序
@Test
public void test4(){
transactions.stream()
.map((t) -> t.getTrader().getName())
.sorted()
.forEach(System.out::println);
System.out.println("-----------------------------------");
String str = transactions.stream()
.map((t) -> t.getTrader().getName())
.sorted()
.reduce("", String::concat);
System.out.println(str);
System.out.println("------------------------------------");
transactions.stream()
.map((t) -> t.getTrader().getName())
.flatMap(TestTransaction::filterCharacter)
.sorted((s1, s2) -> s1.compareToIgnoreCase(s2))
.forEach(System.out::print);
}
public static Stream<String> filterCharacter(String str){
List<String> list = new ArrayList<>();
for (Character ch : str.toCharArray()) {
list.add(ch.toString());
}
return list.stream();
}
//5. 有没有交易员是在米兰工作的?
@Test
public void test5(){
boolean bl = transactions.stream()
.anyMatch((t) -> t.getTrader().getCity().equals("Milan"));
System.out.println(bl);
}
//6. 打印生活在剑桥的交易员的所有交易额
@Test
public void test6(){
Optional<Integer> sum = transactions.stream()
.filter((e) -> e.getTrader().getCity().equals("Cambridge"))
.map(Transaction::getValue)
.reduce(Integer::sum);
System.out.println(sum.get());
}
//7. 所有交易中,最高的交易额是多少
@Test
public void test7(){
Optional<Integer> max = transactions.stream()
.map((t) -> t.getValue())
.max(Integer::compare);
System.out.println(max.get());
}
//8. 找到交易额最小的交易
@Test
public void test8(){
Optional<Transaction> op = transactions.stream()
.min((t1, t2) -> Integer.compare(t1.getValue(), t2.getValue()));
System.out.println(op.get());
}
}