Java8的Stream用法

Java8StreamAPI:简化集合操作与并发处理
本文介绍了Java8中的StreamAPI,展示了如何使用forEach、map、filter、parallelStream等方法对集合进行高效操作,以及其实现的并发处理特性,提升代码生产力。

Java8 API新增了一个新的抽象流Stream,它可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。Stream就是把集合数据看作流,流在管道中传输,我们可在管道中进行排序聚合等操作。

在平时写代码的过程中,涉及到集合操作时候,Stream API可以极大的提高我们的生产力,让我们写出高效率、干净、简洁的代码。Stream API有很多操作可供使用,这里主要介绍常用的几个方法。

基础Stream API

forEach

forEach是迭代流中的每一个元素。

@Test
public void testForeach() {
	Random random = new Random();
	List<Integer> list= new ArrayList<>();
	//random.ints(-100,100).limit(10).forEach(System.out::println);
	random.ints(-100,100).limit(10).forEach(t->list.add(t));
	System.out.println(list);
}

上述是用forEach迭代输出每一个-100至100的随机值,输出10个,还可以迭代进入一个新的List集合。

map

map对流中每个元素进行操作,返回一个新的流

@Test
public void testMap(){
	// 只是对流操作并不会改变原List中的数据
	//Stream<String> fruit=Stream.of("apple","orange","banner","pear");
	List<String> fruit = Arrays.asList("apple","orange","banner","pear");
	fruit.stream().sorted().map(String::toUpperCase).forEach(System.out::println);
	System.out.println(fruit);

	// 返回新的流
	List<String> newfruit = fruit.stream().map(v->v.toUpperCase()).collect(Collectors.toList());
	System.out.println(newfruit);
}

上述是对每个元素进行大写字母化,或输出或返回到新的集合中

filter

filter 方法用于通过设置的条件过滤出元素

/**
 * Long 数组选取不为Null和大于0的值
 * @param t
 * @return
 */
public static Long[] removeNullAndZero(Long[] t) {
	List<Long> list = Arrays.asList(t);
	return list.stream().filter(v->v!=null && v>0).toArray(Long[] :: new);	
}

/**
 * 选取String数组 不为Null的值
 * @param t
 * @return
 */
public static String[] removeNullAndZero(String[] t) {
	List<String> list = Arrays.asList(t);
	return list.stream().filter(v->v!=null).toArray(String[] :: new);	
}

过滤Null或者大于0的元素

@Test
public void testFilter() {
	List<Integer> intList = Arrays.asList(8,2,4,1,8,3,10,6,6,15);
	List<Integer> newIntList=intList.stream().filter(i->i>5).sorted().distinct().collect(Collectors.toList());
	System.out.println(newIntList);
}

上述是过滤大于5的值,并且排序,取唯一的数字输出到新的集合。

parallelStream

parallelStream 是流并行处理程序的代替方法,相比较Stream是多管道操作。它就是基于ForkJoinPool执行并发任务的。

特点

  • 使用parallelStream可以简洁高效的写出并发代码。
  • parallelStream并行执行是无序的。
  • parallelStream提供了更简单的并发执行的实现,但并不意味着更高的性能,它是使用要根据具体的应用场景。如果cpu资源紧张parallelStream不会带来性能提升;如果存在频繁的线程切换反而会降低性能。
  • 任务之间最好是状态无关的,因为parallelStream默认是非线程安全的,可能带来结果的不确定性。

Github上对Parallel做了增强,有兴趣可以点击访问

@Test
public void testParallel() {
	Random random = new Random();
	List<Integer> list= new ArrayList<>();
	random.ints(-10000,10000).limit(10000).forEach(t->list.add(t));
	
	
	long start = System.currentTimeMillis();
	list.stream().filter(e -> e > 1000 && e< 2000).collect(Collectors.toList());
	System.out.println("stream : " + (System.currentTimeMillis() - start) + "ms");
	
	start = System.currentTimeMillis();
	list.parallelStream().filter(e -> e > 1000 && e < 2000).collect(Collectors.toList());
	System.out.println("parallelStream : " + (System.currentTimeMillis() - start) + "ms");
}

测试并发处理速度,上述代码对此做了测试,测试结果parallelStream速度是稍快一点。

Count

Stream API还提供简单的统计操作。

@Test
public void testCount() {
	List<Integer> numList = Arrays.asList(6, 2, 4, 3, 7, 3, 5, 9);
	 
	DoubleSummaryStatistics stats = numList.stream().mapToDouble((x) -> x).summaryStatistics();
	System.out.println("总个数 : " + stats.getCount());
	System.out.println("列表中最大的数 : " + stats.getMax());
	System.out.println("列表中最小的数 : " + stats.getMin());
	System.out.println("所有数之和 : " + stats.getSum());
	System.out.println("平均数 : " + stats.getAverage());
}

简单的统计的功能。

实际应用

我在实际项目对会有集合进行操作,使用Stream API能够帮我简洁的解决问题。下面是我实际使用场景。

对象类

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
	
	private String name;
	private Integer gender;
	private int age;
	private double height;
}

使用场景

