java进阶-JDK1.8之后新特性:Stream流

Stream流

1、概述

java.util.stream.Stream接口,表示能应用在一组元素上,一次执行的操作序列,也就是可以对一组数据进行连续的多次操作。
在这里插入图片描述

Stream在使用的时候,需要指定一个数据源,比如 java.util.Collection的子类,List或者Set都可以,但是Map类型的集合不支持。

Stream是对集合功能的增强,它提供了各种非常便利、高效的聚合操作,可以大批量数据操作,同时再结合Lambda表达式,就可以极大的提高编程效率。

Stream的API提供了串行和并行两种模式进行操作数据
Stream操作分为中间操作或者最终操作两种:

  1. 中间操作:返回Stream本身,这样就可以将多个操作依次串起来(例如map、flatMap、filter、distinct、sorted、peek、limit、skip、parallel、sequential、unordered)
  2. 最终操作:返回特定类型的计算结果(forEach、forEachOrdered、toArray、reduce、collect、min、max、count、anyMatch、allMatch、noneMatch、findFirst、findAny、iterator)

可以把这个Stream操作理解成穿线,把操作都串起来了

例如:

public class Test {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        Collections.addAll(list,1,2,3,4,5,6,7,8);

        list = list.stream()
                .filter(e->e%2==0) //过滤,保留偶数
                .sorted((e1,e2)->e2-e1) //排序,倒序
                .collect(Collectors.toList()); //最终操作,收到一个新的List集合中并返会

        System.out.println(list); //[8, 6, 4, 2]
    }
}

分析:

接口Stream<T> filter(Predicate<? super T> predicate);中的参数是一个Predicate接口,该接口中有个test()抽象方法;接口 Stream<T> sorted(Comparator<? super T> comparator);中的参数是一个比较器Comparator接口类型的;

可以看出,在list集合转为Stream后,可以经过连续的多次数据操作,最后返回我们想要的结果,并且实现功能代码比之前更加优雅、简洁。你甚至可以把stream操作理解成生产车间的流水线作业,一个最初的产品(数据),经过中间多个连续的不同工序(操作),得到最终的产品。

值、数组、集合、基本类型转Stream

可以将现有的数据,转换为Stream对象,然后再使用Stream的API对数据进行一些系列操作。

值转Stream

在这里插入图片描述
通过Arrays.stream()方法将传入的值放到stream对象中,返回stream对象

public Test{

	public static void main(String[] args){
		Stream<String> stream = Stream.of("a","b","c"); //将值转stream
		System.out.println(Arrays.toString(stream.toArray()));
	}
}

分析:传入泛型类型的值,返回一个stream对象,通过stream.toArray()方法将stream对象转换为数组对象,再通过Arrays工具类的toString方法输出数组。

数组转Stream

两个方法:
(1)Arrays工具类中的stream方法可以将数组转换为stream对象
(2)Stream接口中的of方法可以将数组转换为stream对象
例如:

public class Test {
    public static void main(String[] args) {

        String[] arr = {"a","b","c"};
        //第一种方式Arrays.stream(数组)
        Stream<String> stream1 = Arrays.stream(arr);
        //第二种方式:Stream.of(数组)
        //Stream<String> stream2 = Stream.of(arr); 

        //测试
        System.out.println(Arrays.toString(stream1.toArray())); //[a, b, c]
       //System.out.println(Arrays.toString(stream2.toArray())); //[a, b, c]
    }
}

集合转Stream

public class Test {
    public static void main(String[] args) {

        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"a","b","c");
        
        Stream<String> stream = list.stream(); //将集合list转换为stream对象

        System.out.println(Arrays.toString(stream.toArray())); //[a,b,c]
    }
}

基本类型转Stream

虽然也可以用Stream类型,并指定泛型:
Stream
Stream
Stream
但是,在数据量较大的时候,自动拆箱/装箱会比较消耗性能,所以提供了下面三种专门针对基本类型的Stream
对于基本数值类型,有专门的三种Stream类型:
IntStream
LongStream
DoubleStream

例如:以IntStream类型为例

public class Test {
    public static void main(String[] args) {
        IntStream stream1 = IntStream.of(new int[]{1, 2, 3});

        IntStream stream2 = IntStream.range(1, 3); //表示1到3的范围内为左闭右开[1,3)

        //IntStream stream3 = IntStream.rangeClosed(1, 3); //左右都闭[1,3]
        
    }
}

Stream转数组、集合、字符串等

Stream转数组

public class Test {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("hello", "world", "java"); //将三个字符串转换为stream

        String[] strings = stream.toArray(String[]::new); //将stream转换为数组
        //String[]::new是构造器引用中的数组构造引用,且是String类型的数组,通过该方式创建一个数组对象,将stream转换过来的值存放到该数组对象中
        System.out.println(Arrays.toString(strings)); //[hello, world, java]
    }
}

分析,把String[]::new单独拎出来,可以发现它是被IntFunction接收的IntFunction aNew = String[]::new;,查看IntFunction接口源码可以发现里面有一个抽象方法R apply(int value);,通过重写该接口中的apply方法,返回一个数组对象,如下:

