stream流

获取流的方法
llist Collection.stream
map map.keySet().stream()
数组 Stream.of(array)
中间方法
filter 可以用于条件过滤
map 如果需要将流中的元素映射到另一个流中,可以使用 map 方法
limit 取用前几个(截取)
skip 跳过前几个元素
concat 组合(合并流)

distinct

去除流中重复的元素(使用hashcode和equals方法来对比)
flatMap 映射(打开后再转换)
sorted 自然排序
sorted(Comparator com) 定制排序

collect(Collector c)

收集
iterate 迭代
peek 查看
终结方法
allMatch 检测匹配:
count 统计个数
forEach 用于遍历

findFirst,findAny

查找第一个或其中的一个元素,返回Optional类型

max(comparator c)

min(comparator c)

查找最大最小值

reduce

规约

Stream流介绍

forEach为什么不能return?
根本原因:在lambda表达式中的return并不会终止循环,这是由于lambda的底层实现导致的

Stream(流)是一个来自数据源的元素队列并支持聚合操作

Stream操作的两个基础特征
Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

stream流_第1张图片

图中通过Collection.stream()方法得到Head也就是stage0,紧接着调用一系列的中间操作,不断产生新的Stream。这些Stream对象以双向链表的形式组织在一起,构成整个流水线,由于每个Stage都记录了前一个Stage和本次的操作以及回调函数,依靠这种结构就能建立起对数据源的所有操作。这就是Stream记录操作的方式。 

————————————————

 

获取Stream流的方式

java.util.stream.Stream 是Java 8新加入的流接口。(并不是一个函数式接口)
获取一个流非常简单,有以下几种常用的方式:

  • 所有的 Collection 集合都可以通过 stream 默认方法获取流(顺序流);
  • 所有的 Collection 集合都可以通过parallelStream获取并行流
  • Stream 接口的静态方法 of 可以获取数组对应的流。
  • Arrays的静态方法stream也可以获取流

根据Collection获取流

public static void main(String[] args) {
    
	List list = new ArrayList<>();
	Stream stream1 = list.stream();
    
	Set set = new HashSet<>();
	Stream stream2 = set.stream();
    
	Vector vector = new Vector<>();
	// ...
}    

根据Map获取流

public static void main(String[] args) {
    
	Map map = new HashMap<>();
	
	Stream keyStream = map.keySet().stream();
	Stream valueStream = map.values().stream();
	Stream> entryStream = map.entrySet().stream();
}

根据数组获取流

如果使用的不是集合或映射而是数组,由于数组对象不可能添加默认方法,所以 Stream 接口中提供了静态方法of ,使用很简单:

public static void main(String[] args) {
    //使用 Stream.of
	String[] array = { "张无忌", "张翠山", "张三丰", "张一元" };
	Stream stream = Stream.of(array);
    
    //使用Arrays的静态方法
    Arrays.stream(array)
}

Stream流中的常用方法

这些方法可以被分成两种:

  • 中间方法:返回值类型仍然是 Stream 接口自身类型的方法,因此支持链式调用。(除了终结方法外,其余方法均为中间方法。)
  • 终结方法:返回值类型不再是 Stream 接口自身类型的方法,因此不再支持类似 StringBuilder 那样的链式调用。本小节中,终结方法包括 count 和 forEach 方法。

1.forEach(终结方法)

用于遍历的方法,参数传入一个函数式接口:Consumer

public static void main(String[] args) {
	Stream stream = Stream.of("张无忌", "张三丰", "周芷若");
	stream.forEach(name‐> System.out.println(name));
}
2.过滤:filter

可以用于条件过滤

可以通过 filter 方法将一个流转换成另一个子集流。

public static void main(String[] args) {

        //创建一个流
        Stream stream = Stream.of("张三丰", "刘德华", "张国荣", "彭于晏", "纳什", "吴彦祖", "吴绮蓉");

        //对流中元素过滤,只要姓张的人
        Stream stream2 = stream.filter(name -> {
            return name.startsWith("张");
        });
        
        //遍历过滤后的流
        stream2.forEach(name -> System.out.println(name));


    }
3.映射(转换):map

