Java之Stream流
流在java的官方文档中被定义为视图。通过流去完成什么样的任务,而不是如何去实现它。通俗一点来讲它相对于集合就是表达了做什么,而不是怎么做。
流和集合的差异
1.流并不储存数据,数据还是储存在底层的集合中。
2.流的操作并不能修改数据,它只是在原本的流中产生新的流。
3.流的操作是惰性的,只要不使用终结方法提交操作我们就可以一直无限流。
流有
stream() //流 将任何集合转换为一个流
parallelStream() //并行流、顺序流
流的创建
Stream<String> words = Stream.of(contents.split("\\PL+")); //将数组转换成为流 Stream<String> silence = Stream.empty(); //创建一个没有任何元素的流 Stream<String> echos = Stream.generate(() -> "Echo"); //创建一个常量值的流 Stream<Double> randoms = Stream.generate(Math :: random); //获取一个随机数的流 //产生一个无限流,在它的值上产生新的值、在前一个元素上调用后一个方法产生值 Stream<BigInteger> integers = Stream.iterate(BigInteger.ZERO, n -> n.add(BigInteger.ONE));
filter、map 和 flatMap 方法
//过滤产生新的流 List<String> wordList = ...; Stram<String> longWords = wordList.stream().filter(w -> w.length() > 12); //用map转换成新的流 Stream<String> lowercaseWords = words.stram().map(String :: toLowerCase); Stream<String> firsLetters = words.stram().map(s -> s.substring(0, 1)); //用flatMap 将所有当前流中所有元素连接到一起 Stream<String> flatResult = words.stream().flatMap(w -> ... );
抽取子流和连接流
//获取一个限制大小的流 Stream<Double> randows = Stream.generate(Math :: random).limit(100); //丢弃前n个元素 Stream<String> words = Stream.of(contents.split("\\PL+")).skip(1); //连接两个元素的流 Stream<String> combined = Sream.concat( ... , ...);
其他的流转换
//剔除重复元素的流 Stream<String> uniqueWords = Stream.of("a", "a", "b", "c").distinct(); //对流进行排序 Stream<String> longesFirst = words.stram().sorted(Comparator.comparing(String :: length).reversed()); //peek 产生和原来的流相同,但是每次获取一个元素时都调用一次函数 Object[] powers = Stream.iterate(1.0, p -> p*2).peek(e -> System.out.println("Fetching" + e)).limit(20).toArray();
简单约简
约简是一种终结操作。
//获取最大值 Optional<String> largest = words.max(String :: compareToIgnoreCase);
Optional<T>类型
Optional<T>对象是一种包装器对象,要么包装了T类型的对象,要么没有包装任何对象。
如何使用Optional值
//没有任何匹配的时候返回空字符串 String result = optionalString.orElse(""); //计算默认值 String result = optionalString.orElseGet(() -> Locale.getDefault().getDisplayName()); //没有任何值时抛出异常 String result = optionalString.orElseThrow(IllegalStateException :: new); //如果值存在将它添加进某个集中 optionalVlaue.ifPresent(v -> results.add(v)); //或 optionalValue.ifPresent(results :: add); ifPresent不会返回任何值,如果想处理函数结果过应该使用map //added 可能存在三种值,true、false、空的Optional Optional<Boolean> added = optionalValue.map(results :: add);
不适合使用OPtional值的方式
在Optional值为null使用get将会抛出NoSuchElementException对象。
所以不要直接使用get()方法,应先判断值是否存在。
if(optionalValue.isPresent()) optionalValue.get().someMethod(); //或 if(vlaue != null) value.someMethod();
创建Optional值
Optional.of(result)和Optional.empty()
public static Optional<Double> inverse(Double x) { return x == 0 ? Optional.empty() : Optional.of(1/X); }
ofNullable方法
ofNullable(obj)会在obj不为null 的情况返回Optional.of(obj),否则返回Optional.empty()。
用flatMap来构建Optional值的函数
flatMap就像是管道,在Optional<T>对象的方法f有产生另外的Optional<U>对象g时不能直接调用s.f().g()。
因为它们属于不同类型的Optional<T>。则是就可以用到flatMap。
Optional<U> result = s.f().flatMap(T :: g)
收集结果
当处理完流之后,想要获取里面的元素是。可以调用iterator方法或者forEach
stream.forEach(System.out :: println);
在并行流上,forEach方法会以任意顺序遍历。如果想按流中的顺序可以调用forEachOrdered方法。
更多的是转变成为一种数据结构。可以调用toArray。
收集到数组中
因为无法在运行时创建泛型数组,所以steam.toArray()会返回一个Object[]数组。
如果想正确创建指定类型数组。
String[] result = stream.toArray(String[] :: new);
收集到集合中
List<String> result = stream.collect(Collectors.toList()); //或 Set<String> result = stream.collect(Collectors.toSet()); //如果想指定集合种类 TreeSet<String> result = stream.collect(Collectors.toCollection(TreeSet :: new));
连接流中所有字符串
String result = stream.collect(Collectors.joinjing()); //想加分割符 String result = stream.collect(Collectors.joinjing(",")); //如果不是字符串,得先转成字符串 String result = stream.map(Object :: toString).collect(Collectors.joining(","));
对流的结果约简为总和、平均值、最大值或最小值
IntSummaryStatistics summary = stream.collect(Collectors.summarizingInt(String :: length)); double averageWordLength = summary.getAverage(); double maxWordLength = summary.getMax();
收集到映射表中
假设有Stream<Person>
Map<Integer, String> idToName = people.collect(Collectors.toMap(Person :: getId, Person :: getName)); //第二值也可使用函数 Map<Integer, String> idToName = people.collect(Collectors.toMap(Person :: getId, Function.identity()));
如果有多个元素相同的键将会抛出IllegalStateExcpetion对象
这时可以使用第三个函数来处理冲突
Stream<Locale> locale = Stream.of(Locale.getAvailableLocales()); Map<String, String> languages = locales.collect( Collectors.toMap( Locale :: getDisplayLanguage, l -> l.getDisplayLanguage(l), (existingValue, newValue) -> existingValue
)
);
群组和分区
groupingBy一种更方便的方法,它会见现有的集与新集合并。
Map<String, List<Locale>> countryToLocales = locales.collect(Collectors.groupingBy(Locale :: getCountry));
List<Local> swissLocales = countryToLocales.get("CH");
分类函数是断言函数,在这种情况下使用partitioningBy比使用groupingBy更高效
Map<Boolean, List<Locale>> englishAanOtherLocales = locales.collect( Collectors.partitioningBy(l -> l.getLanguage().equals("en"))); List<Local> englishLocales =englishAndToLocales.get(true);
groupingByConcurrent方法使用并行流时可获得并行映射表与toConcurrentMap方法类似。
下游收集器
//groupingBy产生的映射表,它的每个值是列表,想获得集就得 Map<String, Set<Locale>>countryToLocaleSet = locales.collect(groupingBy(Locale :: getCountry, toSet())); //counting收集元素个数 Map<String, Long> countryToLocalCounts = locales.collect(groupingBy(Locale :: getCountry, counting())); //summing(Int|Long|Double) 接受引元,产生它们的和 Map<String, Integer> stateToCityPopulation = locales.collect( groupingBy(City :: getState, summingInt(City :: getPopulation))); //maxBy、minBy获取最大、小值 Map<String, Optional<City>>stateToLargestCity = cities.collect( groupingBy(City :: getState, maxBy(Comparator.comping(City :: getPopulation)))); //mapping方法使用函数 Map<String, Optional<String>> stateToLargestCityName = cities.collect( groupingBy(City :: getState, mapping(City :: getName, maxBy(Comparator.comping(String :: length))))); //mapping方法收集到集 Map<String, Set<String>> countryToLanguages = locales.collect( groupingBy(Locale :: getDisplayCountry, mapping(Locale :: getDisplayLanguage, toSet()))); //IntSummaryStatistics使用汇总(适用int、long、double) Map<String, IntSummaryStatistics> stateToCityPopulationSummary = cities.collect( groupingBy(Ctiy :: getState, summarizing(Ctiy :: getPopulation)));