Java(1.8)高级特性-流

什么是流

与文件的输入/输出流无关,流(Stream)是一种类似集合的概念。我们能够通过流来定义对集合“做什么”来实现我们的目标。

如何操作流

使用流来达成目的需要经过三步:

  1. 创建流
  2. 转换流(即将初始流通过我们定义的“做什么”将其转换成其他流)
  3. 终结操作(terminal peration,从最终的流中获得我们需要的数据)

下面是一个例子,该例子创建了一个Integer类型的流,并对流中的所有元素调用x=x+1的方法,最后使用reduce终止操作获得整个流的和。流的操作是尽可能惰性执行的,直到需要结果时才会执行,即应用了终止操作后,之前的惰性操作会强制执行,流就失去了作用。

Stream<Integer> s=Stream.of(1,2,3,4);
int sum=s.map(x->x+1).reduce(0,(x,y)->{x+y});

创建流

//创建一个元素的流
public static<T> Stream<T> of(T t)
//从多个元素中创建流,参数可以是数组也可以是多个单值 
public static<T> Stream<T> of(T... values) 
//创建一个空流
public static<T> Stream<T> empty() 
//创建一个无限流,元素分别为seed,f(seed),f(f(seed)),....
//例子:Stream<Integer> s=Stream.iterate(1,x->x+1);
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
//通过Supplier创建无限流 
//例子:Stream<Integer> s=Stream.generate(Math::random)
public static<T> Stream<T> generate(Supplier<T> s)

Stream类中提供了如上方法创建一个流。除此之外,Arrays、集合类、Files也都提供了创建流的方法。

//从一个数组的给定范围创建流
public static <T> Stream<T> stream(T[] array, int startInclusive, int endExclusive)
//从集合创建流
default Stream<E> stream()
//从文件创建流,元素为文件中的一行
public static Stream<String> lines(Path path, Charset cs) throws IOException 

转换流

Stream提供了多种方法来进行转换流。

Stream<T> filter(Predicate<? super T> predicate);
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

这是三种主要的流转换方法。

  • filter
    filter的引元是Predicate<T>,即从T到boolean的函数,可以使用此方法获得满足条件的流,比如通过Stream s=Stream.of(1,2,3,4,5).filter(x->x>3)我们就可以获得大于3的流。
  • map
    mapStream中的每个元素调用mapper方法,比如Stream s=Stream.of(1,2,3,4,5).map(x->x=x+1)可以得到每个元素加一的流。
  • flatmap
    flatmap可以处理后得到的Stream<Stream<T>>平摊为Stream<T>的流。比如Stream.of("apple","bit").map(x->letters(x))将得到[["a","p","p","l","e"],["b","i","t"]],而Stream.of("apple","bit").flatmap(x->letters(x))将得到["a","p","p","l","e","b","i","t"]

除了以上3种方法,Stream类还提供了获取子流、排序等方法。

//获取前maxSize个元素的流
Stream<T> limit(long maxSize);
//跳过前n个元素的流
Stream<T> skip(long n);
//将两个流合并成一个流
public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) ;
//对流进行排序
Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);
//移除流中重复的元素
Stream<T> distinct();
//每个元素被使用时都会先传递给action方法,这个方法主要被用来debug
//例子:Stream.of(1,2,3,4).peek(x->System.out.print(x)).map(x->x=x+1)
Stream<T> peek(Consumer<? super T> action);

终结操作

定义了流并进行转换后,我们终于可以得到我们想要的数据。Stream提供了如下的终结操作。

Optional<T> min(Comparator<? super T> comparator);
Optional<T> max(Comparator<? super T> comparator);
long count();
boolean anyMatch(Predicate<? super T> predicate);
boolean allMatch(Predicate<? super T> predicate);
boolean noneMatch(Predicate<? super T> predicate);
void forEach(Consumer<? super T> action);
void forEachOrdered(Consumer<? super T> action);
Optional<T> findFirst();
Optional<T> findAny();
//可以通过reduce操作对流中的数据进行处理。如果定义了简约操作(accumulator)op
//那么返回的值为 v1 op v2 op v3 ...
//可以通过定义identity,当流为空时返回该值
Optional<T> reduce(BinaryOperator<T> accumulator);
T reduce(T identity, BinaryOperator<T> accumulator);
<U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner);

