Java Stream流:forEach、filter、map、count、limit、skip、concat、综合案例

文章目录

  • Stream流 JDK 1.8
    • 引言 :通过案例展示Stream流的作用
    • 1、流式思想
    • 2、Stream特点
    • 3、获取Stream流对象
    • 4、常用方法
      • 逐一处理:forEach
      • 过滤:filter
      • 映射:map
      • 统计个数:count
      • 取用前几个:limit
      • 跳过前几个:skip
      • 组合:concat
    • 练习:集合元素的处理


Stream流 JDK 1.8

说到Stream容易想到 IO Stream,而流不一定是IO流。在Java 8中,得益于Lambda所带来的函数式编程,引入了一个全新的Stream概念,用于解决已有集合类库有的弊端。


引言 :通过案例展示Stream流的作用

需求分析:

  1. 在ArrayList集合中,筛选出姓张的数据;
  2. 从筛选出姓张的数据中,再筛选出名字长度为3;
  3. 将结果打印出来。

普通操作:

public class PrefaceNormal {
    public static void main(String[] args) {
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("张杰");
        arrayList.add("张还行");
        arrayList.add("王小");
        arrayList.add("张怡");
        arrayList.add("唐山");

        // 筛选姓张的
        ArrayList<String> listA = new ArrayList<>();
        for(String str: arrayList){
            if(str.startsWith("张")){
                listA.add(str);
            }
        }

        // 筛选3个字的
        ArrayList<String> listB = new ArrayList<>();
        for(String str: arrayList){
            if(str.length() == 3){
                listB.add(str);
            }
        }

        // 遍历结果
        for (String str : listB){
            System.out.println(str); // 输出:张还行
        }
    }
}

可见用了很多次增强for循环,代码量很多,很冗杂,不美观,效率低。

Stream操作:

public class PrefaceStream {
    public static void main(String[] args) {
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("张杰");
        arrayList.add("张还行");
        arrayList.add("王小");
        arrayList.add("张怡");
        arrayList.add("唐山");

        arrayList.stream()
            	// Lambda实现Predicate接口中的test方法,返回Boolean值
                .filter(s -> s.startsWith("张")) 
                .filter(s -> s.length() == 3)
            	// Lambda实现Customer接口中的accept方法
                .forEach(name -> System.out.println(name)); 
        // 不对原有的集合内容做改变,只是对符合要求的数据进行筛选
    }
}

1、流式思想

Java Stream流:forEach、filter、map、count、limit、skip、concat、综合案例_第1张图片

这里的filtermapskip都是对函数模型进行操作,集合元素并没有真正被处理。只有当终结方法count执行的时候,整个模型才会按照指定策略执行操作。而这得益于Lambda的延迟执行特性。

备注:Stream流 其实是一个集合元素的函数模型,它并不是集合,也不是数据结构,其本身不存储任何元素(或地址值)


2、Stream特点

Stream是一个来数据源的元素队列

  • 元素式特定类型的对象,形成一个队列。Java中的Stream并不会存储元素,而是按需计算,使用一次之后,再次调用就会报异常。
public class PrefaceStream {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("张还行", "heroC", "yikeX");
        stream.filter(name -> name.startsWith("张"))
              .forEach(name -> System.out.println(name));
        
        // stream使用了一次了,再使用一次,会报IllegalStateException异常
        // 说明之前的流已经关闭,不能再使用了。
        stream.forEach(name -> System.out.println(name));
    }
}
  • 数据源 流的来源。可以是集合,数组等。

Stream操作两个基础的特征:

  • Pipelining(流水线):中间操作都会返回流对象本身。这样多个操作可以串联成一个管道,如同流式风格。这样做可以对操作进行优化,比如延迟执行和短路。
  • 内部迭代:以前对集合遍历都是通过Iterator或者增强for的方式。显示的在集合外部进行迭代,这叫做外部迭代。Stream提供了内部迭代的方式,流可以直接调用遍历方法。

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


3、获取Stream流对象

java.util.stream Interface Stream 接口

获取流的方式:

  • 所有Collection集合都可以通过stream默认方法获取流
  • stream的静态方法of可以获取对应的流,of方法是可变参数,可变参数底层就一数组
public class CollectionStream {
    public static void main(String[] args) {
        // List集合获取流
        List<String> list = new ArrayList<>();
        Stream<String> stream = list.stream();
        // Set集合获取流
        Set<String> set = new HashSet<>();
        Stream<String> stream1 = set.stream();
        // 键获取流
        Map<String,String> map1 = new HashMap<>();
        Collection<String> collKey = map1.keySet();
        Stream<String> stream2 = collKey.stream();
        // 值获取流
        Map<String,String> map2 = new HashMap<>();
        Collection<String> collValue = map2.values();
        Stream<String> stream3 = collValue.stream();
        // 键值对获取流
        Map<String,String> map3 = new HashMap<>();
        Collection<Map.Entry<String, String>> entries = map3.entrySet();
        Stream<Map.Entry<String, String>> stream4 = entries.stream();
        // 流的静态方法of,可变参数获取流
        Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5);
        // 流的静态方法of,数组获取流
        Integer[] num = {1,2,3,4};
        Stream<Integer> num1 = Stream.of(num);
        String[] str = {"a","b"};
        Stream<String> str1 = Stream.of(str);
    }
}

4、常用方法

Java Stream流:forEach、filter、map、count、limit、skip、concat、综合案例_第2张图片

