JAVA中Optional和stream流入门

JAVA中Optional和stream流入门,第1张

文章目录
  • 1、Optional
    • Optional(T value)
    • Optional of(T value)
    • empty()
    • ofNullable(T value)
    • orElse(T other) and orElseGet(
    • orElseThrow
    • map(Function mapper) 和 flatMap(Function> mapper) 取值
    • isPresent()和ifPresent(Consumer consumer)
    • filter(Predicate predicate)
  • 2.实战
    • 对象取值
    • 判null写逻辑
    • 过滤一些元素取值
    • Optional循环遍历集合
  • 3.Stream流
    • 收集转化
    • 转换新的Collectors.toList()
    • 转换新的Collectors.toMap(T,E)
    • 转换新的Collectors.toSet()
    • 转换新的Collectors.toCollection(TreeSet::new)
    • filter
    • findFirst找到第一个元素
    • findAny匹配任意(适用于并行流)
    • anyMatch是否包含符合特定条件的元素
    • max 找最大 min 找最小
      • 获取String集合中最长的元素。
      • 获取Integer集合中的最大值
      • 计算Integer集合中大于6的元素的个数
    • 映射(map/flatMap)
      • 英文字符串数组的元素全部改为大写。整数数组每个元素+3。
      • 将两个字符数组合并成一个新的字符数组。
    • 归约(reduce)
      • 求Integer集合的元素之和、乘积和最大值。
    • 统计(count/averaging)
    • 分组(partitioningBy/groupingBy)
    • 接合(joining)
    • 排序(sorted)
    • 提取/组合(合并、去重、限制、跳过)
    • 分页 *** 作
    • 集合转Map *** 作

参考ImportNew

1、Optional

先说明一下,Optional(T value),即构造函数,它是private权限的,不能由外部调用的。其余三个函数是public权限,供我们所调用。那么,Optional的本质,就是内部储存了一个真实的值,在构造的时候,就直接判断其值是否为空。好吧,这么说还是比较抽象。直接上Optional(T value)构造函数的源码,如下图所示

Optional(T value)

/**
     * Constructs an instance with the value present.
     *
     * @param value the non-null value to be present
     * @throws NullPointerException if value is null
     */
    private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }
     /**
     * Constructs an empty instance.
     *
     * @implNote Generally only one empty instance, {@link Optional#EMPTY},
     * should exist per VM.
     */
    private Optional() {
        this.value = null;
    }
Optional of(T value)
 /**
   * Returns an {@code Optional} with the specified present non-null value.
   *
   * @param  the class of the value
   * @param value the value to be present, which must be non-null
   * @return an {@code Optional} with the value present
   * @throws NullPointerException if value is null
   */
  public static <T> Optional<T> of(T value) {
      return new Optional<>(value);
  }

也就是说of(T value)函数内部调用了构造函数。根据构造函数的源码我们可以得出两个结论:

  • 通过of(T value)函数所构造出的Optional对象,当Value值为空时,依然会报NullPointerException。
  • 通过of(T value)函数所构造出的Optional对象,当Value值不为空时,能正常构造Optional对象。

除此之外呢,Optional类内部还维护一个value为null的对象,大概就是长下面这样的
因为实例化的时候会调用以下方法

public static <T> T requireNonNull(T obj) {
        if (obj == null)
            throw new NullPointerException();
        return obj;
    }

如果值为空 会 抛空指针哟

empty()

Optional类内部还维护一个value为null的对象,大概就是长下面这样的


public final class Optional<T> {
    //省略....
    private static final Optional<?> EMPTY = new Optional<>();
    private Optional() {
        this.value = null;
    }
    //省略...
    public static<T> Optional<T> empty() {
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }
}

那么,empty()的作用就是返回EMPTY对象。

ofNullable(T value)

好了铺垫了这么多,可以说ofNullable(T value)的作用了,上源码

 public static <T> Optional<T> ofNullable(T value) {
    return value == null ? empty() : of(value);
}

好吧,大家应该都看得懂什么意思了。相比较of(T value)的区别就是,当value值为null时,of(T value)会报NullPointerException异常;ofNullable(T value)不会throw Exception,ofNullable(T value)直接返回一个EMPTY对象。

那是不是意味着,我们在项目中只用ofNullable函数而不用of函数呢?

不是的,一个东西存在那么自然有存在的价值。当我们在运行过程中,不想隐藏NullPointerException。而是要立即报告,这种情况下就用Of函数。但是不得不承认,这样的场景真的很少。博主也仅在写junit测试用例中用到过此函数。

