Java 常见Stream操作

Java 常见Stream操作

基础知识点

转载请标明出处:鸭梨的药丸哥

流概念

Java 8 API添加了一个新的抽象称为流 Stream,可以使用链式编程的方式对数据进行批处理。

整个流操作就是一条流水线,将元素放在流水线上一个个地进行处理。和迭代器又不同的是,Stream 可以并行化操作(并行流),迭代器只能命令式地、串行化操作。

串行流和并行流

Java提供了两种流,分别是:串行流和并行流。性能上并行流更好,但是需要考虑线程安全问题。

  • Stream:串行流,单线程,线程安全,适合阻塞任务
  • parallelStream:并行流,多线程,线程不安全,其底层使用Fork/Join框架实现

数值流

Java提供了三种数值流,是专门对数值进行操作的,分别是IntStream,LongStream,DoubleStream,相比普通的流,数值流提供了格外的方法。

数值流主要针对原始数据类型,分别是 int,double,long。

如果使用普通的流进行求和,如:

int sum = list.stream().map(User::getAge).reduce(0, Integer::sum);操作中存在int的包装和解包(int和Integer的互转),影响效率。

如果我们通过int sum = list.stream().mapToInt(User::getAge).sum();就可以避免int的包装和解包装。

流和数值流的转换:

  • mapToInt(T -> int) : return IntStream
  • mapToDouble(T -> double) : return DoubleStream
  • mapToLong(T -> long) : return LongStream

对象转换

在Java中数组是对象,想要int[]直接转List是不能的,因为一个对象是不能直接转换为一个列表的,所以需要中间封装。

int[]转List,需要走以下几步:

  • int[]封装成IntStream
  • IntStreamStream<Integer>
  • Stream<Integer>List

char[]转List,因为没有CharStream,所以需要使用String做中间封装,需要走以下几步:

  • char[]封装成String
  • StringIntStream
  • IntStreamList

int[] []转List< List>,因为int[]是被看作对象,所以需要使用map操作将每一个int[]转换为List,需要走以下几步:

  • int[][]封装成Stream<int[]>
  • Stream<int[]>使用map函数对int[]元素转List,变成Stream<List<Integer>>
  • Stream<List<Integer>>List<List>

巧用flatMap去展平二维数组,具体操作如下:

  • int[][]封装成Stream<int []>
  • Stream<int []>调用flatMap展平数组

巧用Map去遍历数组中元素,具体操作如下:

  • 从集合Collection接口的实现类获取Stream
  • Stream中使用map函数对元素进行操作
  • 使用.collect(Collectors.toList())再次变成list

Comparator和Collectors

  • Collectors:一个工具类,提供集合的相关方法
  • Comparator:一个函数式接口,提供一些对比的方法

语法糖

Java提供了很多语法糖,这里只是简单介绍一下

lambda语法,主要有以下几种

  • o->{} //对象->{函数体}
  • (k,v)->{} //(键,值)->{函数体}
  • o->t //对象->返回值
  • 类::方法 //代表使用某指定方法

数据结构互转

int[]互转List

注意:long,double这种数值型数组同理
《阿里巴巴 Java 开发手册》的描述如下:

使用集合转数组的方法,必须使用集合的 toArray(T[] array),传入的是类型完全一致、长度为 0 的空数组。

//int[]转List
int[] ints = new int[5];
//方法一
List<Integer> collect = Arrays.stream(ints)
                                .boxed()
                                .collect(Collectors.toList());
//方法二
List<Integer> collect = Arrays.stream(ints)
                                .mapToObj((Integer::new))
                                .collect(Collectors.toList());

//List转int[]
List<Integer> arr = new ArrayList<>();
//方法一
Integer[] integers = arr.toArray(new Integer[0]);
//方法二
int[] ints = arr.stream()
                .mapToInt(Integer::new)
                .toArray();

int[] []互转List< List >

注意:long,double这种数值型数组同理

