JDK8之Stream流入门

Java8中的Stream是对容器对象功能的增强,他专注于对容器对象进行各种非常便利、高效的操作,Stream Api 借助于同样新出现的Lambda表达式,极大的提高编程效率和程序可读性,同时,他提供串行并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势。通常,编写并行代码很难并容易出错,但使用Stream API 无需编写一行多线程的代码,就可以很方便的写出高性能的并发程序。

我觉得我们可以将流看做流水线,这个流水线是处理数据的流水线,一个产品经过流水线会有一道道的工序就如同对数据的中间操作,比如过滤我不需要的,给数据排序,最后的终止操作就是产品从流水线下来,我们就可以统一打包放入仓库了

当我们使用一个流的时候,通常包括三个基本步骤:获取一个数据源—》数据转转–》执行数据操作获取想要的结果。每次转换原有Stream对象不改变,返回一个新的Stream对象(可以有多次转换),这就允许对其操作可以向链条样排列,变成一个管道,如下图所示:

Stream有几个特性:

  • stream不存储数据,而是按照特定的规则对数据进行计算
  • stream不会改变数据源,通常情况下会产生一个新的集合或一个值
  • stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行

在这里插入图片描述

1.Stream流的创建
(1)Stream可以通过集合数组创建

  1. 通过java.util.Collection.stream() 方法用集合创建流,我们发现
  default Stream<E> stream(){
        return StreamSupport.stream(spliterator,false);
  }
List<String> list = Arrays.asList("a","b","c");
// 创建一个顺序流
Stream<String> stream = list.stream();
// 创建一个并行流
Stream<String> parallelStream = list.parallelStream();


// 合并两个流
Stream<String> concat = Stream.concat(list, stream);

(2)使用java.util.Arrays.stream(T[ ] array) 方法用数组创建流

int [] array = {1,3,5,6,8};
IntStream stream = Arrays.stream(array);

(3)使用Stream的静态方法: of()

Stream<Integer> stream = Stream.of(1,2,3,4,5,6);

2.Stream的终止操作
为了方便我们后续使用,我们先初始化一部分数据

public class Person {
    private String name; // 姓名
    private int salary; // 薪资
    private int age;  // 年龄
    private String sex; //性别
    private String area; // 地区


    public Person(String name, int salary, int age, String sex, String area) {
        this.name = name;
        this.salary = salary;
        this.age = age;
        this.sex = sex;
        this.area = area;
    }

    public Person(){

    }
getting、setting和toString()省略。。

初始化数据,我们设计一个简单的集合和一个复杂的集合

public class lamdaTest {

  List<Person> personList  = new ArrayList<Person>();
  List<Integer> simpleList = Arrays.asList(15,22,9,11,33,52,14);


  @Before
  public void initData(){
    personList.add(new Person("张三",3000,23,"男","太原"));
    personList.add(new Person("李四",7000,34,"男","西安"));
    personList.add(new Person("王五",5200,22,"女","太原"));
    personList.add(new Person("小黑",1500,33,"女","上海"));
    personList.add(new Person("狗子",8000,44,"女","北京"));
    personList.add(new Person("铁蛋",6200,36,"女","南京"));

  }

(1)遍历/匹配(foreach/find/match)

 @Test
  public void foreachTest(){
      //  遍历打印打印集合元素
    simpleList.stream().forEach(System.out::println);
      // 其实可以简化操作
    simpleList.forEach(System.out::println);

  }


  // 找寻元素
  @Test
  public void findTest(){
      // 拿到任意一个元素
    Optional<Integer> any = simpleList.stream().findAny();
      // 拿到第一个元素
    Optional<Integer> first = simpleList.stream().findFirst();
      // 如果存在该元素,则打印
    any.ifPresent(System.out::println);
    /**
     * 在单线程环境下,any和first拿到的其实是同一个数据,使用findAny()是为了更高效的性能。
     * 如果是数据较少,串行地 情况下,一般会返回第一个结果,但是在多线程的环境下
     * stream会把流进行截断分成几部分,那摩每个线程拿到的数据就可能不一样了,
     */
         // 多线程环境下
    Optional<Integer> firstMore = simpleList.parallelStream().findFirst();
    firstMore.ifPresent(System.out::println);
    Optional<Integer> anyMore = simpleList.parallelStream().findAny();
    anyMore.ifPresent(System.out::println);

  }