orElse(T other) and orElseGet(

这三个函数放一组进行记忆,都是在构造函数传入的value值为null时,进行调用的。orElse和orElseGet的用法如下所示,相当于value值为null时,给予一个默认值:

这两个函数的区别:当user值不为null时,orElse函数依然会执行createUser()方法,而orElseGet函数并不会执行createUser()方法,大家可自行测试。

orElseThrow

至于orElseThrow,就是value值为null时,直接抛一个异常出去,用法如下所示

 User user = null;
        Optional.ofNullable(user).orElseThrow(()->new Exception("用户不存在"));
map(Function mapper) 和 flatMap(Function> mapper) 取值

这两个函数做的是转换值的 *** 作。

public final class Optional<T> {
 //省略....
  public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
     Objects.requireNonNull(mapper);
     if (!isPresent())
         return empty();
     else {
         return Optional.ofNullable(mapper.apply(value));
     }
 }
 //省略...
  public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
     Objects.requireNonNull(mapper);
     if (!isPresent())
         return empty();
     else {
         return Objects.requireNonNull(mapper.apply(value));
     }
 }
}

这两个函数,在函数体上没什么区别。唯一区别的就是入参,map函数所接受的入参类型为Function,而flapMap的入参类型为Function>。

如果User结构是下面这样的

public class User {
    private String name;
    public String getName() {
        return name;
    }
}

这时候取name的写法如下所示

String city = Optional.ofNullable(user).map(u-> u.getName()).get();

对于flatMap而言:

如果User结构是下面这样的

public class User {
    private String name;
    public Optional<String> getName() {
        return Optional.ofNullable(name);
    }
}

这时候取name的写法如下所示

String city = Optional.ofNullable(user).flatMap(u-> u.getName()).get();
isPresent()和ifPresent(Consumer consumer)

这两个函数放在一起记忆,isPresent即判断value值是否为空,而ifPresent就是在value值不为空时,做一些 *** 作。这两个函数的源码如下

public final class Optional<T> {
    //省略....
    public boolean isPresent() {
        return value != null;
    }
    //省略...
    public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }
}

需要额外说明的是,大家千万不要把

if (user != null){
   // TODO: do something
}

给写成

User user = Optional.ofNullable(user);
if (Optional.isPresent()){
   // TODO: do something
}

因为这样写,代码结构依然丑陋。博主会在后面给出正确写法

至于ifPresent(Consumer consumer),用法也很简单,如下所示

Optional.ofNullable(user).ifPresent(u->{
    // TODO: do something
});


如果user 为null的话 不会进入这一段逻辑

filter(Predicate predicate)

源码

public final class Optional<T> {
    //省略....
   Objects.requireNonNull(predicate);
        if (!isPresent())
            return this;
        else
            return predicate.test(value) ? this : empty();
}

filter 方法接受一个 Predicate 来对 Optional 中包含的值进行过滤,如果包含的值满足条件,那么还是返回这个 Optional;否则返回 Optional.empty。

用法如下

Optional<User> user1 = Optional.ofNullable(user).filter(u -> u.getName().length()<6);

如上所示,如果user的name的长度是小于6的,则返回。如果是大于6的,则返回一个EMPTY对象。

2.实战

不过采用这种链式编程,虽然代码优雅了。但是,逻辑性没那么明显,可读性有所降低,大家项目中看情况酌情使用。

对象取值

before

public String getCity(User user)  throws Exception{
        if(user!=null){
            if(user.getAddress()!=null){
                Address address = user.getAddress();
                if(address.getCity()!=null){
                    return address.getCity();
                }
            }
        }
        throw new Excpetion("取值错误");
    }

now

public String getCity(User user) throws Exception{
    return Optional.ofNullable(user)
                   .map(u-> u.getAddress())
                   .map(a->a.getCity())
                   .orElseThrow(()->new Exception("取指错误"));
}
判null写逻辑

before

if(user!=null){
    dosomething(user);
}

now

 Optional.ofNullable(user)
    .ifPresent(u->{
        dosomething(u);
});
过滤一些元素取值

before

public User getUser(User user) throws Exception{
    if(user!=null){
        String name = user.getName();
        if("zhangsan".equals(name)){
            return user;
        }
    }else{
        user = new User();
        user.setName("zhangsan");
        return user;
    }
}

now


public User getUser(User user) {
    return Optional.ofNullable(user)
                   .filter(u->"zhangsan".equals(u.getName()))
                   .orElseGet(()-> {
                        User user1 = new User();
                        user1.setName("zhangsan");
                        return user1;
                   });
}
Optional循环遍历集合

