JKD8新特性之List的Stream流操作

1.测试用例准备

首先我们可以创建一个实体类Node,属性有title和value。

public class Node {
    private String title;
    private int value;
}

然后声明一个Node集合。

List<Node> list = new ArrayList<>();
list.add(new Node("测试一", 1));
list.add(new Node("测试二", 10));
list.add(new Node("测试三", 99));

2.Stream流操作详解

1)stream() / parallelStream()

这两个方法是把集合转化成流,其中parallelStream() 是并行流方法,能够让数据集执行并行操作。

2)collect(toList())

把流转为List类型。

3)filter(T -> boolean)

筛选数据,将T中满足条件的元素过滤出来。将上述三个方法组合使用,就可以过滤出一个满足固定条件的新集合,例如执行下面的代码就会得到一个包含value为1的Node的集合。

list = list.stream()
        .filter(node -> node.getValue() == 1)
        .collect(Collectors.toList());

4)distinct()

去除重复数据,需要注意的是该方法是通过equals()方法来判断是否重复的,所以像例子中的Node类去重时需要重写equals()方法。

5)sorted() / sorted((T, T) -> int)

如果流中的元素的类实现了 Comparable 接口,即有自己的排序规则,那么可以直接调用 sorted() 方法对元素进行排序,反之则需要调用sorted((T, T) -> int)自己定义排序规则。示例如下:

list = list.stream()
        .sorted((p1, p2) -> p1.getValue() - p2.getValue())
        .collect(Collectors.toList());

6)limit(long n)/skip(long n)

两者用法相似,limit为只返回前n个元素,skip为去除前n个元素。同时调用两个方法时,根据两者的顺序不同,会先返回/去除前n个元素,再去除/返回m个元素,示例如下:

list = list.stream()
        .limit(2)
		.skip(1)
        .collect(Collectors.toList());

7)map(T -> R)

类型转换,将T元素映射为R元素,如下面的代码就是返回了一个值为原集合的title的新String集合。同时还展示了另一种写法,调用Node类的getTitle()可以写为Node::getTitle。

List<String> newlist = list.stream().map(Node::getTitle).collect(Collectors.toList());

8)flatMap(T -> Stream)

将流中的每一个元素 T 映射为一个流,再把所有流连接成为一个流,如下面的例子就是是把 List 中每个字符串元素以","分割开,变成一个新的 List。

List<String> list = new ArrayList<>();
list.add("aaa,bbb,ccc");
list.add("ddd,eee,fff");
list.add("ggg,hhh,iii");

list = list.stream().map(s -> s.split(","))
	.flatMap(Arrays::stream).collect(Collectors.toList());

首先 map 方法分割每个字符串元素,但此时流的类型为 String[],因为 split 方法返回的是 String[] 类型;所以我们需要使用 flatMap 方法,先使用Arrays::stream将每个 String[] 元素变成一个 String 流,然后 flatMap 会将每一个流连接成为一个流,最终返回我们需要的 String集合。

9)anyMatch(T -> boolean)/allMatch(T -> boolean)/noneMatch(T -> boolean)

这三个方法相似,都是判断方法,anyMatch是判断流中是否有任一符合条件的元素,allMatch是判断流中是否所有元素都符合条件,noneMatch是判断流中是否没有元素符合条件。示例如下:

boolean b = list.stream()
             .anyMatch(Node -> Node.getValue() == 1);

10)findAny()/findFirst()

findAny()在使用 stream() 时找到的是第一个元素,使用 parallelStream() 并行时找到的是其中一个元素;findFirst()为找到第一个元素。这两个方法返回的是一个Optional对象,该对象为一个容器类,能代表一个值存在或不存在。

11)reduce((T, T) -> T)/reduce(T, (T, T) -> T)

用于组合或处理流中的元素,如求和,求积,求最大值等。示例如下:

//计算value总和:
int sum = list.stream().map(Node::getValue)
        .reduce(0, (a, b) -> a + b);
//另一种写法:
int sum = list.stream().map(Node::getValue)
        .reduce(0, Integer::sum);

其中第一个参数0代表起始值为0,若不传起始值,则要考虑结果可能不存在的情况,因此返回的是 Optional 类型,示例如下:

Optional<Integer> sum = list.stream().map(Node::getValue)
        .reduce(Integer::sum);

12)count()

该方法会返回流中元素的个数。

13)forEach()

该方法无返回结果,可以当作普通的循环使用,例如直接打印每个元素,示例如下:

list.stream().forEach(System.out::println);

3实际应用

该部分主要记录一些个人对于Stream流的应用,不定期更新。

1)组织树形结构

利用Stream流可以很方便的组织出树形结构的数据,示例如下:

首先我们可以先建立一个树形数据的基础实体类BaseTree。
public class BaseTree<T> {
    private String id;
    private String parentId;
    private List<T> children;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getParentId() {
        return parentId;
    }

    public void setParentId(String parentId) {
        this.parentId = parentId;
    }

    public List<T> getChildren() {
        return children;
    }

    public void setChildren(List<T> children) {
        this.children = children;
    }
}
然后我们创建实际的数据类TestTree,使其继承BaseTree,这样我们只需要添加需要的数据就可以了。
public class TestTree extends BaseTree<TestTree> {
    private String title;
    private int value;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }
}
最后写组织树形结构的方法,只要调用getTree方法,传入根节点的parentId和整个集合,就可以得到一个树形结构的集合了。
public static <T extends BaseTree<T>> List<T> getTree(String rootId, List<T> all){
    return all.stream()
            .filter(m -> Objects.equals(m.getParentId(), rootId))
            //.peek(m -> m.setChildren(getChildren(m, all)))
            .peek(m -> {
                List<T> temp = getTree(m.getId(), all);
                if(temp.size()>0){
                    m.setChildren(temp);
                }else{
                    m.setChildren(null);
                }
            })
            .collect(Collectors.toList());
}

2)稍微复杂的统计功能

一些较为复杂的统计功能也可以通过stream来实现。比如现在我们有一个这样的类:

public static class Test{
        private List<String> a;

        public List<String> getA() {
            return a;
        }

        public void setA(List<String> a) {
            this.a = a;
        }
    }

如果需要统计List中所有的a的数量,我们可以这样来统计:

		List<Test> lists1 = new ArrayList<>();

        List<String> list2 = new ArrayList<>();
        List<String> list3 = new ArrayList<>();

        list2.add("1");
        list2.add("2");
        list3.add("5");
        list3.add("6");
        Test test1 = new Test();
        test1.setA(list2);
        lists1.add(test1);
        Test test2 = new Test();
        test2.setA(list3);
        lists1.add(test2);

		//统计
		System.out.println(lists1.stream().map(t -> t.getA().size()).collect(Collectors.summarizingInt(Integer::intValue)).getSum());

其中summarizing相关的方法返回的是一个SummaryStatistics对象,其中包含的值有:

{count=2, sum=4, min=2, average=2.000000, max=2}

我们可以通过get方法来获取其中任意的值。

3)分组功能

比如我们要给一个类ApplyArchivalParams的集合分组并统计出每组的元素数量,就可以这样写:

Map<String,Long> tsMap = applyArchivalParams.parallelStream().collect(Collectors.groupingBy(
                p->{
                    return p.getQlr().get(0).getQlrmc() +"-"+ p.getQlr().get(0).getZjh();
                },ConcurrentHashMap::new,Collectors.counting()));

你可能感兴趣的:(java开发,list,数据结构,java)