如果需要将流中的元素映射到另一个流中,可以使用 map 方法。

该接口需要一个 Function 函数式接口参数

此前我们已经学习过 java.util.stream.Function 函数式接口,其中唯一的抽象方法为

R apply(T t);

这可以将一种T类型转换成为R类型,而这种转换的动作,就称为“映射"

map使用方法

/**
     * stream流的map方法练习
     * map方法可以将流中的元素映射到另一个流中
     * map方法的参数是一个Function函数式接口
     */
    @Test
    public void test(){

        //创建一个流,里面是字符串类型的整数
        Stream stream1 = Stream.of("2", "32", "2", "33", "2");
        
        //把stream1流中的整数全部转成int类型
        Stream stream2 = stream1.map((String s) -> {
            return Integer.parseInt(s);
        });

        //遍历
        stream2.forEach((i)-> System.out.println(i));


    }

4.统计个数:count(终结方法)

正如旧集合 Collection 当中的 size 方法一样,流提供 count 方法来数一数其中的元素个数:

该方法返回一个long值代表元素个数(不再像旧集合那样是int值)。基本使用:

public class Demo09StreamCount {
    
	public static void main(String[] args) {
        
		Stream original = Stream.of("张无忌", "张三丰", "周芷若");
        
        //筛选姓张的
		Stream result = original.filter(s ‐> s.startsWith("张"));
        
        //输出个数
		System.out.println(result.count()); // 2
	}
}
5.取用前几个(截取):limit

limit 方法可以对流进行截取,只取用前n个。

参数是一个long型,如果集合当前长度大于参数则进行截取;否则不进行操作。基本使用:

public class Demo10StreamLimit {
    
	public static void main(String[] args) {
		Stream original = Stream.of("张无忌", "张三丰", "周芷若");
        //截取前两个
		Stream result = original.limit(2);
        
		System.out.println(result.count()); // 2
	}
}
6.跳过前几个元素:skip

如果希望跳过前几个元素,可以使用 skip 方法获取一个截取之后的新流:

Stream skip(long n);

如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流。基本使用

public class Demo11StreamSkip {
    
	public static void main(String[] args) {
        
		Stream original = Stream.of("张无忌", "张三丰", "周芷若");
        //跳过前两个,返回一个新的流
		Stream result = original.skip(2);
		System.out.println(result.count()); // 1
	}
}
7.组合(合并流):concat

如果有两个流,希望合并成为一个流,那么可以使用 Stream 接口的静态方法 concat :

public class Demo12StreamConcat {
    
	public static void main(String[] args) {
		Stream streamA = Stream.of("张无忌");
		Stream streamB = Stream.of("张翠山");
        //合并成一个新的流
		Stream result = Stream.concat(streamA, streamB);
	}
}

8.筛选:distinct

去除流中重复的元素(使用hashcode和equals方法来对比)

9.映射(打开后再转换):flatMap

内部传入一个Function函数式接口,跟map的区别就是这个会把流中的元素打开,再组合成一个新的流

// map和flatMap的练习
public class StreamDemo {

    @Test
    public void test(){
        List list = Arrays.asList("aa","bb","cc","dd");

        // 练习1 (map) 输出的全是大写
        list.stream().map(s -> s.toUpperCase()).forEach(System.out::println);
        System.out.println("----------");

        // 练习2(map)流里还有流,需要两个遍历才行看到里面内容
        Stream> streamStream = list.stream().map(StreamDemo::fromStringToStream);
        streamStream.forEach(s -> {
            s.forEach(System.out::println);
        });

        System.out.println("---------");
        // 练习3(flatMap)流里还有流,使用flatMap可以直接把里面的流打开,一次遍历就可以了
        Stream characterStream = list.stream().flatMap(StreamDemo::fromStringToStream);
        characterStream.forEach(System.out::println);
        
    }

    /**
     *  将字符串中的多个字符构成的集合转换为对应的stream
     * @param str
     * @return
     */
    public static Stream fromStringToStream(String str){
        ArrayList list = new ArrayList();
        // 将字符串转成字符数组,并遍历加入list集合
        for(Character c : str.toCharArray()){
            list.add(c);
        }
        // 返回list集合的stream流
        return list.stream();
    }
}
10.自然排序:sorted