before

List<String> userList = getList();

if (list != null) {

for(String user: list){

System.out.println(user);

}

}

now

List<String> userList = getList();

Optional.ofNullable(userList).orElse(new ArrayList<>()).forEach(user -> {

System.out.println(user);

});

3.Stream流 收集转化
  • List.stream();//将List转化成stream流;
  • Stream.collect(转化规则);//将List按照转化规则转化成stream流;
转换新的Collectors.toList()
   public static List<User> createListUser() {
        User user = new User();
        user.setName("zhangsan");

        User user2 = new User();
        user2.setName("lisi");

        User user3 = new User();
        user3.setName("wangwu");

        System.out.println("执行了createListUser方法");
        return Lists.newArrayList(user,user2,user3);
    }
   
   List<User> listUser = createListUser();
   List<User> collect = listUser.stream().collect(Collectors.toList());

转换新的Collectors.toMap(T,E)
Map<T,E> new= old.stream().collect(Collectors.toMap(T,E));
转换新的Collectors.toSet()
Set<> new =old.stream().Collectors.toSet();
转换新的Collectors.toCollection(TreeSet::new)
TreeSet<> new =old.stream().Collectors.toCollection(TreeSet::new);
filter

过滤名字长度大于5的

findFirst找到第一个元素
Optional<User> zhangsan = listUser.stream().filter(u -> u.getName().equals("zhangsan")).findFirst();
        System.out.println(zhangsan.get());
--找到张三

findAny匹配任意(适用于并行流)

listUser.parallelStream().filter(user -> user.getName().length() > 5).findAny().get();
anyMatch是否包含符合特定条件的元素
listUser.stream().anyMatch(user -> user.getName().equals("zhangsan"));
-- true
max 找最大 min 找最小
User user1 = listUser.stream().max(Comparator.comparing(user -> user.getName().length())).get();
        User user2 = listUser.stream().min(Comparator.comparing(user -> user.getName().length())).get();
        System.out.println(user1);
        System.out.println(user2);
结果
User(name=zhangsan, phone=null, address=null)
User(name=lisi, phone=null, address=null)
获取String集合中最长的元素。
public static void main(String[] args) {
        List<String> list = Arrays.asList("adnm", "admmt", "pot", "xbangd", "weoujgsd");
        Optional<String> max = list.stream().max(Comparator.comparing(String::length));
        System.out.println("最长的字符串:" + max.get());
    }

获取Integer集合中的最大值
public static void main(String[] args) {
        List<Integer> list = Arrays.asList(7, 6, 9, 4, 11, 6);
        // 自然排序
        Optional<Integer> max = list.stream().max(Integer::compareTo);
        // 自定义排序
        Optional<Integer> max2 = list.stream().max(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1.compareTo(o2);
            }
        });
        System.out.println("自然排序的最大值:" + max.get());
        System.out.println("自定义排序的最大值:" + max2.get());
    }

计算Integer集合中大于6的元素的个数
public static void main(String[] args) {
        List<Integer> list = Arrays.asList(7, 6, 4, 8, 2, 11, 9);
        long count = list.stream().filter(x -> x > 6).count();
        System.out.println("list中大于6的元素个数:" + count);
    }

映射(map/flatMap)

映射,可以将一个流的元素按照一定的映射规则映射到另一个流中。分为map和flatMap:map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

英文字符串数组的元素全部改为大写。整数数组每个元素+3。
public static void main(String[] args) {
        String[] strArr = { "abcd", "bcdd", "defde", "fTr" };
        List<String> strList = Arrays.stream(strArr).map(String::toUpperCase).collect(Collectors.toList());
        System.out.println("每个元素大写:" + strList);
        List<Integer> intList = Arrays.asList(1, 3, 5, 7, 9, 11);
        List<Integer> intListNew = intList.stream().map(x -> x + 3).collect(Collectors.toList());
        System.out.println("每个元素+3:" + intListNew);
    }

将两个字符数组合并成一个新的字符数组。
public static void main(String[] args) {
        List<String> list = Arrays.asList("m,k,l,a", "1,3,5,7");
        List<String> listNew = list.stream().flatMap(s -> {
            // 将每个元素转换成一个stream
            String[] split = s.split(",");
            Stream<String> s2 = Arrays.stream(split);
            return s2;
        }).collect(Collectors.toList());
        System.out.println("处理前的集合:" + list);
        System.out.println("处理后的集合:" + listNew);
    }

归约(reduce)

归约,也称缩减,顾名思义,是把一个流缩减成一个值,能实现对集合求和、求乘积和求最值 *** 作。