  // 匹配元素
  @Test
  public void MatchTest(){
    // 是不是所有人的工资都大于8k
    boolean b = personList.stream().allMatch(p -> p.getSalary() > 5000);
    System.out.println(b);
    // 是不是有人的工资大于5000
    boolean flag = personList.stream().anyMatch(person -> person.getSalary() > 5000);
    System.out.println(flag);
  }

(3) 统计(count/averaging/sum/max/min)

// 统计(count/averaging/sum/max/min)
  @Test
  public void test(){
      // 统计集合中有多少个对象
    long count = personList.stream().count();
    System.out.println(count);

    // 平均年龄
    OptionalDouble average = simpleList.stream().mapToInt(i -> i).average();
    average.ifPresent(System.out::println);

    // 求和
    int sum = IntStream.of(1, 2, 3, 4, 5).sum();
    System.out.println(sum);


    // 求最大值 new Random().ints() 会生成一个包含20个数的Stream流
    OptionalInt max = new Random().ints(20).max();
    // max.ifPresent(System.out::println);

//    max.ifPresent(new IntConsumer() {
//      @Override
//      public void accept(int value) {
//        System.out.println(value);
//      }
//    });

    max.ifPresent(value -> System.out.println(value));

    // 求最小值
    OptionalInt min = new Random().ints(20).min();
    min.ifPresent(System.out::println);
    
    // 求工资最高的员工
    Optional<Person> max1 = personList.stream().max((o1, o2) -> o1.getSalary() - o2.getSalary());
    max1.ifPresent(person -> System.out.println("工资最高的员工是:"+ person));
  }

(3) 归约(reduce)
归约,也称缩减,顾名思义,是把一个流缩减成一个值,能实现对集合求和、求乘积和求最值的操作

// 规约
    @Test
   public void reduceTest(){
         // 1代表一开始的值是多少 n1代表结果,n2代表当前遍历到的哪个值,在一次遍历之后将n1*n2的值赋值给n1
      int reduce = IntStream.of(2, 1, 3, 4).reduce(1, (n1, n2) -> n1 * n2);
      System.out.println(reduce);
      int reduce1 = IntStream.of(2, 1, 3, 4).reduce(0, (n1, n2) -> n1 + n2);
      System.out.println(reduce1);
    }

(5)接合(joining)

//joining可以将Stream中的元素用特定的连接符(没有的话,则直接连接)连接成一个字符串。
  @Test public void joiningTest(){
    List<String> list = Arrays.asList("A", "B", "C");
    String string = list.stream().collect(Collectors.joining("-"));
    System.out.println("拼接后的字符串:" + string); 
  }

(6)分组(partitioningBy/groupingBy)

  • 分区: 将stream按条件分为两个Map,比如员工按薪资是否高于8000分为两部分
  • 分组:将集合分为多个Map,比如员工按照性别分组

在这里插入图片描述

// 分区/分组
  @Test
  public void groupingByTest(){
      // 将工资大于5k的分true组,不满足的分到false组,这就是分区
    Map<Boolean, List<Person>> collect = personList.stream().collect(Collectors.partitioningBy(person -> person.getSalary() > 5000));
    System.out.println(collect);

      // 分组,根据性别分组
    Map<String, List<Person>> collect1 = personList.stream().collect(Collectors.groupingBy(person -> person.getSex()));
    System.out.println(collect1.get("男"));

  }

(6) 归集(toList/toSet/toMap)
因为流不存储数据,那摩在流中的数据完成处理后,需要将流中的数据重新归集到新的集合里,toList、toSet和toMap比较常用

//归集(toList/toSet/toMap)
  @Test
  public void collectTest(){
    List<Integer> collect = simpleList.stream().collect(Collectors.toList());
    Set<Integer> collect1 = simpleList.stream().collect(Collectors.toSet());
         // 构造出k-名字,v-person对象的Map集合
    personList.stream().collect(Collectors.toMap(person -> person.getName(),person -> person));
  }

3.Stream中间操作
(1)筛选(filter)
该操作符需要传入一个function函数

// 筛选(filter)/ 过滤
  @Test
  public void filterTest(){
       // 将工资大于5000的人过滤出来放到List集合中
    List<Person> collect = personList.stream().filter(person -> person.getSalary() > 5000).collect(Collectors.toList());
    System.out.println(collect);
  }

(2) 映射(map)
映射,可以将一个流的元素按照一定的映射规则映射到另外一个流中,分为map和flatMap

  • map: 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
  • flatMap: 接收一个函数作为参数,将流中的每个值都换成另外一个流,然后把所有的流连接成一个流
// 映射map
  @Test
  public void mapTest(){
       // 将所有员工的工资印射成一个List集合,集合里面存放的是所有员工的薪资
    List<Integer> collect = personList.stream().map(person -> person.getSalary()).collect(Collectors.toList());

  List<Person> collect1  = personList.stream().map(p ->{
       p.setSalary(p.getSalary()+1000);
       return p;
    }).collect(Collectors.toList());


  }

(3)排序(sorted)
sorted,中间操作,有两种排序:

  • sorted(): 自然排序,流中元素需实现Comparable接口
  • sorted(Comparator com): Comparator 排序器自定义排序
// 排序
  @Test
  public void sortTest(){
    // 将员工的工资按照从低到高排序
   personList.stream().sorted((p1,p2)-> p1.getSalary() - p2.getSalary()).forEach(System.out::println);
  }

//(4)peek操作

//(4)peek操作
  @Test
  public void peekTest(){
      // 修改元素的信息,给每个员工涨工资1k
    List<Person> collect = personList.stream().peek(person -> person.setSalary(person.getSalary() + 1000)).collect(Collectors.toList());
  }

(5) 其他操作
流也可以进行合并、去重、限制、跳过等操作

@Test
  public void otherTest(){
       // limit  找出工资前三名的人
    personList.stream().sorted(((o1, o2) -> o2.getSalary() - o1.getSalary())).limit(3).forEach(System.out::println);

        // 去掉集合中前两个元素之后进行剩下的元素去重操作
       IntStream.of(1,2,3,3,5,5,6,6,7,8).distinct().skip(2).forEach(System.out::println);


  }
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值