看下一条里的代码

11.定制排序:sorted(Comparator com)
 /**
     * 排序的练习
     */
    @Test
    public void test2(){

        List integers = List.of(124, 2, 15, 12, 51, -5, 5);
        // 按照自然排序
        integers.stream().sorted().forEach(System.out::println);

        System.out.println("---------");

        List integers2 = List.of(124, 2, 15, 12, 51, -5, 5);
        // 定制排序(大到小),需要传入Comparator接口(如果流中的是引用类型,只能用定制排序)
        // 简写:integers2.stream().sorted((e1,e2) -> e2-e1).forEach(System.out::println);
        integers2.stream().sorted((e1,e2) -> {
            return e2-e1;
        }).forEach(System.out::println);
    }
12.检测匹配(终结方法):

返回一个Boolean值

是否全部匹配:allMatch

是否至少匹配一个:anyMatch

是否没有匹配的:noneMatch

 /**
     * 匹配的练习
     */
    @Test
    public void test3(){
        List integers = List.of(124, 2, 15, 12, 51, -5, 5);
        // 判断是否全部大于5
        boolean b = integers.stream().allMatch(i -> i > 5);
        // 结束输出false
        System.out.println(b);

        System.out.println("-------");

        List integers2 = List.of(124, 2, 15, 12, 51, -5, 5);
        // 检测是否匹配至少一个元素
        boolean b1 = integers2.stream().anyMatch(i -> i > 5);
        // 输出true
        System.out.println(b1);

        System.out.println("-------");

        List integers3 = List.of(124, 2, 15, 12, 51, -5, 5);
        // 检查是否没有匹配的元素
        boolean b2 = integers3.stream().noneMatch(i -> i > 1000);
        // 输出true,全部不匹配
        System.out.println(b2);
    }
13.查找元素(终结方法)

查找第一个元素:findFirst,返回Optional类型

查找其中一个元素:findAny,返回Optional类型

public void test4(){
        List integers = List.of(124, 2, 15, 12, 51, -5, 5);
        // 输出第一个元素
        Optional first = integers.stream().findFirst();
        // 输出结果是Optional[124]
        System.out.println(first);

        System.out.println("-------------");

        List integers2 = List.of(124, 2, 15, 12, 51, -5, 5);
        // 返回其中一个元素
        Optional any = integers2.stream().findAny();
        System.out.println(any);
    }
14.查找最大最小值(终结方法)

max(comparator c)