当然,转换后的流并不一定非要进行上述的终结操作,我们也可以将得到的结果存入集合中来使用。

//获取流的iterator,遍历方法与集合的iterator相同
Iterator<T> iterator();
Object[] toArray();
<A> A[] toArray(IntFunction<A[]> generator);
<R, A> R collect(Collector<? super T, A, R> collector);
<R> R collect(Supplier<R> supplier,BiConsumer<R, ? super T> accumulator,BiConsumer<R, R> combiner)

对于其中的collect方法,可以通过定义不同收集器来获得不同的集合类型,比如Stream.of(1,2,3,4).collect(Collectors.toSet())来获得Set。收集器Collectors有如下这些方法(部分),方法的说明在注释部分。

public static <T> Collector<T, ?, List<T>> toList();
public static <T> Collector<T, ?, Set<T>> toSet();
//通过keyMapper生成key,通过valueMapper生成value,通过mergeFunction合并key相同的对象
//例子:Map<integer,String> idName=people.collect(Collectors.toMap(Person::getId,Person::getName,
//        (existValue,newValue)->existValue))
public static <T, K, U> Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper
    ,Function<? super T, ? extends U> valueMapper);
public static <T, K, U> Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper
    ,Function<? super T, ? extends U> valueMapper,BinaryOperator<U> mergeFunction)
//将元素收集到指定的集合中
//例子:person.collect(Collectors.toCollection(TreeSet::new))
public static <T, C extends Collection<T>> 
    Collector<T, ?, C> toCollection(Supplier<C> collectionFactory);
//合并字符串,可以自定义分隔符、第一个元素的前缀和最后一个元素的后缀
public static Collector<CharSequence, ?, String> joining();
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter);
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter,
                                                             CharSequence prefix,
                                                             CharSequence suffix);

//生成(Int|Long|Double)SummaryStatistics
/* (Int|Long|Double)SummaryStatistics可以通过
 *   getCount()产生元素个数
 *   getSum()求和
 *   getAverage()求平均数
 *   getMax()、getMin() 获得最大/小值,如果没有元素时返回(MAX|MIN)_VALUE
 *   例子:Stream.of(1,2,3,4).collect(Collectors.summaringzingInt(x->x)).getCount();
 */

public static <T>
    Collector<T, ?, IntSummaryStatistics> summarizingInt(ToIntFunction<? super T> mapper);
public static <T>
    Collector<T, ?, LongSummaryStatistics> summarizingLong(ToLongFunction<? super T> mapper);
public static <T>
    Collector<T, ?, DoubleSummaryStatistics> summarizingDouble(ToDoubleFunction<? super T> mapper)

//对映射表中的元素进行分组   
/*
 * classifier为分类的标准,比如Persion::getName以名字分组
 * mapFactory为映射表的工厂类,比如TreeMap::new
 * downstream为对分类后的数据进行的操作,有counting()、summingInt()、maxBy(Cmparator)、minBy()
 * mapping(mapper,downstream)
 */
//例子:people.collect(
//          Collectors.groupingBy(
//              Person::getName,
//              Collectors.mapping(Person::getId,Collectors.counting())))
public static <T, K> Collector<T, ?, Map<K, List<T>>>
    groupingBy(Function<? super T, ? extends K> classifier)
public static <T, K, A, D>
    Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,
         Collector<? super T, A, D> downstream)
public static <T, K, D, A, M extends Map<K, D>>
    Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
         Supplier<M> mapFactory,Collector<? super T, A, D> downstream)

并行流

我们可以创建并行流来加快流的处理速度。获取流的方法有两种,一是使用Strea.parallel(),二是使用Collection.parallelStreamm()从集合中获取并行流。只要终结方法执行时,流处于并行状态,所有的中间操作都将并行执行。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容