流模型的操作很丰富,这里介绍一些常用的API。这些方法可以分为两种:

  • 延迟方法:返回值类型仍然是Stream接口自身类型的方法,因此支持链式调用。
  • 终结方法:返回类型不再是Stream接口自身类型的方法,因此不再支持链式调用。常见countforEach方法,更多见API文档。

逐一处理:forEach

forEach与增强for是不同的

void forEach(Consumer action)

public class PrefaceStream {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("张还行", "heroC", "yikeX");
        stream.forEach(name -> System.out.println(name)); // 对流中的每一个数据进行处理
    }
}

过滤:filter

可通过filter将一个流转换成另一个流

Stream filter(Predicate predicate)

public class PrefaceStream {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("张还行", "heroC", "yikeX");
        stream.filter(name -> name.startsWith("张")) // 可以将流过滤转换成另一个只符合要求的流
              .forEach(name -> System.out.println(name)); // 对新的流每个数据进行遍历处理
    }
}
// 输出:张还行

映射:map

将流中的方法映射到另一个流中

Stream map(Function mapper)

T类型的数据转换成R类型的数据就是“映射”。

public class PrefaceStream {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("1", "2", "3", "4");
        Stream<Integer> stream1 = stream.map(str -> Integer.parseInt(str));
        stream1.forEach(num -> System.out.print(num+" "));
    }
}
// 输出:1 2 3 4 

统计个数:count

long count() 返回long类型数据,统计流中元素的个数

public class PrefaceStream {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("1", "12", "23", "1234");
        long count = stream.filter(str -> str.startsWith("1")).count();
        System.out.println(count);
    }
}
// 输出:3

取用前几个:limit

limit方法可以对流中的数据进行截取,只取用前n个。

Stream limit(long maxSize)

public class PrefaceStream {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("1", "12", "23", "1234");
        stream.limit(3).forEach(str -> System.out.print(str+" "));
    }
}
// 输出:1 12 23

跳过前几个:skip

如果流的当前长度大于n,则跳过前n个元素;否则将会得到一个长度为0的空流。

Stream skip(long n)

public class PrefaceStream {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("1", "12", "23", "1234");
        stream.skip(2).forEach(str-> System.out.print(str+" "));
    }
}
// 输出:23 1234 

组合:concat

将有两个流,合并成一个流

static Stream concat(Stream a,Stream b)

备注:这是一个静态方法,与java.lang.String当中的concat方法是不同的。

public class PrefaceStream {
    public static void main(String[] args) {
        Stream<String> stream1 = Stream.of("heroC");
        Stream<String> stream2 = Stream.of("yikeX");
        Stream.concat(stream1,stream2).forEach(str -> System.out.print(str+" "));
    }
}
// 输出:heroC yikeX 

练习:集合元素的处理

题目:

现在有两个ArrayList集合存储队伍当中的多个成员姓名,使用Stream流完成以下操作:

  1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
  2. 第一个队伍筛选后只要前3个人名;存储到一个新集合中。
  3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。
  4. 第二个队伍筛选之后不要前2个人名;存储到一个新集合中。
  5. 将两个筛选后的队伍合并为一个队伍;存储到一个新集合中。
  6. 根据姓名创建Person对象;存储到一个新集合中。
  7. 打印整个队伍的Person对象信息。
public class StreamExample {
    public static void main(String[] args) {
        ArrayList<String> one = new ArrayList<>();
        one.add("张还行");
        one.add("张还");
        one.add("张行");
        one.add("施柏宇");
        one.add("林俊杰");
        one.add("张一山");

        // 1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
        Collection<String> nameThree = new ArrayList<>();
        one.stream().filter(name -> name.length()==3).forEach(name -> nameThree.add(name));

        // 2. 第一个队伍筛选后只要前3个人名;存储到一个新集合中。
        Collection<String> nameThird = new ArrayList<>();
        nameThree.stream().limit(3).forEach(name -> nameThird.add(name));

        ArrayList<String> two = new ArrayList<>();
        two.add("张一山");
        two.add("黄轩");
        two.add("张行");
        two.add("张若昀");
        two.add("罗晋");
        two.add("张子枫");

        // 3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。
        Collection<String> nameZhang = new ArrayList<>();
        two.stream().filter(name -> name.startsWith("张")).forEach(name -> nameZhang.add(name));

        // 4. 第二个队伍筛选之后不要前2个人名;存储到一个新集合中。
        Collection<String> nameSkip = new ArrayList<>();
        nameZhang.stream().skip(2).forEach(name -> nameSkip.add(name));

        // 5. 将两个筛选后的队伍合并为一个队伍;存储到一个新集合中。
        Collection<String> nameConcat = new ArrayList<>();
        Stream.concat(nameThird.stream(), nameSkip.stream()).forEach(name -> nameConcat.add(name));

        // 6. 根据姓名创建`Person`对象;存储到一个新集合中。
        Collection<Person> namePerson = new ArrayList<>();
        nameConcat.stream().forEach(name -> namePerson.add(new Person(name)));

        // 7. 打印整个队伍的`Person`对象信息。
        long count = namePerson.stream().count();
        System.out.println("筛选后的队伍总人数为:"+count);
        namePerson.stream().forEach(namePer -> System.out.println(namePer));
    }
}

输出:

筛选后的队伍总人数为:5
Person{name='张还行'}
Person{name='施柏宇'}
Person{name='林俊杰'}
Person{name='张若昀'}
Person{name='张子枫'}

你可能感兴趣的:(Java学习,java,stream)