//int[][]转List<List>
int[][] ints = new int[5][5];
//方法一
List<List<Integer>> collect = Arrays.stream(ints)
                                    .map(o -> Arrays.stream(o).boxed().collect(Collectors.toList()))
                                    .collect(Collectors.toList());
//方法二
List<List<Integer>> collect = Arrays.asList(ints)
                                    .stream()
                                    .map(o -> Arrays.stream(o).boxed().collect(Collectors.toList()))
                                    .collect(Collectors.toList());


//List<List>转int[][]
List<List> arr = new ArrayList<>();
//方法一
int[][] objects = (int[][]) arr.toArray();

int[] []互转List< int[] >

注意:long,double这种数值型数组同理

//int[][]转List<int[]>
int[][] ints = new int[5][5];
List<int[]> collect = Arrays.stream(ints)
    						.collect(Collectors.toList());


//List<int[]>转int[][]
List<int[]> list = new ArrayList<>();
int[][] ints = (int[][])list.toArray();

char[]转List

char[] chars = new char[0];
//char[]转List
List<Character> collect = new String(chars).chars()
                                            .mapToObj(c -> (char) c)
                                            .collect(Collectors.toList());

//List转char[]
Character[] characters = collect.toArray(new Character[collect.size()]);

int[] []展平为List

//int[][]转List<List>
int[][] ints = new int[0][0];
List<Integer> collect = Arrays.stream(ints)
                                .flatMap(arr->Arrays.stream(arr).boxed())
                                .collect(Collectors.toList());

List< List >展平为List

List<List> arr = new ArrayList<>();
List<?> collect = arr.stream()
                        .flatMap(list -> (Stream<?>) list.stream())
                        .collect(Collectors.toList());

Object[]互转List

//Object[]转List
Object[] objects = new Object[0];
//方法一
List<Object> collect = Arrays.stream(objects)
    							.collect(Collectors.toList());
//方法二
List<Object> collect = Arrays.asList(objects);

//List转Object[]
List<Object> arr = new ArrayList<>();
Object[] objects = arr.toArray();

Object[] []互转List< List >

//object[][]转List<List>
Object[][] objects = new Object[0][0];
//方法一
List<List<Object>> collect = Arrays.stream(objects)
                                    .map(o -> Arrays.stream(o).collect(Collectors.toList()))
                                    .collect(Collectors.toList());
//方法二
List<List<Object>> collect = Arrays.asList(objects)
                                    .stream()
                                    .map(Arrays::asList)
                                    .collect(Collectors.toList());

//List<List>转Object[][]
List<List> list = new ArrayList<>();
Object[][] objects = (Object[][])list.toArray();

常用操作

聚合

sum(数组求和)

double等同理

int[] ints = {1,2,3,4,5};
int sum = Arrays.stream(ints).sum();
count(统计个数)

count方法用来统计当前流中元素的个数,一般是在一些流操作(过滤,去重)后才使用。

ArrayList<User> users = new ArrayList<>();
users.add(new User("ste",12));
users.add(new User("jack",14));
users.add(new User("tom",16));
long count = users.stream().count();
max,min(寻找最值)
ArrayList<User> users = new ArrayList<>();
users.add(new User("ste",12));
users.add(new User("jack",14));
users.add(new User("tom",16));
//年龄最大的user
User maxuser = users.stream()
                    .max(Comparator.comparingInt(User::getAge))
                    .get();
//年龄最小的user
User minuser = users.stream()
                    .min(Comparator.comparingInt(User::getAge))
                    .get();
avg(计算平均值)
ArrayList<User> users = new ArrayList<>();
users.add(new User("ste",12,"男"));
users.add(new User("jack",14,"男"));
users.add(new User("tom",16,"男"));

//计算年龄均值
double avg = users.stream()
                    .mapToInt(User::getAge)
                    .average()
                    .getAsDouble();

遍历

forEach(串行流遍历)

串行Stream遍历数据是安装当前流中数据的顺序,Stream()方法返回串行流,串行Stream按照流中当前数据的顺序进行遍历。

ArrayList<User> users = new ArrayList<>();
users.add(new User("ste",12));
users.add(new User("jack",14));
users.add(new User("tom",16));
//遍历
users.stream().forEach(o->{
	System.out.println(o);
});
forEach(并行流遍历)

