Stream流是java8的一大亮点,它与java.io包里面的InputStream和OutputStream是完全不同的概念。
java8中的Stream流是对集合(Collections)和数组对象功能的增强,它专注与对集合对象和数组进行非常高效的遍历操作。Stream不是集合元素,它不是数据结构,并不保存数据,它更像一个高级版本的Iteractor。原始版本的Iteractor:用户只能显示的一个一个遍历元素并对其进行操作。高级版本的Stream:用户只要给出对其包含的元素执行什么操作,比如“过滤长度大于10的字符串”,“获取每个字符串的首字母”等,Stream会隐士的在内部进行遍历并作出相应的数据转换。
Stream流可以理解为一个流水线,通过一道道工序对集合元素进行处理,加工,得到想要的元素。
一.Stream流之初体验
/**
* @Author: chuxia0811
* @Date: 2021/3/2 22:33
* @Description :
* 需求:筛选集合中所有姓孙的,名字长度为3的,并打印出来这些名字
*/
public class Demo1 {
public static void main(String[] args) {
List<String> names = new ArrayList<String>();
names.add("孙大圣");
names.add("安琪拉");
names.add("德玛西亚");
names.add("孙悟空");
names.add("张三丰");
names.add("吴孟达");
names.add("小强");
//一.用原始方法实现
List<String> zlist = new ArrayList<>();
// 筛选所有姓孙的
for (String name : names) {
if (name.startsWith("孙")) {
zlist.add(name);
}
}
// 筛选名字长度为3的
List<String> result = new ArrayList<>();
for (String name : zlist) {
if (name.length() == 3) {
result.add(name);
}
}
//打印名字
for (String name : result) {
System.out.println(name);
}
System.out.println("=================华丽的分割线==============");
//二:用Stream流实现
names.stream().filter(name -> name.startsWith("孙")).filter(name -> name.length()==3).forEach(name -> System.out.println(name));
}
}
看到上面的demo应该可以体会到Stream流惊人的效率吧?普通方式实现一个过滤打印需要十几行代码,用Stream流只需要一行就可以实现了,下面详细介绍Stream流的使用。
二:Stream流的获取方式
Stream流的获取方式:
1.单列集合:list,Set Stream 集合对象.stream();
2.双列集合:map 没有方法直接获取Stream流对象,需要将双列集合转化为单列集合,再调用.stream()方法获取流;
3.数组:使用Stream提供的静态方法of()获取。
/**
* @Author: chuxia0811
* @Date: 2021/3/2 23:15
* @Description :
*/
public class Demo2 {
public static void main(String[] args) {
// 1.List集合
List<String> list = new ArrayList<>();
Stream<String> listStream = list.stream();
//2.Set集合
Set<String> set = new HashSet<>();
Stream<String> streamSet = set.stream();
//3.Map集合
Map<String,String> map = new HashMap<>();
//获取键对应的stream流
Set<String> keyset = map.keySet();
keyset.stream();
//获取值对应的stream流
Collection<String> value = map.values();
value.stream();
//获取Entry对象集合
Set<Map.Entry<String,String>> entrySet = map.entrySet();
entrySet.stream();
//4.数组
String[] str = {"a","b","c"};
Stream<String> arrayStream = Stream.of(str);
}
}
三:Stream流常用的方法
1.forEach方法
/**
* @Author: chuxia0811
* @Date: 2021/3/3 22:57
* @Description :作用:循环,对流中的每一个元素进行遍历,将每一个元素传递给消费者对象处理
*/
public class ForEach {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
Collections.addAll(list,"迪丽热巴","唐嫣","罗晋","郝泽宇","艾伦");
list.stream().forEach(name ->{
System.out.println(name);
});
}
}
运行结果如下:
迪丽热巴
唐嫣
罗晋
郝泽宇
艾伦
2.filter方法
/**
* @Author: chuxia0811
* @Date: 2021/3/3 22:57
* @Description :
*/
public class ForEach {
public static void main(String[] args) {
// filter作用:对流中的元素进行过滤
List<String> list = new ArrayList<String>();
Collections.addAll(list, "迪丽热巴", "唐嫣", "罗晋", "郝泽宇", "艾伦");
//筛选保留名字为3位的
list.stream().filter(name -> name.length() == 3).forEach(name -> System.out.println(name));
}
}
运行结果如下:
郝泽宇
3.limit方法
/**
* @Author: chuxia0811
* @Date: 2021/3/3 23:19
* @Description :
*/
public class LimitDemo {
public static void main(String[] args) {
// limit(n)作用:获取流中前n个元素,其中n>=0,n=0时输出一个空流,
// 当n>元素总数size时会输出所有元素,当n<0时会报非法参数异常
List<String> list = new ArrayList<String>();
Collections.addAll(list, "迪丽热巴", "唐嫣", "罗晋", "郝泽宇", "艾伦");
Stream<String> stream = list.stream();
Stream<String> newStream = stream.limit(3);
newStream.forEach(name -> System.out.println(name));
}
}
4.skip方法
/**
* @Author: chuxia0811
* @Date: 2021/3/3 23:28
* @Description :
*/
public class SkipDemo {
public static void main(String[] args) {
// skip方法作用:和limit相反,指跳过前n个元素放在另外一个流中
// n < 0时会报参数非法异常; n = 0会跳过0个元素,即输出所有元素
// n < 元素个数size时会跳过前n个元素; n > 元素个数size时会跳过所有元素,输出空流
List<String> list = new ArrayList<String>();
Collections.addAll(list, "迪丽热巴", "唐嫣", "罗晋", "郝泽宇", "艾伦");
Stream<String> stream = list.stream();
Stream<String> newStream = stream.skip(3);
newStream.forEach(name -> System.out.println(name));
}
}
5.map方法
/**
* @Author: chuxia0811
* @Date: 2021/3/4 23:22
* @Description :
*/
public class MapDemo {
public static void main(String[] args) {
//map作用:遍历流中的每一个元素,将元素从一种类型转换成另外一种类型,放到新流中
List<String> list = new ArrayList<>();
Collections.addAll(list, "1", "2", "3", "4", "5", "6", "7");
Stream<Integer> stream = list.stream().map(str -> Integer.parseInt(str) + 1);
stream.forEach(num -> System.out.println(num));
}
}
运行如下:
2
3
4
5
6
7
8
6.concat方法
/**
* @Author: chuxia0811
* @Date: 2021/3/4 23:33
* @Description :
*/
public class ConcactDemo {
public static void main(String[] args) {
//concact作用,合并流
List<String> one = new ArrayList<>();
List<String> two = new ArrayList<>();
Collections.addAll(one, "A", "B", "C");
Collections.addAll(two, "1", "2", "3");
Stream<String> stream1 = one.stream();
Stream<String> stream2 = two.stream();
Stream<String> stream = Stream.concat(stream1, stream2);
stream.forEach(str -> System.out.println(str));
}
}
运行如下:
A
B
C
1
2
3
7.count方法
/**
* @Author: chuxia0811
* @Date: 2021/3/4 23:39
* @Description :
*/
public class CountDemo {
public static void main(String[] args) {
//count方法作用:统计流中元素的个数
List<String> list = new ArrayList<>();
Collections.addAll(list, "A", "B", "C", "D", "E");
Stream<String> stream = list.stream();
System.out.println(stream.count());
}
}
运行结果:
5
四:Stream流使用注意事项
1.一旦调用了终结方法,流将不能再被使用,一旦调用非终结方法,原来旧的流不能再被使用了,只能用新的流进行操作。
2.终结方法:如果返回值不为Stream流,如foreach,count方法;非终结方法:如果返回值为stream流,如filter,limit,skip,map,cancat等方法。
为了解决Stream流终结不能再被使用的情况,可以收集 Stream流,即将Stream流操作后的元素放在新 的容器中(集合或数组)。
/**
* @Author: chuxia0811
* @Date: 2021/3/6 10:48
* @Description :
*/
public class CollectDemo {
/**
* 讲解:以下1~4程序不能同时执行,流被终结了,需要注释其他的,一个个单独运行
* 1.将Stream流结果收集到集合中
* 收集的Stream流放在List集合中:流对象.collect(collectors.toList())
* * 收集的Stream流放在Set集合中:流对象.collect(collectors.toSet())
* <p>
* 2.收集的流放到数组中
* *流对象.toArray()
* *流对象.toArray(数据类型::new)
*/
public static void main(String[] args) {
List<String> list = new ArrayList<>();
Collections.addAll(list, "老子", "庄子", "孟子", "韩非子", "儿子");
Stream<String> stream = list.stream();
//1.收集流到List集合中
List<String> newList = stream.collect(Collectors.toList());
System.out.println(newList.size());
for (String name : newList) {
System.out.println(name);
}
//2.收集到Set集合
Set<String> set = stream.collect(Collectors.toSet());
System.out.println(set.size());
for (String name : set) {
System.out.println(name);
}
//3.收集到Array
Object[] obj = stream.toArray();
for (Object name : obj) {
System.out.println(obj);
}
//4.指定类型的数组,重载的toarry方法
String[] names = stream.toArray(String[]::new);
for (String name : names) {
System.out.println(name);
}
}
}
五:Stream流之串行与并行
/**
* @Author: chuxia0811
* @Date: 2021/3/6 11:14
* @Description :
*/
public class ParalleDemo {
public static void main(String[] args) {
test1();
test2();
}
/**
*测试1~ 1亿的和。串行流
*/
public static void test1(){
Long startTime = System.currentTimeMillis();
Long sum = LongStream.rangeClosed(0L,100000000L).sum();
System.out.println(sum);
Long endTime = System.currentTimeMillis();
System.out.println("串行执行总耗时:"+(endTime-startTime));
}
/**
*测试1~ 1亿的和。并行流
*/
public static void test2(){
Long startTime = System.currentTimeMillis();
Long sum = LongStream.rangeClosed(0L,100000000L).parallel().sum();
System.out.println(sum);
Long endTime = System.currentTimeMillis();
System.out.println("串行执行总耗时:"+(endTime-startTime));
}
}
运行结果如下:
5000000050000000
串行执行总耗时:111
5000000050000000
串行执行总耗时:40
由此可以看出,通过parallel()获得并行流的效率是串行的好几倍,所有数据量比较大的时候建议使用并行流。如果数据量不大建议使用串行流,应为数据量小两者区别不大,创建并行流反而需要消耗资源。
六:方法的引用
1.静态方法的引用
静态方法的引用:类名::静态方法,通过类名来引用静态方法,简化Lambda表达式。
注意事项:
1.被引用的方法和函数式接口中的抽象方法必要要有相同的参数列表;
2.如果函数式接口中的抽象方法有返回值,则被引用的方法必须要有相同类型的返回值;
3.如果函数式接口中的抽象方法没有返回值,则被引用的方法中可以有也可以没有返回值。
/**
* @Author: chuxia0811
* @Date: 2021/3/6 14:23
* @Description :
*/
public class StreamStaticDemo {
public static void main(String[] args) {
int[] arr = {45, 12, 3, 189, -12};
// ArrayUtils::getMax 会创建一个类,实现ArrayHelper接口,再创建了一个实现类对象给arrayHelper;
ArrayHelper arrayHelper = ArrayUtils::getMax;
int max = arrayHelper.maxValue(arr);
System.out.println("数组最大值为:" + max);
}
}
@FunctionalInterface
interface ArrayHelper {
int maxValue(int[] arr);
}
class ArrayUtils {
public static int getMax(int[] arr) {
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
}
2.对象方法的引用
对象方法的引用:对象::方法,通过对象来引用普通已存在方法,简化Lambda表达式。
注意事项同静态方法引用。
/**
* @Author: chuxia0811
* @Date: 2021/3/6 14:56
* @Description :
*/
public class StreamFunDemo {
public static void main(String[] args) {
MyRandom myRandom = new MyRandom();
NumHelper helper = myRandom::nextIntAtoB;
System.out.println(helper.nexIntAtoB(100, 200));
}
}
class MyRandom {
public int nextIntAtoB(int a, int b) {
Random random = new Random();
return random.nextInt(b - a + 1) + a;
}
}
@FunctionalInterface
interface NumHelper {
int nexIntAtoB(int a, int b);
}
3.构造方法的引用
构造方法的引用:对象::new。
/**
* @Author: chuxia0811
* @Date: 2021/3/6 16:12
* @Description :
*/
public class ConstructDemo {
public static void main(String[] args) {
CarFactory factory = Car::new;
System.out.println( factory.makeCar("奔驰"));
}
}
class Car{
private String brand;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public Car(String brand) {
this.brand = brand;
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
'}';
}
}
@FunctionalInterface
interface CarFactory{
Car makeCar(String brand);
}
4.特定类型方法的引用
通过类名引用非静态方法:类名::非静态方法
/**
* @Author: chuxia0811
* @Date: 2021/3/6 16:42
* @Description :
*/
public class StreamFunDemoother {
public static void main(String[] args) {
String[] strs = {"Ba", "Ac", "De", "a", "哈哈哈"};
Arrays.sort(strs, String::compareToIgnoreCase);
for (String str : strs) {
System.out.println(str);
}
}
}