IntFunction ic = new IntFunction() {
            @Override
            public Object apply(int value) {
                return new String[value];
            }
        }

简化成lambda表达式如下
IntFunction ic = value->new String[value];
再次简化就成Strig[] ::new

Stream转集合

public class Test {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("hello", "world", "java"); //将三个字符串转换为stream

        List<String> list = stream.collect(Collectors.toList());

//        ArrayList<String> list = stream.collect(Collectors.toCollection(ArrayList::new));

//        Set<String> set = stream.collect(Collectors.toSet());

//        HashSet<String> set = stream.collect(Collectors.toCollection(HashSet::new));
    }
}

分析:collect方法中的参数是Collectors接口对象,作用是将元素收集到一个可以修改的容器中,并返回该容器
在这里插入图片描述
(1)toList()方法源码如下
在这里插入图片描述
主要作用是返回一个List集合,stream.collect()将stream中的内容放到这个List集合中。
(2)toCollection()方法原码如下:
在这里插入图片描述
ArrayList::new(构造方法引用)可以构造一个ArrayList集合,toCollection()方法将元素放到构造的ArrayList中
(3)toSet()方法和HashSet::new也如上两个这么理解

需要注意的是一个Stream在代码只能使用一次,再次使用就会报错,如多次使用一个Stream对象就会抛出以下异常:java.lang.IllegalStateException: stream has already been operated upon or closed

Stream转字符串

public class Test {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("hello", "world", "java");
        String result = stream.collect(Collectors.joining("-")); //使用“-”将字符串连接起来hello-world-java
        System.out.println(result);
    }
}

Stream中的常见操作

最终操作

(1)iterator()方法

public class Test {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("hello", "world", "java");
        Iterator<String> it = stream.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }
    }
}

(2)forEach()方法
将Stream中的每个元素交给Consumer函数处理
在这里插入图片描述

public class Test {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("hello", "world", "java");
        stream.forEach(System.out::println);
    }
}

(3)count()方法
统计流中的元素个数,并返回结果

public class Test {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("hello", "world", "java");
        System.out.println(stream.count());;
    }
}

(4)max()方法
返回流中基于comparator所指定的比较规则,比较出的最大值

public class Test {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("hello", "world", "java");
       /* 实例方法引用:类名::非静态方法名
       Optional<String> max = stream.max(new Comparator<String>(){
            @Override
            public int compare(String o1, String o2) {
                return o1.compareTo(o2);
            }
        });
        */
        Optional<String> max = stream.max(String::compareTo);
        System.out.println(max.get());
    }
}

(5)min()方法
返回流中基于comparator所指定的比较规则,比较出的最小值
在这里插入图片描述

public class Test {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("hello", "world", "java");
       /* 实例方法引用:类名::非静态方法名
       Optional<String> min = stream.min(new Comparator<String>(){
            @Override
            public int compare(String o1, String o2) {
                return o1.compareTo(o2);
            }
        });
        */
        Optional<String> max = stream.min(String::compareTo);
        System.out.println(max.get());
    }
}

(6)collect方法
将元素收集到一个可以修改的容器中,并返回该容器。
在这里插入图片描述
使用样例1:

public class Test {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("hello", "world", "java","python","javascript","zhangsan");
        //把Stream中的元素,按照字符串长度进行分组,长度相同算是一组,并存放到同一个集合中
        //map的key是字符串的长度,value是同一组的数据
        Map<Integer, List<String>> map = stream.collect(Collectors.groupingBy(String::length));

        map.forEach((k,v) -> System.out.println(k + " : " + v));
    }
}


4 : [java]
5 : [hello, world]
6 : [python]
8 : [zhangsan]
10 : [javascript]

使用样例2:

public class Test {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("hello", "world", "java","python","javascript","zhangsan");
        //把Stream中的元素,按照指定条件分割成俩组,条件返回true是一组,条件返回false是另一组
        //map的key是true或者false,value是对应的数据
        // 按照数据中是否包含"java"字符串来进行划分

        Map<Boolean, List<String>> map = stream.collect(Collectors.partitioningBy(s -> s.indexOf("java") != -1));
        map.forEach((k,v) -> System.out.println(k + " : " + v));
    }
}

//结果:
false : [hello, world, python, zhangsan]
true : [java, javascript]

(7)Match()方法
匹配操作,Stream中提供了多种匹配模式

public class Test {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("hello", "world", "java","python","javascript","zhangsan");
        //所有元素匹配成功才返回true 否则返回false
        boolean allMatch = stream.allMatch((s) -> s.startsWith("j"));
        System.out.println(allMatch);

        //任意一个匹配成功就返回true 否则返回false
//        boolean anyMatch = stream.anyMatch((s)->s.startsWith("j"));
//        System.out.println(anyMatch);

        //没有一个匹配的就返回true,否则返回false
//        boolean noneMatch = stream.noneMatch((s) -> s.startsWith("j"));
//        System.out.println(noneMatch);

    }

这些操作不能同时执行,因为一个Stream只能使用一次