并行流遍历数据不一定按照流中数据的顺序,parallelStream()方法返回并行流,并行流相比串行流效率更高,但是无法确保数据顺序,在数据顺序不做要求的操作中可以使用并行流提高效率。

ArrayList<User> users = new ArrayList<>();
users.add(new User("ste",12));
users.add(new User("jack",14));
users.add(new User("tom",16));

users.parallelStream().forEach(o->{
    System.out.println(o);
});

筛选

filter(过滤)

使用filter方法过滤数据,保留为 true 的元素,过滤false的数据。

ArrayList<User> users = new ArrayList<>();
users.add(new User("ste",12));
users.add(new User("jack",14));
users.add(new User("tom",16));
//过滤掉不是ste的user
List<User> collect = users.stream()
                            .filter(user -> user.getName().equals("ste"))//返回true时数据被保留
                            .collect(Collectors.toList());
distinct(去重)

distinct()依赖于类的equals 方法,对比对象值时,请记得重写equals方法,否则只是对比引用值。

//User类已经重写equals方法
ArrayList<User> users = new ArrayList<>();
users.add(new User("jack",12));
users.add(new User("jack",12));
users.add(new User("tom",12));
//去重后的数据
List<User> collect = users.stream()
                            .distinct()
                            .collect(Collectors.toList());
limit和skip(截流)

limit和skip一般用来截取流中元素

//截取前n个元素
List<User> collect = users.stream()
                            .limit(2)
                            .collect(Collectors.toList());

//跳过前n个元素
List<User> collect2 = users.stream()
                            .skip(1)
                            .collect(Collectors.toList());

排序

sort(排序)

排序又两个方法,一是sort(),二是sorted(Comparator<? super T> comparator)

  • sort()使用对象的compareTo()方法(需要实现Comparable接口)
  • sorted(Comparator<? super T> comparator)使用自定义的对比方法
@Data
@AllArgsConstructor
public class User implements Comparable{

    private String name;
    private int age;

    /**
     * 返回0,代表等于
     * 返回负数,代表当前对象小于传入对象
     * 返回正数,代表当前对象大于传入对象
     * @param o
     * @return
     */
    @Override
    public int compareTo(Object o) {
        return getAge()-((User)o).getAge();
    }
}

//使用
ArrayList<User> users = new ArrayList<>();
users.add(new User("ste",12));
users.add(new User("jack",14));
users.add(new User("tom",16));
//升序(如果想要降序,将上面compareTo对比换个位置即可)
List<User> collect = users.stream()
                            .sorted()
                            .collect(Collectors.toList());
//使用自定义的排序方法
List<User> collect1 = users.stream()
                            .sorted((u1, u2) -> u1.getAge() - u2.getAge())
                            .collect(Collectors.toList());

//使用Comparator里面的方法
List<User> collect2 = users.stream()
                            .sorted(Comparator.comparingInt(User::getAge))
                            .collect(Collectors.toList());

映射

map(映射)

map操作常常用来做类型转换或者对象映射处理操作,R->T。

//修改年龄
List<User> collect = users.stream()
                        .map(o -> {
                            o.setAge(55);
                            return o;
                        }).collect(Collectors.toList());
//提取名字
List<String> collect1 = users.stream()
                            .map(User::getName)
                            .collect(Collectors.toList());
flatMap(展平)

flatMap先进行Map操作,将每个元素转为流,如果在对流进行合并,所以flatMap里面的lambda函数返回值是流

//int[][]转List<List>
int[][] ints = new int[0][0];
List<Integer> collect = Arrays.stream(ints)
                                .flatMap(arr->Arrays.stream(arr).boxed())
                                .collect(Collectors.toList());

归约

reduce(汇总,归约)

reduce常常用来做求和操作,第一个参数代表了累加的初始值和累加的类型。对于一些浮点数的累加我们可以使用BigDecimal来避免数位的损失

