JDK1.8的新特性之Stream API

JDK1.8的新特性之Stream API

Stream流的介绍

  1. stream 不会存储数据,而是按照特定的规则进行对数据的计算,一般会输出结果

  2. stream 不会改变源数据,通常情况下会产生一个新的集合

  3. stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行

  4. 对stream操作分为终端操作和中间操作,那么这两者又分别代表什么呢?

    • 终端操作:会消费流,这个操作会产生一个结果的,如果一个流被消费了,那他就不会被重用的

    • 中间操作:中间操作会产生一个流,因此中间操作可以用来创建一系列的动作的管道。一个特别注意的点,中间操作不是立马发生的。相反,当在中间操作创建的新流上执行玩终端操作后,中间操作指定的操作才会发生。所以中间操作是延迟发生的,中间操作的延迟行为主要是让流API能够更加高效的执行

获取流的方式

获取流的方式非常简单,有以下几种常见的方式:

  1. 所有的collection集合都可以通过stream默认方法来获取流(顺序流)

  2. 所有的collection集合都可以通过parallelStream获取并行流

  3. Stream接口的静态方法of可以获取数组的对应流

  4. Arrays的静态方法stream也可以获取流

根据Collections获取流

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

根据Map获取流

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

根据数组获取流

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

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

Stream流中的常用方法

这些方法可以分成两种:

  • 延迟方法:返回值类型仍然是Stream接口自身类型的方法,因此支持链式调用(除了终结方法外,其余的都是延迟方法)

  • 终结方法:返回值类型不再是Stream接口自身类型的方法,因此不支持链式调用

1. forEach(终结方法)

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

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

2. filter过滤

可以用于过滤,通过filter方法将一个流转换为另一个子集流

public static void main(String[] args) {

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

    //对流中元素过滤,只要姓张的人
    Stream<String> stream2 = stream.filter(name -> {
        return name.startsWith("张");
    });

    //遍历过滤后的流
    stream2.forEach(name -> System.out.println(name));
    }

3. map映射(转换)

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

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

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

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

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

    }

4. count统计方法(终结方法)

正如Collections中的size()方法一样,流提供了count方法来数一数其中的元素个数,该方法返回一个long值代表元素个数(不再像以往的集合返回int值)。基本使用:

public class Demo09StreamCount {
    
	public static void main(String[] args) {
        
		Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
        
        //筛选姓张的
		Stream<String> 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<String> original = Stream.of("张无忌", "张三丰", "周芷若");
        //截取前两个
		Stream<String> result = original.limit(2);
        
		System.out.println(result.count()); // 2
	}
}

6. skip 跳过前几个元素

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

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

7. concat 组合流

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

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

8. distinct 筛选重复

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

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

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

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

    @Test
    public void test(){
        List<String> 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<Stream<Character>> streamStream = list.stream().map(StreamDemo::fromStringToStream);
        streamStream.forEach(s -> {
            s.forEach(System.out::println);
        });

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

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

10. sorted 排序

 /**
     * 排序的练习
     */
    @Test
    public void test2(){

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

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

        List<Integer> 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);
    }

11. 检测匹配(终结方法)

返回一个Boolean值

是否全部匹配:allMatch

是否至少匹配一个:anyMatch

是否没有匹配:noneMatch

 /**
     * 匹配的练习
     */
    @Test
    public void test3(){
        List<Integer> 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<Integer> 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<Integer> integers3 = List.of(124, 2, 15, 12, 51, -5, 5);
        // 检查是否没有匹配的元素
        boolean b2 = integers3.stream().noneMatch(i -> i > 1000);
        // 输出true,全部不匹配
        System.out.println(b2);
    }

12. 查找元素(终结方法)

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

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

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

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

        List<Integer> integers2 = List.of(124, 2, 15, 12, 51, -5, 5);
        // 返回其中一个元素
        Optional<Integer> any = integers2.stream().findAny();
        System.out.println(any);
    }

13. 查找最大最小值(终结方法)

max(comparator c)

min(comparator c)

 @Test
    public void maxTest(){
        List<Person> 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<Person> max = list.stream().max((e1, e2) -> e1.getAge() - e2.getAge());
        // 返回马云,55岁年龄最大
        System.out.println(max.get());
        System.out.println("---------");

        Optional<Person> min = list.stream().min((p1, p2) -> {
            return p1.getAge() - p2.getAge();
        });
        System.out.println(min.get());

    }

14. 规约(终结方法)求和?

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

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

/**
     * 归约的练习
     */
    @Test
    public void test6(){
        List<Integer> 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<Person> 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<Integer> reduce = list.stream().map(person -> person.getSalary()).reduce((e1, e2) -> e1 + e2);
        Optional<Integer> reduce = list.stream().map(Person::getSalary).reduce(Integer::sum);
        // 输出Optional[19937]
        System.out.println(reduce);

    }

15. collect 收集(终结方法)

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

 @Test
    public void collectTest(){
        List<Person> 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<Person> collect = list.stream().filter(person -> person.getAge() > 30).collect(Collectors.toList());
        // 遍历输出(输出雷军和马云)
        for (Person person : collect) {
            System.out.println(person);
        }

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

        List<Person> 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<Person> set = list2.stream().filter(person -> person.getName().startsWith("马")).collect(Collectors.toSet());
        // 输出马云和马化腾
        set.forEach(System.out::println);

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

        //map是将原有的list进行选取某个属性组成一个新的list
        //filter是将原有的list通过定义的规则进行对list筛选,只会减少数量,list里的类型不变
        List<Integer> list1 = list2.stream().map((person) -> person.getMoney()).collect(Collectors.toList());
        list1.forEach(System.out::println);
    }
}

16. iterate 迭代

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

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

17. peek 查看

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

@Test
    public void test1(){
        List<String> 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值