Java8新特性笔记--跟着楠哥学java--Stream Api
1.集合处理数据的弊端
当我在需要对集合中的元素进行操作的时候,除了必须的添加删除获取外,最典型的操作就是集合遍历
public class Demo01StreamApi { public static void main(String[] args) { // 定义一个集合 List<String> list = Arrays.asList("张三","张三丰","成龙","周星驰"); // 1.获取所有姓张的信息 List<String> list1 = new ArrayList<>(); for (String s : list) { if (s.startsWith("张")){ list1.add(s); } } // 2.获取名称长度为3的用户 List<String> list2 = new ArrayList<>(); for (String s : list1) { if (s.length() == 3){ list2.add(s); } } // 3.输出所有用户信息 张姓且三个字 for (String s: list2) { System.out.println(s); } }}
上面的代码针对于我们不同的需求总是一字字的循环循环,这是我们希望有更加高效的处理方式,
我们可以通过jdk8中提供的Stream Api来解决这个问题。
Stream更加优雅的解决方案:
public class Demo02StreamApi { public static void main(String[] args) { // 定义一个集合 List<String> list = Arrays.asList("张三","张三丰","成龙","周星驰"); // 1.获取所有姓张的信息 // 2.获取名称长度为3的用户 // 3.输出所有用户信息 张姓且三个字 list.stream() .filter(s -> s.startsWith("张")) .filter(s -> s.length() == 3) .forEach(s -> System.out.println(s)); System.out.println("------------------------------------------------"); list.stream() .filter(s -> s.startsWith("张")) .filter(s -> s.length() == 3) .forEach(System.out::println); }}
上面的StreamAPI代码的含义:获取流,过滤张,过滤字长,逐一打印。代码相比于上面的案例更加简洁直观。
2.Stream流的作用
Stream API能让我们快速完成许多复杂的操作,如筛选,切片,映射,查找,去重,统计,匹配和归约。
3.Stream流的获取方式
3.1 根据Collection获取
首先,java.util.Collection接口中加入了default方法stream,也就是说Collection接口下的所有实现都可以
通过stream方法来获取Stream流对象
public class Demo03StreamApi { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.stream(); Set<String> set = new HashSet<>(); set.stream(); Vector vector = new Vector(); vector.stream(); }}
但是Map接口没有实现Collection接口,这时该怎么办呢? 这是可以根据Map获取对应的key value的集合。
public class Demo04StreamApi { public static void main(String[] args) { Map<String, Object> map = new HashMap<>(); Stream<String> stream1 = map.keySet().stream(); Stream<Object> stream2 = map.values().stream(); Stream<Map.Entry<String, Object>> stream = map.entrySet().stream(); }}
3.2 通过Stream的of方法
在实际开发中我们不可避免的还是会操作到数组中的数据,由于数组对象不可能添加默认方法,所以Stream接口中
提供了静态方法of
public class Demo05StreamApi { public static void main(String[] args) { Stream<String> a1 = Stream.of("a1", "a2", "a3", "a4"); String[] arr1 = {"aa","b","cc"}; Stream<String> arr11 = Stream.of(arr1); Integer[] arr2 = {1,2,3,4}; Stream<Integer> arr21 = Stream.of(arr2); arr21.forEach(System.out::println); // 注:基本数据类型的数组是不行的 会将数组数据看成一个整体 int[] arr3 = {1,2,3,4}; Stream.of(arr3).forEach(System.out::println); }}
4.Stream常用方法介绍
终结方法:返回值类型不再是Sream类型的方法,不再支持链式调用,包括count和foreach。
非终结方法:返回值类型仍然是Stream类型的方法,支持链式调用。
Stream注意事项
1.Stream只能操作一次
2.Stream方法返回的是一个新的流
3.Stream不调用终结方法,中间的操作不会执行,必须以终结方法结束
4.1 forEach
foreach用来遍历流中的数据的
void forEach(Consumer<? super T> action);
该方法接受一个Consumer接口,会将每一个流元素交给函数处理
public class Demo07StreamApiForeach { public static void main(String[] args) {Stream.of("a1", "a2", "a3") .forEach(System.out::println); }}
4.2 count
Stream流中的count方法用来统计其中的元素个数的
long count();
该方法返回一个long值,代表元素个数。
public class Demo08StreamApiCount { public static void main(String[] args) { long count = Stream.of("a1", "a2", "a3").count(); System.out.println(count); }}
4.3 filter
filter方法的作用是用来过滤数据的,返回符合条件的数据
可以通过filter方法讲一个流转换成另一个子集流
Stream<T> filter(Predicate<? super T> predicate);
该接口接受一个Predicate函数式接口参数作为筛选条件
public class Demo09StreamApiFilter { public static void main(String[] args) { Stream.of("a1", "a2", "a3","bb","cc","dd").filter(s -> s.contains("a")) .forEach(System.out::println); }}
4.4 limit
limit方法可以对流进行截取处理,支取前n个数据
Stream<T> limit(long maxSize);
参数是一个long类型的数值,如果集合长度大于参数就进行截取,否则不操作:
public class Demo10StreamApiLimit { public static void main(String[] args) { Stream.of("a1", "a2", "a3", "bb", "cc", "aa", "dd").limit(5) .forEach(System.out::println); }}
4.5 skip
如果希望跳过前面几个元素,可以使用skip方法获取一个截取之后的新流:
public class Demo11StreamApiSkip { public static void main(String[] args) { Stream.of("a1", "a2", "a3","bb","cc","aa","dd").skip(3) .forEach(System.out::println); }}
4.6 map
如果我们需要将流中的元素映射到另一个流中可以使用map方法;
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
该接口需要一个Function函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的数据
public class Demo12StreamApiMap { public static void main(String[] args) { Stream.of("1", "2", "3","4","5","6","7") //.map(s -> Integer.parseInt(s)) .map(Integer::parseInt) .forEach(System.out::println); }}
4.7 sorted
如果需要将数据排序,可以使用sorted方法:
Stream<T> sorted();
在使用的时候可以根据自然规则排序,也可以通过比较来指定对应的排序规则
public class Demo13StreamApiSorted { public static void main(String[] args) { Stream.of("1", "9", "3","4","22","6","7") .map(Integer::parseInt) //.sorted() //根据数据的自然顺序排序 .sorted((o1,o2)->o2-o1) //根据比较指定排序规则 //.sorted(Integer::compareTo) .forEach(System.out::println); }}
4.8 distinct
如果要去掉重复的数据,可以使用distinct方法:
Stream<T> distinct();
public class Dmo14StreamApiDistinct { public static void main(String[] args) { Stream.of("1", "9", "3","4","3","6","7") .distinct() //去掉重复数据 .forEach(System.out::println); System.out.println("---------------------------------"); Stream.of( new Person("张三",18,198), new Person("李四",22,199), new Person("张三",18,167) ).distinct() .forEach(System.out::println); }}
Stream流中的 方法对于基本数据类型是可以直接去重的,但是对于自定义类型,我们是需要重写 hashCode和equals方法来移除重复元素
4.9 match
`如果需要判断数据是否匹配指定的条件,可以使用match相关的方法
boolean anyMatch(Predicate<? super T> predicate);// 元素是否有任意一个满足条件boolean allMatch(Predicate<? super T> predicate);// 元素是否都满足条件boolean noneMatch(Predicate<? super T> predicate);// 元素是否都不满足条件
使用
public class Demo15StreamApiMatch { public static void main(String[] args) { boolean b = Stream.of("1", "9", "3", "4", "3", "6", "7") .map(Integer::parseInt) //.allMatch(s -> s > 0) //.anyMatch(s -> s > 4) .noneMatch(s-> s > 4); System.out.println(b); }}
注意:match也是终结方法
4.10 find
如果我们需要找到某些元素,可以使用find方法来实现
Optional<T> findFirst();Optional<T> findAny();
使用
public class Demo16StreamApiFind { public static void main(String[] args) { Optional<String> first = Stream.of("1", "9", "3", "4", "3", "6", "7").findFirst(); System.out.println(first.get()); Optional<String> any = Stream.of("1", "9", "3", "4", "3", "6", "7").findAny(); System.out.println(any.get()); }}
4.11 max和min
如果我们想要获取最大值和最小值没那么可以使用max和min方法
Optional<T> max(Comparator<? super T> comparator);Optional<T> min(Comparator<? super T> comparator);
使用
public class Demo17StreamApiMaxMin { public static void main(String[] args) { Optional<Integer> first = Stream.of("1", "9", "3", "4", "3", "6", "7") .map(Integer::parseInt) .max((o1,o2)->o1-o2); System.out.println(first.get()); Optional<Integer> any = Stream.of("1", "9", "3", "4", "3", "6", "7") .map(Integer::parseInt) .min(((o1, o2) -> o1-o2)); System.out.println(any.get()); }}
4.12 reduce
如果需要将所有数据归纳得到一个数据,使用reduce方法
T reduce(T identity, BinaryOperator<T> accumulator);
使用
public class Demo18StreamApiReduce { public static void main(String[] args) { Integer total = Stream.of(4, 5, 3, 9) // identity是默认值 // 第一次的时候会将默认值赋给x // 之后每次会将上一次的操作结果赋值给x y就是每次从数据中获取的元素 .reduce(0, (x, y) -> { System.out.println("x="+x+",y="+y); return x + y; }); System.out.println(total); // 获取最大值 Integer max = Stream.of(4, 5, 3, 9) .reduce(0, (x, y) -> { return x > y ? x : y; }); System.out.println(max); // 获取最小值 Optional<Integer> reduce2 = Stream.of(4, 5, 3, 9) .reduce((x, y) -> { return x < y ? x : y; }); System.out.println(reduce2.get()); }}
4.13 map和reduce的组合
在实际开发中,我们经常会将map和reduce组合使用
public class Demo19StreamApiMapReduce { public static void main(String[] args) { //求年龄总和 Integer sumAge = Stream.of( new Person("张三", 18, 198), new Person("李四", 22, 199), new Person("张三", 18, 167), new Person("赵六", 19, 176), new Person("张三", 33, 180) ).map(Person::getAge) .reduce(0, Integer::sum); System.out.println(sumAge); // 求出年龄最大值 Integer maxAge = Stream.of( new Person("张三", 18, 198), new Person("李四", 22, 199), new Person("张三", 18, 167), new Person("赵六", 19, 176), new Person("张三", 33, 180) ).map(Person::getAge) .reduce(0, Math::max); System.out.println(maxAge); //统计 字符 a 出现的次数 Integer count = Stream.of("a", "b", "c", "d", "a", "c", "a") .map(s -> "a".equals(s) ? 1 : 0) .reduce(0, Integer::sum); System.out.println(count); }}
4.14 mapToInt
如果需要将Stream中的Integer类型转换成int类型,可以使用mapToInt方法来实现
public class Demo20StreamApiMapToInt { public static void main(String[] args) { // Integer占用的内存比int多很多,在stream流操作中会自动拆装箱操作 Integer arr[] = {1,2,3,4,5}; Stream.of(arr) .filter(integer -> integer > 0) .forEach(System.out::println); // 为了提高程序代码的效率, 我们可以现将流中Integer数据转换为int数据,然后操作 Integer arr2[] = {1,2,3,4,5,7,9}; Stream.of(arr2) .mapToInt(Integer::intValue) .filter(i -> i > 4) .forEach(System.out::println); }}
4.15 concat
如果有两个流希望合并成为一个流,那么可以使用Stream接口的静态方法concat
public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) { Objects.requireNonNull(a); Objects.requireNonNull(b); @SuppressWarnings("unchecked") Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>( (Spliterator<T>) a.spliterator(), (Spliterator<T>) b.spliterator()); Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel()); return stream.onClose(Streams.composedClose(a, b)); }
使用:
public class Demo21StreamApiConcat { public static void main(String[] args) { Stream<String> stream1 = Stream.of("a", "b", "c"); Stream<String> stream2 = Stream.of("x", "y", "z"); // 通过concat方法将两个流合并成一个新的流 Stream.concat(stream1,stream2) .forEach(System.out::println); }}
4.16 综合案例
public class Demo22StreamApiTest { /** * @methodName main * @description * 定义两个集合,然后再集合中存储多个用户名称,然后完成如下操作: * 1.第一个队伍只保留姓名长度为3的成员 * 2.第一个队伍筛选后只要前三个 * 3.第二个队伍只要姓张的成员 * 4.第二个队伍筛选之后不要前两个人 * 5.将两个队伍合并成一个队伍 * 6.根据姓名创建Person对象 * 7.打印整个队伍的Person信息 * @author FredHe * @param args * @return * @since 2022/4/23 18:09 */ public static void main(String[] args) { List<String> list1 = Arrays.asList("迪丽热巴","宋远桥","苏星河","老子","庄子","孙子","洪七公"); List<String> list2 = Arrays.asList("古力娜扎","张无忌","张三丰","赵丽颖","张二狗","张天爱","张三"); // 1.第一个队伍只保留姓名长度为3的成员 // 2.第一个队伍筛选后只要前三个 Stream<String> stream1 = list1.stream().filter(s -> s.length() == 3).limit(3); // 3.第二个队伍只要姓张的成员 // 4.第二个队伍筛选之后不要前两个人 Stream<String> stream2 = list2.stream().filter(s -> s.startsWith("张")).skip(2); // 5.将两个队伍合并成一个队伍 Stream<String> stream3 = Stream.concat(stream1, stream2); // 6.根据姓名创建Person对象 // 7.打印整个队伍的Person信息 stream3.map(Person::new).forEach(System.out::println); }}
输出结果
Person(name=宋远桥, age=null, height=null)Person(name=苏星河, age=null, height=null)Person(name=洪七公, age=null, height=null)Person(name=张二狗, age=null, height=null)Person(name=张天爱, age=null, height=null)Person(name=张三, age=null, height=null)
未完待续
本人代码笔记gitee地址:https://gitee.com/FredHeYuTong/learn-java8