ArrayList<User> users = new ArrayList<>();
users.add(new User("ste",12));
users.add(new User("jack",14));
users.add(new User("tom",16));
//对年龄求和,这里的0代表累加的初始值
Integer reduce = users.stream()
                        .map(User::getAge)
                        .reduce(0, Integer::sum);

//对于一些浮点数的累加我们可以使用BigDecimal来避免数位的损失
BigDecimal totalPrice = goods.stream()
                            .map(Goods::getPrice())
                            .reduce(BigDecimal.ZERO, BigDecimal::add);

//接口源码注释如下,可得知第一个参数的初始值
	/* <pre>{@code
     *     T result = identity;
     *     for (T element : this stream)
     *         result = accumulator.apply(result, element)
     *     return result;
     * }</pre>
     */

匹配

match匹配有三种:

  • 某一元素匹配:anyMatch()
  • 全部匹配:allMatch()
  • 没有一个匹配:noneMatch()
allMatch(全部匹配)
ArrayList<User> users = new ArrayList<>();
users.add(new User("ste",12));
users.add(new User("jack",14));
users.add(new User("tom",16));

//是否全部人都是12岁
boolean b1 = users.stream()
    			.allMatch(user -> user.getAge() == 12);
noneMatch(全不匹配)
//是否没人是10岁
boolean b2 = users.stream()
    				.noneMatch(user -> user.getAge() == 10);
anyMatch(存在匹配)
//是否存在一个叫tom的人
boolean b3 = users.stream()
    			.anyMatch(user -> user.getName().equals("tom"));

收集

collect(收集)

可以通过collect进行不同的收集操作,如分组收集。

ArrayList<User> users = new ArrayList<>();
users.add(new User("ste",12));
users.add(new User("jack",14));
users.add(new User("tom",16));
users.add(new User("zhangsan",16));
//返回set(底层其实是HashSet)
Set<User> collect1 = users.stream().collect(Collectors.toSet());
//返回List(其实是ArrayList)
List<User> collect2 = users.stream().collect(Collectors.toList());
//返回map(底层其实是HashMap),名字的k,年龄是v
Map<String, Integer> collect3 = users.stream()
    .collect(Collectors.toMap(User::getName, User::getAge));

//返回指定的collection类型,这里选择LinkedList
LinkedList<User> collect4 = users.stream()
    .collect(Collectors.toCollection(LinkedList::new));
Collectors.groupingBy(分组)

利用collect()和Collectors.groupingBy()方法可以实现分组收集的功能。

ArrayList<User> users = new ArrayList<>();
users.add(new User("ste",12));
users.add(new User("jack",14));
users.add(new User("tom",16));
users.add(new User("zhangsan",16));
//按年龄分组,年龄是k,List是v
Map<Integer, List<User>> collect = users.stream()
    .collect(Collectors.groupingBy(User::getAge));
Collectors.groupingBy(多重分组)

Collectors.groupingBy两个参数的方法可以向下分组。

ArrayList<User> users = new ArrayList<>();
users.add(new User("ste",12,"男"));
users.add(new User("jack",14,"男"));
users.add(new User("tom",16,"男"));
users.add(new User("zhangsan",16,"男"));
//先按年龄分组,在按性别分组
Map<Integer, Map<String, List<User>>> collect5 = users.stream()
    .collect(Collectors
    .groupingBy(User::getAge, Collectors.groupingBy(User::getSex)));

源码解读

public static <T, K, A, D>
Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,Collector<? super T, A, D> downstream) {
    //底层使用HashMap,downstream向下传递新的Collector
        return groupingBy(classifier, HashMap::new, downstream);
    }
Collectors.groupingBy(分组统计)
ArrayList<User> users = new ArrayList<>();
users.add(new User("ste",12,"男"));
users.add(new User("jack",14,"男"));
users.add(new User("tom",16,"男"));
users.add(new User("zhangsan",16,"男"));

//按性别分组,然后按年龄统计
Map<String, Integer> collect = users.stream().collect(Collectors.groupingBy(User::getSex, Collectors.summingInt(User::getAge)));
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值