目录
2.4map(T -> R) 和 flatMap(T -> Stream)
2.6 limit(long n) 和 skip(long n)
3.1 reduce((T, T) -> T) 和 reduce(T, (T, T) -> T)
3.2 mapToInt(T -> int) 、mapToDouble(T -> double) 、mapToLong(T -> long)
3.4 summingInt()、summingLong()、summingDouble()
3.5 averagingInt()、averagingLong()、averagingDouble()
你需要了解的快捷开发 Java8 stream的使用(二) stream对List集合进行分组、汇总、排序等操作
Java 8 Stream
Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
什么是 Stream?
Stream(流)是一个来自数据源的元素队列并支持聚合操作
- 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
- 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
- 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
和以前的Collection操作不同, Stream操作还有两个基础的特征:
- Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
- 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。
生成流
在 Java 8 中, 集合接口有两个方法来生成流:
-
stream() − 为集合创建串行流。
-
parallelStream() − 为集合创建并行流。
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
下面是使用Stream的常用方法的综合实例。
部分实例来源
www.cnblogs.com/qinhao517/p/9197885.html
www.blog.csdn.net/pan_junbiao/article/details/105913518
综合实例
1.创建实体类和数据
User.java
import java.math.BigDecimal;
/**
* 用户信息实体类
**/
public class User
{
private int id; //用户ID
private String name; //用户名称
private String sex; //性别
private int age; //年龄
private String department; //部门
private BigDecimal salary; //薪资
//构造方法
public User(int id,String name,String sex,int age,String department,BigDecimal salary)
{
this.id = id;
this.name = name;
this.sex = sex;
this.age = age;
this.department = department;
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 String getSex()
{
return sex;
}
public void setSex(String sex)
{
this.sex = sex;
}
public int getAge()
{
return age;
}
public void setAge(int age)
{
this.age = age;
}
public String getDepartment()
{
return department;
}
public void setDepartment(String department)
{
this.department = department;
}
public BigDecimal getSalary()
{
return salary;
}
public void setSalary(BigDecimal salary)
{
this.salary = salary;
}
@Override
public String toString()
{
return "ID:" + this.id + " 名称:" + this.name + " 性别:" + this.sex
+ " 年龄:" + this.age + " 部门:" + this.department + " 薪资:" + this.salary + "元";
}
}
UserService.class
import com.pjb.streamdemo.entity.User;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
/**
* 用户信息业务逻辑类
**/
public class UserService
{
/**
* 获取用户列表
*/
public static List<User> getUserList()
{
List<User> userList = new ArrayList<User>();
userList.add(new User(1, "青冘的博客_01", "男", 32, "研发部", BigDecimal.valueOf(1600)));
userList.add(new User(2, "青冘的博客_02", "男", 30, "财务部", BigDecimal.valueOf(1800)));
userList.add(new User(3, "青冘的博客_03", "女", 20, "人事部", BigDecimal.valueOf(1700)));
userList.add(new User(4, "青冘的博客_04", "男", 38, "研发部", BigDecimal.valueOf(1500)));
userList.add(new User(5, "青冘的博客_05", "女", 25, "财务部", BigDecimal.valueOf(1200)));
return userList;
}
}
2.查询方法
2.1 forEach()
使用 forEach() 遍历列表数据。
/**
* 使用forEach()遍历列表信息
*/
@Test
public void forEachTest()
{
//获取用户列表
List<User> userList = UserService.getUserList();
//遍历用户列表
userList.forEach(System.out::println);
}
上述遍历语句等同于以下语句:
userList.forEach(user -> {System.out.println(user);});
执行结果:
2.2 filter(T -> boolean)
使用 filter() 过滤列表数据
获取部门为“研发部”的用户列表。
/**
* 使用filter()过滤列表信息
*/
@Test
public void filterTest()
{
//获取用户列表
List<User> userList = UserService.getUserList();
//获取部门为“研发部”的用户列表
userList = userList.stream().filter(user -> user.getDepartment() == "研发部").collect(Collectors.toList());
//遍历用户列表
userList.forEach(System.out::println);
}
执行结果:
2.3findAny() 和 findFirst()
使用 findAny() 和 findFirst() 获取第一条数据。
获取用户名称为“青冘的博客_02”的用户信息,如果未找到则返回null。
/**
* 使用findAny()获取第一条数据
*/
@Test
public void findAnytTest()
{
//获取用户列表
List<User> userList = UserService.getUserList();
//获取用户名称为“青冘的博客_02”的用户信息,如果没有找到则返回null
User user = userList.stream().filter(u -> u.getName().equals("青冘的博客_02")).findAny().orElse(null);
//打印用户信息
System.out.println(user);
}
执行结果:
注意:findFirst() 和 findAny() 都是获取列表中的第一条数据,但是findAny()操作,返回的元素是不确定的,对于同一个列表多次调用findAny()有可能会返回不同的值。使用findAny()是为了更高效的性能。如果是数据较少,串行地情况下,一般会返回第一个结果,如果是并行(parallelStream并行流)的情况,那就不能确保是第一个。
2.4map(T -> R) 和 flatMap(T -> Stream)
使用 map() 将流中的每一个元素 T 映射为 R(类似类型转换)。
使用 flatMap() 将流中的每一个元素 T 映射为一个流,再把每一个流连接成为一个流。
使用 map() 方法获取用户列表中的名称列。
/**
* 使用map()获取列元素
*/
@Test
public void mapTest()
{
//获取用户列表
List<User> userList = UserService.getUserList();
//获取用户名称列表
List<String> nameList = userList.stream().map(User::getName).collect(Collectors.toList());
//或者:List<String> nameList = userList.stream().map(user -> user.getName()).collect(Collectors.toList());
//遍历名称列表
nameList.forEach(System.out::println);
}
返回的结果为数组类型,写法如下:
//数组类型
String[] nameArray = userList.stream().map(User::getName).collect(Collectors.toList()).toArray(new String[userList.size()]);
执行结果:
使用 flatMap() 将流中的每一个元素连接成为一个流。
/**
* 使用flatMap()将流中的每一个元素连接成为一个流
*/
@Test
public void flatMapTest()
{
//创建城市
List<String> cityList = new ArrayList<String>();
cityList.add("北京;上海;深圳;");
cityList.add("广州;武汉;杭州;");
//分隔城市列表,使用 flatMap() 将流中的每一个元素连接成为一个流。
cityList = cityList.stream()
.map(city -> city.split(";"))
.flatMap(Arrays::stream)
.collect(Collectors.toList());
//遍历城市列表
cityList.forEach(System.out::println);
}
执行结果:
2.5 distinct()
使用 distinct() 方法可以去除重复的数据。
获取部门列表,并去除重复数据。
/**
* 使用distinct()去除重复数据
*/
@Test
public void distinctTest()
{
//获取用户列表
List<User> userList = UserService.getUserList();
//获取部门列表,并去除重复数据
List<String> departmentList = userList.stream().map(User::getDepartment).distinct().collect(Collectors.toList());
//遍历部门列表
departmentList.forEach(System.out::println);
}
执行结果:
2.6 limit(long n) 和 skip(long n)
limit(long n) 方法用于返回前n条数据,skip(long n) 方法用于跳过前n条数据。
/**
* limit(long n)方法用于返回前n条数据
* skip(long n)方法用于跳过前n条数据
*/
@Test
public void limitAndSkipTest()
{
//获取用户列表
List<User> userList = UserService.getUserList();
//获取用户列表,要求跳过第1条数据后的前3条数据
userList = userList.stream()
.skip(1)
.limit(3)
.collect(Collectors.toList());
//遍历用户列表
userList.forEach(System.out::println);
}
执行结果:
3.统计方法
3.1 reduce((T, T) -> T) 和 reduce(T, (T, T) -> T)
使用 reduce((T, T) -> T) 和 reduce(T, (T, T) -> T) 用于组合流中的元素,如求和,求积,求最大值等。
@Test
public void testReduce() {
Stream<Integer> stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7, 8});
//求集合元素之和
System.out.println("求集合元素之和");
Integer result = stream.reduce(0, Integer::sum);
System.out.println(result);
stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
//求和
System.out.println("求和");
stream.reduce((i, j) -> i + j).ifPresent(System.out::println);
stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
//求最大值
System.out.println("求最大值");
stream.reduce(Integer::max).ifPresent(System.out::println);
stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
//求最小值
System.out.println("求最小值");
stream.reduce(Integer::min).ifPresent(System.out::println);
stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
//做逻辑
System.out.println("做逻辑");
stream.reduce((i, j) -> i > j ? j : i).ifPresent(System.out::println);
stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
//求逻辑求乘机
System.out.println("求逻辑求乘机");
int result2 = stream.filter(i -> i % 2 == 0).reduce(1, (i, j) -> i * j);
Optional.of(result2).ifPresent(System.out::println);
}
执行结果:
3.2 mapToInt(T -> int) 、mapToDouble(T -> double) 、mapToLong(T -> long)
int sumVal = userList.stream().map(User::getAge).reduce(0,Integer::sum);计算元素总和的方法其中暗含了装箱成本,map(User::getAge) 方法过后流变成了 Stream 类型,而每个 Integer 都要拆箱成一个原始类型再进行 sum 方法求和,这样大大影响了效率。针对这个问题 Java 8 有良心地引入了数值流 IntStream, DoubleStream, LongStream,这种流中的元素都是原始数据类型,分别是 int,double,long。
流转换为数值流:
- mapToInt(T -> int) : return IntStream
- mapToDouble(T -> double) : return DoubleStream
- mapToLong(T -> long) : return LongStream
//用户列表中年龄的最大值、最小值、总和、平均值
int maxVal = userList.stream().mapToInt(User::getAge).max().getAsInt();
int minVal = userList.stream().mapToInt(User::getAge).min().getAsInt();
int sumVal = userList.stream().mapToInt(User::getAge).sum();
double aveVal = userList.stream().mapToInt(User::getAge).average().getAsDouble();
3.3 counting() 和 count()
使用 counting() 和 count() 可以对列表数据进行统计。
//统计研发部的人数,使用 counting()方法进行统计
Long departCount = userList.stream().filter(user -> user.getDepartment() == "研发部").collect(Collectors.counting());
//统计30岁以上的人数,使用 count()方法进行统计(推荐)
Long ageCount = userList.stream().filter(user -> user.getAge() >= 30).count();
3.4 summingInt()、summingLong()、summingDouble()
用于计算总和,需要一个函数参数。
//计算年龄总和
int sumAge = userList.stream().collect(Collectors.summingInt(User::getAge));
3.5 averagingInt()、averagingLong()、averagingDouble()
用于计算平均值
//计算平均年龄
double aveAge = userList.stream().collect(Collectors.averagingDouble(User::getAge));