Java8新特性——Stream
1. stream概述
Java8 也出来好久了,接口默认方法,lambda 表达式,函数式接口,Date API 等特性还是有必要去了解一下。比如在项目中经常用到集合,遍历集合可以试下 lambda 表达式,经常还要对集合进行过滤和排序,Stream 就派上用场了。用习惯了,不得不说真的很好用。
Stream 作为 Java8 的新特性,基于 lambda 表达式,是对集合对象功能的增强,它专注于对集合对象进行各种高效、便利的聚合操作或者大批量的数据操作,提高了编程效率和代码可读性。
Stream 的原理:将要处理的元素看做一种流,流在管道中传输,并且可以在管道的节点上处理,包括过滤筛选、去重、排序、聚合等。元素流在管道中经过中间操作的处理,最后由最终操作得到前面处理的结果。
集合有两种方式生成流:
-
stream() − 为集合创建串行流
-
parallelStream() - 为集合创建并行流
上图中是 Stream 类的类结构图,里面包含了大部分的中间和终止操作。 -
中间操作
主要有以下方法(此类型方法返回的都是 Stream):map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered -
终止操作
主要有以下方法:forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator
2. 举例说明
首先为了说明 Stream 对对象集合的操作,新建一个 Student 类(学生类),覆写了 equals() 和 hashCode() 方法。
public class Student {
private Long id;
private Integer age;
private String name;
private String address;
public Student(Long id, Integer age, String name, String address) {
this.id = id;
this.age = age;
this.name = name;
this.address = address;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
if (id != null ? !id.equals(student.id) : student.id != null) return false;
if (age != null ? !age.equals(student.age) : student.age != null) return false;
if (name != null ? !name.equals(student.name) : student.name != null) return false;
return address != null ? address.equals(student.address) : student.address == null;
}
@Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (age != null ? age.hashCode() : 0);
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + (address != null ? address.hashCode() : 0);
return result;
}
}
2.1 filter(筛选)
@Test
public void testStream() {
Student s1 = new Student(1L, 11, "路飞", "北京");
Student s2 = new Student(2L, 22, "索隆", "上海");
Student s3 = new Student(3L, 33, "索隆", "河北");
Student s4 = new Student(4L, 44, "山治", "河南");
Student s5 = new Student(5L, 55, "娜美", "浙江");
Student s6 = new Student(6L, 66, "乔巴", "福建");
Student s7 = new Student(7L, 77, "乌索普", "广东");
Student s8 = new Student(8L, 88, "布鲁克", "广西");
Student s9 = new Student(9L, 99, "甚平", "云南");
Student s10 = new Student(10L, 100, "弗兰奇", "贵州");
List<Student> studentList = new ArrayList<>(10);
studentList.add(s1);
studentList.add(s2);
studentList.add(s3);
studentList.add(s4);
studentList.add(s5);
studentList.add(s6);
studentList.add(s7);
studentList.add(s8);
studentList.add(s9);
studentList.add(s10);
List<Student> streamStudentList = testFilter(studentList);
streamStudentList.forEach(System.out::println);
}
/**
* 集合筛选
*
* @param studentList
* @return
*/
private List<Student> testFilter(List<Student> studentList) {
//筛选出年龄大于50岁的学生
//return studentList.stream().filter(student->student.getAge()>50).collect(Collectors.toList());
//筛选出地址为北京的学生
return studentList.stream().filter(student -> student.getAddress().equals("北京")).collect(Collectors.toList());
}
2.2 map(转换)
map 就是将对应的元素按照给定的方法进行转换。
/**
* 集合转换
*
* @param studentList
* @return
*/
private List<String> testMap(List<Student> studentList) {
//在地址前面加上部分信息,只获取地址输出
return studentList.stream().map(student -> "住址:" + student.getAddress()).collect(Collectors.toList());
}
2.3 distinct(去重)
/**
* 简单字符串去重
*/
private void testDistinct1() {
List<String> stringList = Arrays.asList("111", "222", "333", "444", "111", "222");
stringList.stream().distinct().forEach(System.out::println);
}
引用对象的去重,引用对象要实现hashCode和equal方法,否则去重无效。
/**
* 引用对象去重
*/
private void testDistinct2() {
//引用对象的去重,引用对象要实现hashCode和equal方法,否则去重无效
Student s1 = new Student(1L, 11, "路飞", "北京");
Student s2 = new Student(2L, 22, "索隆", "上海");
Student s3 = new Student(3L, 33, "索隆", "河北");
Student s4 = new Student(4L, 44, "山治", "河南");
Student s5 = new Student(5L, 55, "娜美", "浙江");
Student s6 = new Student(6L, 66, "乔巴", "福建");
Student s7 = new Student(7L, 77, "乌索普", "广东");
Student s8 = new Student(8L, 88, "布鲁克", "广西");
Student s9 = new Student(9L, 99, "甚平", "云南");
Student s10 = new Student(10L, 100, "弗兰奇", "贵州");
List<Student> studentList = new ArrayList<>(10);
studentList.add(s1);
studentList.add(s2);
studentList.add(s3);
studentList.add(s4);
studentList.add(s5);
studentList.add(s6);
studentList.add(s7);
studentList.add(s8);
studentList.add(s9);
studentList.add(s10);
studentList.stream().distinct().forEach(System.out::println);
}
2.4 sorted(排序)
/**
* 集合排序(默认排序)
*/
private void testSort1() {
List<String> stringList = Arrays.asList("333", "222", "111");
stringList.stream().sorted().forEach(System.out::println);
}
运行结果:
/**
* 集合排序(指定排序规则)
*/
private void testSort2() {
Student s1 = new Student(1L, 11, "路飞", "北京");
Student s2 = new Student(2L, 22, "索隆", "上海");
Student s3 = new Student(3L, 33, "索隆", "河北");
Student s4 = new Student(4L, 44, "山治", "河南");
List<Student> studentList = new ArrayList<>(10);
studentList.add(s1);
studentList.add(s2);
studentList.add(s3);
studentList.add(s4);
studentList.stream().sorted((stu1, stu2) -> Long.compare(stu2.getId(), stu1.getId())).sorted(
(stu1, stu2) -> Integer.compare(stu2.getAge(), stu1.getAge())
).forEach(System.out::println);
}
上面指定排序规则,先按照学生的 id 进行降序排序,再按照年龄进行降序排序。
2.5 limit(限制返回个数)
/**
* 集合limit,返回前几个元素
*/
private void testLimit() {
List<String> stringList = Arrays.asList("333", "222", "111");
stringList.stream().limit(2).forEach(System.out::println);
}
运行结果:
2.6 skip(删除元素)
/**
* 集合skip,删除前n个元素
*/
private void testSkip() {
List<String> stringList = Arrays.asList("333", "222", "111");
stringList.stream().skip(2).forEach(System.out::println);
}
运行结果:
2.7 reduce(聚合)
/**
* 集合reduce,将集合中每个元素聚合成一条数据
*/
private void testReduce() {
List<String> stringList = Arrays.asList("欢", "迎", "您");
String result = stringList.stream().reduce("北京", (a, b) -> a + b);
System.out.println(result);
}
运行结果:
2.8 min(求最小值)
/**
* 求集合中元素的最小值
*/
private void testMin() {
Student s1 = new Student(1L, 11, "路飞", "北京");
Student s2 = new Student(2L, 22, "索隆", "上海");
Student s3 = new Student(3L, 33, "索隆", "河北");
Student s4 = new Student(4L, 44, "山治", "河南");
List<Student> studentList = new ArrayList<>(10);
studentList.add(s1);
studentList.add(s2);
studentList.add(s3);
studentList.add(s4);
Student minStudent = studentList.stream().min((stu1, stu2) -> Integer.compare(stu1.getAge(), stu2.getAge())).get();
System.out.println(minStudent.toString());
}
上面是求所有学生中年龄最小的一个,max 同理,求最大值。
2.9 anyMatch/allMatch/noneMatch(匹配)
private void testMatch() {
Student s1 = new Student(1L, 11, "路飞", "北京");
Student s2 = new Student(2L, 22, "索隆", "上海");
Student s3 = new Student(3L, 33, "索隆", "河北");
Student s4 = new Student(4L, 44, "山治", "河南");
List<Student> studentList = new ArrayList<>(10);
studentList.add(s1);
studentList.add(s2);
studentList.add(s3);
studentList.add(s4);
boolean anyMatch = studentList.stream().anyMatch(student -> "北京".equals(student.getAddress()));
if (anyMatch) {
System.out.println("学生中有北京人");
}
boolean allMatch = studentList.stream().allMatch(student -> student.getAge() >= 15);
if (allMatch) {
System.out.println("所有学生都满15周岁");
}
boolean noneMatch = studentList.stream().noneMatch(student -> "路飞".equals(student.getName()));
if (noneMatch) {
System.out.println("没有叫路飞的学生");
}
}
- anyMatch:Stream 中任意一个元素符合传入的 predicate,返回 true。
- allMatch:Stream 中全部元素符合传入的 predicate,返回 true。
- noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true。
上面介绍了 Stream 常用的一些方法,虽然对集合的遍历和操作可以用以前常规的方式,但是当业务逻辑复杂的时候,你会发现代码量很多,可读性很差,明明一行代码解决的事情,你却写了好几行。试试 lambda 表达式,试试 Stream,你会有不一样的体验。
上一篇: php使用正则替换过滤掉js(script)脚本例子
下一篇: laravel 在nginx配置