min(comparator c)

 /**
     * 查找最大最小值
     */
    @Test
    public void test5(){
        List list = new ArrayList<>();
        list.add(new Person("马化腾",25,3000));
        list.add(new Person("李彦宏",27,2545));
        list.add(new Person("雷军",35,4515));
        list.add(new Person("马云",55,9877));

        //  查找年龄最大的人
        Optional max = list.stream().max((e1, e2) -> e1.getAge() - e2.getAge());
        // 返回马云,55岁年龄最大
        System.out.println(max.get());

        System.out.println("--------");
15.规约(终结方法)

reduce(T identity ,BinaryOperator) 第一个参数是初始值,第二个参数是一个函数式接口

reduce(BinaryOperator) 参数是一个函数式接口

/**
     * 归约的练习
     */
    @Test
    public void test6(){
        List integers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        // 求集合里数字的和(归约)reduce第一个参数是初始值。
        Integer sum = integers.stream().reduce(0, Integer::sum);
        System.out.println(sum);

        System.out.println("-------");

        List list = new ArrayList<>();
        list.add(new Person("马化腾",25,3000));
        list.add(new Person("李彦宏",27,2545));
        list.add(new Person("雷军",35,4515));
        list.add(new Person("马云",55,9877));
        // 求所有人的工资和(归约)
        // 不用方法引用写法:Optional reduce = list.stream().map(person -> person.getSalary()).reduce((e1, e2) -> e1 + e2);
        Optional reduce = list.stream().map(Person::getSalary).reduce(Integer::sum);
        // 输出Optional[19937]
        System.out.println(reduce);

    }

16.收集(中间方法)

collect(Collector c):将流转化为其他形式,接收一个Collector接口的实现

/**
     * 收集的练习
     */
    @Test
    public void test7(){
        List list = new ArrayList<>();
        list.add(new Person("马化腾",25,3000));
        list.add(new Person("李彦宏",27,2545));
        list.add(new Person("雷军",35,4515));
        list.add(new Person("马云",55,9877));
        // 把年龄大于30岁的人,转成一个list集合
        List collect = list.stream().filter(person -> person.getAge() > 30).collect(Collectors.toList());
        // 遍历输出(输出雷军和马云)
        for (Person person : collect) {
            System.out.println(person);
        }

        System.out.println("----------");

        List list2 = new ArrayList<>();
        list2.add(new Person("马化腾",25,3000));
        list2.add(new Person("李彦宏",27,2545));
        list2.add(new Person("雷军",35,4515));
        list2.add(new Person("马云",55,9877));
        // 把姓马的人,转成Set集合
        Set set = list2.stream().filter(person -> person.getName().startsWith("马")).collect(Collectors.toSet());
        // 输出马云和马化腾
        set.forEach(System.out::println);

    }

17.迭代:iterate

可以使用Stream.iterate创建流值,即所谓的无限流。

//Stream.iterate(initial value, next value)
	Stream.iterate(0, n -> n + 1)
                .limit(5)
                .forEach(x -> System.out.println(x));

输出:

0
1
2
3
4    
18.查看:peek

peek接收的是一个Consumer函数,peek 操作会按照 Consumer 函数提供的逻辑去消费流中的每一个元素,同时有可能改变元素内部的一些属性

@Test
    public void test1(){
        List collect = Stream.of("one", "two", "three", "four")
                .filter(e -> e.length() > 3)
                .peek(e -> System.out.println("查看一下刚过滤出的值:" + e))
                .map(String::toUpperCase)
                .peek(e -> System.out.println("查看一下转大写之后的值:" + e))
                .collect(Collectors.toList());
        System.out.println("-----分割线-----");
        // 遍历过滤后的集合
        for (String s : collect) {
            System.out.println(s);
        }
    }

输出:

查看一下刚过滤出的值:three
查看一下转大写之后的值:THREE
查看一下刚过滤出的值:four
查看一下转大写之后的值:FOUR
-----分割线-----
THREE
FOUR

Srtream流中方法的使用练习

/**
 * 	1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
 *	2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。
 *	3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。
 *	4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。
 *	5. 将两个队伍合并为一个队伍;存储到一个新集合中。
 *	6. 根据姓名创建 Person 对象;存储到一个新集合中。
 *	7. 打印整个队伍的Person对象信息。
 */
public class Demo3 {

    public static void main(String[] args) {


        //第一支队伍
        ArrayList one = new ArrayList<>();
        one.add("迪丽热巴");
        one.add("宋远桥");
        one.add("苏星河");
        one.add("石破天");
        one.add("石中玉");
        one.add("老子");
        one.add("庄子");
        one.add("洪七公");
        //第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
        //第一个队伍筛选之后只要前3个人;存储到一个新集合中。
        Stream stream = one.stream();
        Stream stream1 = stream.filter(name -> name.length() == 3).limit(3);



        //第二支队伍
        ArrayList two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("张无忌");
        two.add("赵丽颖");
        two.add("张三丰");
        two.add("尼古拉斯赵四");
        two.add("张天爱");
        two.add("张二狗");
        //第二个队伍只要姓张的成员姓名;存储到一个新集合中。
        //第二个队伍筛选之后不要前2个人;存储到一个新集合中。
        Stream stream2 = two.stream();
        Stream stream3 = stream2.filter(name -> name.startsWith("张")).skip(2);

        //合并两个队伍
        Stream concat = Stream.concat(stream1, stream3);

        //把合并后的队伍根据姓名创建Person对象,并存入新的集合中,然后打印
        concat.map(name -> new Person(name)).forEach(p -> System.out.println(p));
    }
}

你可能感兴趣的:(java基础,java)