求Integer集合的元素之和、乘积和最大值。
public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 3, 2, 8, 11, 4);
        // 求和方式1
        Optional<Integer> sum = list.stream().reduce(Integer::sum);
        // 求和方式2
        Optional<Integer> sum2 = list.stream().reduce(Integer::sum);
        // 求和方式3
        // 第一个参数会增加到sum的求和中
        Integer sum3 = list.stream().reduce(0, Integer::sum);
        // 求乘积
        Optional<Integer> product = list.stream().reduce((x, y) -> x * y);
        // 求最大值方式1
        Optional<Integer> max = list.stream().reduce((x, y) -> x > y ? x : y);
        // 求最大值写法2
        //这里的第一个参数是 拿这个数加入里面比较
        Integer max2 = list.stream().reduce(1, Integer::max);
        System.out.println("list求和:" + sum.get() + "," + sum2.get() + "," + sum3);
        System.out.println("list求积:" + product.get());
        System.out.println("list最大值:" + max.get() + "," + max2);
    }

统计(count/averaging)

Collectors提供了一系列用于数据统计的静态方法:计数:count平均值:averagingInt、averagingLong、averagingDouble最值:maxBy、minBy求和:summingInt、summingLong、summingDouble统计以上所有:summarizingInt、summarizingLong、summarizingDouble

public static void main(String[] args) {
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        // 求总数
        long count = personList.size();
        // 求平均工资
        Double average = personList.stream().collect(Collectors.averagingDouble(Person::getSalary));
        // 求最高工资
        Optional<Integer> max = personList.stream().map(Person::getSalary).max(Integer::compare);
        // 求工资之和
        int sum = personList.stream().mapToInt(Person::getSalary).sum();
        // 一次性统计所有信息
        DoubleSummaryStatistics collect = personList.stream().collect(Collectors.summarizingDouble(Person::getSalary));
        System.out.println("员工总数:" + count);
        System.out.println("员工平均工资:" + average);
        System.out.println("员工最高工资:" + max.get());
        System.out.println("员工工资总和:" + sum);
        System.out.println("员工工资所有统计:" + collect);
    }
员工总数:3
员工平均工资:7900.0
员工最高工资:8900
员工工资总和:23700
员工工资所有统计:DoubleSummaryStatistics{count=3, sum=23700.000000, min=7000.000000, average=7900.000000, max=8900.000000}

分组(partitioningBy/groupingBy)

分区:将stream按条件分为两个Map,比如员工按薪资是否高于8000分为两部分。
分组:将集合分为多个Map,比如员工按性别分组。有单级分组和多级分组。
案例:将员工按薪资是否高于8000分为两部分;将员工按性别和地区分组

public static void main(String[] args) {
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "Washington"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "New York"));
        personList.add(new Person("Anni", 8200, 24, "female", "New York"));
        // 将员工按薪资是否高于8000分组
        Map<Boolean, List<Person>> part = personList.stream().collect(Collectors.partitioningBy(x -> x.getSalary() > 8000));
        // 将员工按性别分组
        Map<String, List<Person>> group = personList.stream().collect(Collectors.groupingBy(Person::getSex));
        // 将员工先按性别分组,再按地区分组
        Map<String, Map<String, List<Person>>> group2 = personList.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.groupingBy(Person::getArea)));
        System.out.println("员工按薪资是否大于8000分组情况:" + part);
        System.out.println("员工按性别分组情况:" + group);
        System.out.println("员工按性别、地区:" + group2);
    }

接合(joining)

joining可以将stream中的元素用特定的连接符(没有的话,则直接连接)连接成一个字符串。

public static void main(String[] args) {
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        String names = personList.stream().map(Person::getName).collect(Collectors.joining(","));
        System.out.println("所有员工的姓名:" + names);
        List<String> list = Arrays.asList("A", "B", "C");
        String string = list.stream().collect(Collectors.joining("-"));
        System.out.println("拼接后的字符串:" + string);
    }

排序(sorted)

sorted,中间 *** 作。有两种排序:

sorted():自然排序,流中元素需实现Comparable接口
sorted(Comparator com):Comparator排序器自定义排序

案例:将员工按工资由高到低(工资一样则按年龄由大到小)排序