构造数据
static List<Person> createPeople(){
	List<Person> people=new ArrayList<Person>();
	Person person=new Person("张三",0,30,2.8);
	people.add(person);
	person=new Person("李四",0,32,1.6);
	people.add(person);
	person=new Person("王五",1,32,2.0);
	people.add(person);
	person=new Person("王五",1,33,1.6);
	people.add(person);
	return people;
}
连接某一属性

需要并行显示,用,连接某一属性

@Test
public void CollectionStreamJoin(){
	List<Person> people=createPeople();
	Stream<Person> stream=people.stream();
	// 取出对象的name值,并用,连接
	String names = stream.map(v->v.getName()).collect(Collectors.joining(","));
	System.out.println(names);
}
获取某属性唯一值

根据某一个属性值,删选对象。只保留唯一的属性值

@Test
public void distinctList() {
	List<Person> people=createPeople();
	List<Person> distinctlist = people.stream()
			.collect(Collectors.collectingAndThen(
					Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Person::getName))),
					ArrayList::new));
	System.out.println(distinctlist);
}
多属性排序

需要用多个属性去进行排序

@Test
public void sortField() {
	List<Person> people=createPeople();
	people.sort(Comparator.comparing(Person::getAge).reversed().thenComparing(Person::getHeight));
	System.out.println(JSON.toJSONString(people));
}
分组后排序

对象列表先按照一个字段分组,并根据另一个字段的大小来排序,取第一个对象

@Test
public void getOnlyOneByField() {
	List<Person> people=createPeople();
	Map<String,Person> map =new HashMap<>();
	map = people.parallelStream()
				.collect(Collectors.groupingBy(Person::getName,
						Collectors.collectingAndThen(
								Collectors.reducing((c1, c2) -> c1.getAge()>c2.getAge()?c1:c2),
								Optional::get)));
	System.out.println(map);
}
累加求和

根据某个属性值累加

@Test
public void accumulation(){
	List<Person> people=createPeople();
	int sumAge = people.stream().mapToInt(Person::getAge).sum();
	System.out.println(sumAge);
}
分组求和

根据某个属性分组,然后另外一个值求和

/**
 * 分组求和
 */
@Test
public void groupSum(){
	List<Person> people=createPeople();
	Map<Integer, DoubleSummaryStatistics> summaryStatisticsMap = people.stream()
			.collect(
					Collectors.groupingBy(Person::getGender,
							Collectors.summarizingDouble(Person::getHeight))
			);
	System.out.println(JSON.toJSONString(summaryStatisticsMap));
}

总结

使用Stream API可以让集合操作的代码更为简洁,提高生产力。而且本人在写Stream操作的时候,思想就跟写SQL语句类似,包括分组、聚合、过滤等操作,遇到难的可以在思维上借鉴下。

### Java 8 Stream API 使用教程和示例 Java 8 引入了 Stream API,这是一个用于处理集合数据的强大工具。它提供了一种简洁、高效且易于并行化的方式来处理数据流,从而极大地简化了编写代码的复杂性[^2]。Stream API 的核心功能包括数据过滤、排序、转换及汇总等操作,支持链式调用,使开发者可以写出更简洁、更高效、更易于维护的代码[^4]。 以下是一个具体的使用示例,展示如何利用 Stream API 进行数据处理: ```java import java.util.*; import java.util.stream.*; public class StreamExample { public static void main(String[] args) { List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); // 链式操作:筛选偶数并将其加倍,最后收集到列表中 List<Integer> result = numbers.stream() .filter(n -> n % 2 == 0) // 筛选偶数 .map(n -> n * 2) // 将偶数加倍 .collect(Collectors.toList()); // 收集到列表 System.out.println(result); // 输出: [4, 8, 12, 16, 20] } } ``` 在这个示例中,`filter` 方法用于筛选流中符合条件的元素(即偶数),`map` 方法用于转换流中的每个元素(将偶数加倍),而 `collect` 方法则用于将流处理的结果收集到集合中[^5]。 #### Stream API 的基本概念 Stream API 的工作流程可以分为三个阶段: 1. **创建流**:从集合、数组或其他数据源创建一个流。 2. **中间操作**:对流进行一系列转换操作(如 `filter` 和 `map`),这些操作会返回一个新的流。 3. **终端操作**:执行流的计算并生成结果(如 `collect` 或 `forEach`)[^3]。 #### 常见的操作方法 - **`filter`**:筛选符合条件的元素。 - **`map`**:将流中的每个元素映射为另一个元素。 - **`sorted`**:对流中的元素进行排序。 - **`distinct`**:去除流中的重复元素。 - **`limit`**:限制流的大小。 - **`skip`**:跳过指定数量的元素。 - **`collect`**:将流的结果收集到集合中。 - **`forEach`**:遍历流中的每个元素[^4]。 通过结合这些操作方法,开发者可以轻松实现复杂的数据处理逻辑。 ### 注意事项 在使用 Stream API 时需要注意以下几点: - 流是不可变的,所有操作都会返回一个新的流。 - 中间操作不会触发实际的计算,只有当终端操作被调用时,流才会开始执行操作。 - 流只能被消费一次,一旦执行了终端操作,流就会关闭[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值