(9)findFirst()
返回Stream的第一个元素

public class Test {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("hello", "world", "java","python","javascript","zhangsan");
        Optional<String> first = stream.findFirst();
        System.out.println(first.get()); //hello
    }
}

中间操作

(1)filter方法()
过滤方法,返回满足predicate指定的条件的所有元素的一个新流
在这里插入图片描述

public class Test {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("hello", "world", "java","python","javascript","zhangsan");
       stream.filter(e -> e.contains("o")).forEach(System.out::println);
    }
}

(2)map()方法
对调用流中的元素,应用Function所指定的操作,然后返回一个新流
在这里插入图片描述

public class Test {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("hello", "world", "java","python","javascript","zhangsan");
        List<Integer> list = stream.map(str -> str.length()).collect(Collectors.toList());
        list.forEach(System.out::println);
    }
}
//结果
5
5
4
6
10
8

map生成的是个1:1映射,每个输入元素,都按照规则转换成为另外一个元素

(3)reduce()方法
将一组数据俩俩合并,最后得出一个结果

public class Test {
    public static void main(String[] args) {
//        Stream<String> stream = Stream.of("hello", "world", "java","python","javascript","zhangsan");
        IntStream stream = IntStream.rangeClosed(1, 10);
        //reduce方法需要提供一个起始值(种子)
        // 然后依照运算规则,和Stream中的第一个数据进行操作,得出结果
        // 再将这个结果和Stream中的第二个数据进行操作,再得出结果,依次类推,直到得出最终结果
        int result = stream.reduce(0,(a,b) -> a+b);
        System.out.println(result); //55
    }
}

(4)sorted()方法
排序

public class Test {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("hello", "world", "java","python","javascript","zhangsan");
        //默认自然排序
        stream.sorted().forEach(System.out::println);
    }
}

//输出结果
hello
java
javascript
python
world
zhangsan
public class Test {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("hello", "world", "java","python","javascript","zhangsan");
        //比较器排序,注意Lambda表达式中返回的值前加了符号
        stream.sorted((o1,o2) -> -o1.compareTo(o2)).forEach(System.out::println);
    }
}

(5)limit()方法
返回Stream 的前面 n 个元素

public class Test {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("hello", "world", "java","python","javascript","zhangsan");
        stream.limit(5).forEach(System.out::println); 
    }
}

(6)skip()方法
跳过前 n 个元素只要后面的元素。

public class Test {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("hello", "world", "java","python","javascript","zhangsan");
       stream.skip(3).forEach(System.out::println); //跳过前面3个元素
    }
}

(7)distinct()方法
去除重复数据

public class Test {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("hello","hello", "world", "java","python","javascript","zhangsan");
        stream.distinct().forEach(System.out::println);
    }
}

静态方法

(1)concat()方法
拼接两个流

public class Test {
    public static void main(String[] args) {
        Stream<String> stream1 = Stream.of("hello", "world");
        Stream<String> stream2 = Stream.of("tom", "mary");
        Stream<String> concat = Stream.concat(stream1, stream2);
        concat.forEach(System.out::println);
    }
}

(2)Stream.generate()方法
通过Supplier接口,可以自己来控制数据的生成。
在这里插入图片描述

public class Test {
    public static void main(String[] args) {
        Random random = new Random();

        //生成100个随机数,并输出
        Stream.generate(() -> random.nextInt(100))
                .limit(100)
                .forEach(System.out::println);

        //生成100个随机数,并存放到集合中
        Stream.generate(()->random.nextInt())
                .limit(100)
                .collect(Collectors.toList());
    }
}

Stream.iterate,它跟 reduce 操作很像,需要接受一个起始值(种子),然后通过函数得出一个结果,再把结果当做参数传给函数,再得出第二个结果,依次类推,其实就是一个递归操作。

IO流与Stream

在IO流中,也有方法,可以将读取到的数据转换为Stream对象。
在这里插入图片描述
例如:

public class Test {
    public static void main(String[] args) throws FileNotFoundException {
        BufferedReader br = null;
        br = new BufferedReader(new FileReader("src/com/demo.a.txt"));
        int maxLen = br.lines() //io流转为Stream
                .mapToInt(String::length)  //每行字符串转换为它的字符长度
                .max() //获取最大长度的数字
                .getAsInt(); //返回int类型的数据
        System.out.println(maxLen);

        try {
            br.close();
        } catch (IOException exception) {
            exception.printStackTrace();
        }
    }
}

并行流

Stream有串行和并行两种。串行Stream上的操作是在一个线程中依次完成,而并行Stream则是在多个线程上同时执行。

创建并行Stream的两种方式:
(1)调用串行Stream的parallel()方法,可以将其转换并行Stream

Stream<String> stream = Stream.of("hello","world","java");
Stream<String> parallelStream = stream.parallel();

(2)调用集合对象的parallelStream方法,之后获取并行Stream

List<String> list = new ArrayLIst<>();
Collections.addAll(list,"hello","world","java");

Stream<String> parallelStream = list.parallelStream();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值