public static void main(String[] args) {
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Sherry", 9000, 24, "female", "New York"));
        personList.add(new Person("Tom", 8900, 22, "male", "Washington"));
        personList.add(new Person("Jack", 9000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 8800, 26, "male", "New York"));
        personList.add(new Person("Alisa", 9000, 26, "female", "New York"));
        // 按工资升序排序(自然排序) 从小到大
        List<String> newList = personList.stream().sorted(Comparator.comparing(Person::getSalary)).map(Person::getName)
                .collect(Collectors.toList());
        // 按工资倒序排序
        List<String> newList2 = personList.stream().sorted(Comparator.comparing(Person::getSalary).reversed())
                .map(Person::getName).collect(Collectors.toList());
        // 先按工资再按年龄升序排序
        List<String> newList3 = personList.stream()
                .sorted(Comparator.comparing(Person::getSalary).thenComparing(Person::getAge)).map(Person::getName)
                .collect(Collectors.toList());
        // 先按工资再按年龄自定义排序(降序)
        List<String> newList4 = personList.stream().sorted((p1, p2) -> {
            if (p1.getSalary() == p2.getSalary()) {
                return p2.getAge() - p1.getAge();
            } else {
                return p2.getSalary() - p1.getSalary();
            }
        }).map(Person::getName).collect(Collectors.toList());
        System.out.println("按工资升序排序:" + newList);
        System.out.println("按工资降序排序:" + newList2);
        System.out.println("先按工资再按年龄升序排序:" + newList3);
        System.out.println("先按工资再按年龄自定义降序排序:" + newList4);
    }

按工资升序排序:[Lily, Tom, Sherry, Jack, Alisa]
按工资降序排序:[Sherry, Jack, Alisa, Tom, Lily]
先按工资再按年龄升序排序:[Lily, Tom, Sherry, Jack, Alisa]
先按工资再按年龄自定义降序排序:[Alisa, Jack, Sherry, Tom, Lily]

提取/组合(合并、去重、限制、跳过)

流也可以进行合并、去重、限制、跳过等 *** 作。

public static void main(String[] args) {
        String[] arr1 = { "a", "b", "c", "d" };
        String[] arr2 = { "d", "e", "f", "g" };
        Stream<String> stream1 = Stream.of(arr1);
        Stream<String> stream2 = Stream.of(arr2);
        // concat:合并两个流 distinct:去重
        List<String> newList = Stream.concat(stream1, stream2).distinct().collect(Collectors.toList());
        // limit:限制从流中获得前n个数据
        List<Integer> collect = Stream.iterate(1, x -> x + 2).limit(10).collect(Collectors.toList());
        // skip:跳过前n个数据
        List<Integer> collect2 = Stream.iterate(1, x -> x + 2).skip(1).limit(5).collect(Collectors.toList());
        System.out.println("流合并:" + newList);
        System.out.println("limit:" + collect);
        System.out.println("skip:" + collect2);
    }
流合并:[a, b, c, d, e, f, g]
limit:[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
skip:[3, 5, 7, 9, 11]
分页 *** 作

stream api 的强大之处还不仅仅是对集合进行各种组合 *** 作,还支持分页 *** 作。

例如,将如下的数组从小到大进行排序,排序完成之后,从第1行开始,查询10条数据出来, *** 作如下:

 public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5, 10, 6, 20, 30, 40, 50, 60, 100);
        List<Integer> dataList = numbers.stream().sorted(Integer::compareTo).skip(0).limit(10).collect(Collectors.toList());
        System.out.println(dataList.toString());
    }
[2, 2, 3, 3, 3, 5, 6, 7, 10, 20]


集合转Map *** 作

在实际的开发过程中,还有一个使用最频繁的 *** 作就是,将集合元素中某个主键字段作为key,元素作为value,来实现集合转map的需求,这种需求在数据组装方面使用的非常多。

public static void main(String[] args) {
    List<Person> personList = new ArrayList<>();
    personList.add(new Person("Tom",7000,25,"male","安徽"));
    personList.add(new Person("Jack",8000,30,"female","北京"));
    personList.add(new Person("Lucy",9000,40,"male","上海"));
    personList.add(new Person("Airs",10000,40,"female","深圳"));
    Map<Integer, Person> collect = personList.stream().collect(Collectors.toMap(Person::getAge, v -> v, (k1, k2) -> k1));
    System.out.println(collect);
}


{40=Person{name='Lucy', salary=9000, age=40, sex='male', area='上海'}, 25=Person{name='Tom', salary=7000, age=25, sex='male', area='安徽'}, 30=Person{name='Jack', salary=8000, age=30, sex='female', area='北京'}}

(k1,k2)->k1 相当于ke有相同的情况下 value取前面还是后面

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/868375.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-13
下一篇 2022-05-13

发表评论

登录后才能评论

评论列表(0条)

保存