java8 lambda

目录

1、Lambda表达式

2、函数式接口

3、Lambda语法

4、方法引用

5、Stream

6、并行操作


参考代码:https://github.com/xiangqian19831224/lambda

一、Lambda表达式

1、用处

     Lambda表达式可以替代只有一个抽象函数的接口<函数式接口>实现。

    提升了对集合,框架的迭代,遍历,过滤数据等操作

2、特点

    函数式编程

    参数类型自动推断

    代码简洁

   例如:

   public static void main(String[] args){

          new Thread(new Runnable(){

                 public void run(){

                       System.out.println("runnable 是只有一个抽象函数的接口");

                 }).start();

        }

        new Thread(()->{System.out.println("只是用一个lambda表达式替代了Runnable的实现");}).start();

   }


二、函数式接口

     只有一个抽象方法(Object类中的方法除外)的接口是函数式接口

jdk1.8内置的函数式接口

1> Supplier                : 代表一个输出

2> Comsumer             :  代表一个输入

3> BiComsumer         :两个输入

4> Function                :一个输入一个输出     输入输出类型可以不同

5> UnaryOperator       : 一个输入一个输出     输入输出类型相同

6> BiFunction             :两个输入一个输出    输入输出类型可以不同

7> BinaryOperator      :两个输入一个输出    输入输出类型相同


三、Lambda语法

1、概念

      Lambda表达式是对象,是一个函数式接口的实例

2、语法

     (Object… args) -> {函数式接口抽象方法实现逻辑}

    


     参数个数由函数式接口中的抽象方法的参数个数决定

     一个参数可以省略()

    实现逻辑简单时候,{}和return都可以省略

3、示例

() -> {}                            // 无参,无返回值

() -> { System.out.println(1); }  // 无参,无返回值

() -> System.out.println(1)//无参,无返回值(上面的简写)

() -> { return 100; }      // 无参,有返回值

() -> 100                       // 无参,有返回值(上面的简写)

() -> null                      // 无参,有返回值(返回null)

(int x) -> { return x+1; } // 单个参数,有返回值

(int x) -> x+1               // 单个参数,有返回值(上面的简写)

(x) -> x+1                  // 单个参数,有返回值(不指定参数类型,多个参数必须用括号)

x -> x+1                     //单个参数,有返回值(不指定参数类型)

4、注意

不需要也不允许使用throws语句来声明它可能会抛出的异常


四、方法引用

1、概念

    用直接访问类或者示例已经存在的方法或者构造方法

2、类型   

类型语法对应lambda表达式
静态方法引用className::staticMethod(args)->className.staticMethod(args)
实例方法引用instance::instMethod(args)->instance.instMethod(args)
对象方法引用className::instMethod(args)->className.instMethod(args)
构造方法引用className::new(args)->new className(args)

对象方法引用: 抽象方法的第一个参数类型刚好是实例方法的类型,抽象方法剩余的参数恰好可以当做实例方法的参数。如果函数式接口的实现可以由上面说的实例方法调用来实现的话,那么就可以使用对象方法引用

构造方法引用:如果函数式接口的实现恰好可以通过调用一个类的构造方法来实现,那么就可以使用构造方法引用


五、stream API

1、概念

      对一组数据支持串行和并行累加处理的操作的API

2、特点

      不是数据结构,没有内部存储

      不支持索引访问

      延迟计算

      支持并行

     容易生成数据或计划(List, Set)

     支持过滤,查找,转化,汇总,聚合等操作

3、机制

     Stream分为源,中间操作,终止操作

 1> 源: 数组,集合,生成器方法,I/O通道

     生成方法: 数组,集合,Stream.generate, Stream.iterate,其他

 2> 中间操作: 返回新的流

      过滤filter,去重distinct,排序sorted,截取limit、skip,转换map/flatMap/mapToInt,其他peek

3> 终止操作:开启执行遍历操作

     循环:foreach

     计算:min,max,count,average,sum

     匹配:anyMatch,allMatch,noneMatch,findFirst,findAny

     汇聚:reduce

     收集器: toArray,collect


六、stream深度分析

参考:http://www.cnblogs.com/CarpenterLee/p/6637118.html

1、函数回调

     stream API大量使用表达式作为回调方法。

     回调示例:

    ArrayList.forEach()实现:

  default void forEach(Consumer<? super T> action) {
     Objects.requireNonNull(action);
     for (T t : this) {
        action.accept(t);
     }
  }
2、Stream操作

Stream操作分类
中间操作(Intermediate operations) 无状态(Stateless) unordered() filter() map() mapToInt() mapToLong() mapToDouble() flatMap() flatMapToInt() flatMapToLong() flatMapToDouble() peek()
有状态(Stateful) distinct() sorted() sorted() limit() skip()
终止操作(Terminal operations) 非短路操作 forEach() forEachOrdered() toArray() reduce() collect() max() min() count()
短路操作(short-circuiting) anyMatch() allMatch() noneMatch() findFirst() findAny()

中间操作: 只是一种标记,不触发计算

终止操作: 出发实际的计算


无状态:元素的处理不受前面元素的影响

有状态: 元素的处理受前面元素的影响,必须等到所有元素的处理之后才知道最终结果


短路操作:不需要处理完全部元素就可以返回结果

非短路操作:处理完所有的元素才可以返回结果


3、stream优点

1>  简单的实现:

    Stream_pipeline_naive

   int longest =0;

   for(String str : strings){

          if(str.startsWith("A")){// 1. filter(), 保留以A开头的字符串

                 int len = str.length();// 2. mapToInt(), 转换成长度

                longest = Math.max(len, longest);// 3. max(), 保留最长的长度

          }

   }

2> 简单实现缺点:

   迭代次数多

   频繁产生中间结果

3> stream

   尽量减少迭代次数

   减少中间结果的产生

   并行计算


4、Stream流水线

关键问题:

1> 操作如何记录

2> 操作如何叠加

3> 叠加后如何执行

4> 结果记录在哪里


详细解析:

1> 操作如何记录

    Stream_pipeline_example

   每个stage记录:

        前一个stage

        本次的操作

        回调函数

2> 操作如何叠加

    叠加协议 Sink

                         方法名                                                                       

作用

void begin(long size) 开始遍历元素之前调用该方法,通知Sink做好准备。
void end() 所有元素遍历完成之后调用,通知Sink没有更多的元素了。
boolean cancellationRequested() 是否可以结束操作,可以让短路操作尽早结束。
void accept(T t)                 遍历元素时调用,接受一个待处理元素,并对元素进行处理。Stage把自己包含的操作和回调方法封装到该方法里,前一个Stage只需要调用当前Stage.accept(T t)方法就行了。

    

方法名 作用
void begin(long size) 开始遍历元素之前调用该方法,通知Sink做好准备。
void end() 所有元素遍历完成之后调用,通知Sink没有更多的元素了。
boolean cancellationRequested() 是否可以结束操作,可以让短路操作尽早结束。
void accept(T t) 遍历元素时调用,接受一个待处理元素,并对元素进行处理。Stage把自己包含的操作和回调方法封装到该方法里,前一个Stage只需要调用当前Stage.accept(T t)方法就行了。

    accept: 必须实现,每个stage通过accept接受数据并调用后一个stage的accept操作

    bengin:

    end:         有状态方法必须实现

    cancellationRequested: 短路操作必须实现


   void accept(U u){

       1. 使用当前sink包装的回调函数处理u

       2. 将结果传递给流水线下游的sink

   }


// Stream.map(),调用该方法将产生一个新的Stream
public final <R> Stream<R> map(Function<? super P_OUT, ? extends R> mapper) {
   ...
   return new StatelessOp<P_OUT, R>(this, StreamShape.REFERENCE,
         StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) {
      @Override /*opWripSink()方法返回由回调函数包装而成Sink*/
      Sink<P_OUT> opWrapSink(int flags, Sink<R> downstream) {
         return new Sink.ChainedReference<P_OUT, R>(downstream) {
            @Override
            public void accept(P_OUT u) {
               R r = mapper.apply(u);// 1. 使用当前Sink包装的回调函数mapper处理u
               downstream.accept(r);// 2. 将处理结果传递给流水线下游的Sink
            }
         };
      }
   };
}

// Stream.sort()方法用到的Sink实现
class RefSortingSink<T> extends AbstractRefSortingSink<T> {
   private ArrayList<T> list;// 存放用于排序的元素
   RefSortingSink(Sink<? super T> downstream, Comparator<? super T> comparator) {
      super(downstream, comparator);
   }
   @Override
   public void begin(long size) {
        ...
      // 创建一个存放排序元素的列表
      list = (size >= 0) ? new ArrayList<T>((int) size) : new ArrayList<T>();
   }
   @Override
   public void end() {
      list.sort(comparator);// 只有元素全部接收之后才能开始排序
      downstream.begin(list.size());
      if (!cancellationWasRequested) {// 下游Sink不包含短路操作
         list.forEach(downstream::accept);// 2. 将处理结果传递给流水线下游的Sink
      }
      else {// 下游Sink包含短路操作
         for (T t : list) {// 每次都调用cancellationRequested()询问是否可以结束处理。
            if (downstream.cancellationRequested()) break;
            downstream.accept(t);// 2. 将处理结果传递给流水线下游的Sink
         }
      }
      downstream.end();
      list = null;
   }
   @Override
   public void accept(T t) {
      list.add(t);// 1. 使用当前Sink包装动作处理t,只是简单的将元素添加到中间列表当中
   }
}

     Sink的四个接口协同工作:

            首先beging()方法告诉Sink参与排序的元素个数,方便确定中间结果容器的的大小;

            之后通过accept()方法将元素添加到中间结果当中,最终执行时调用者会不断调用该方法,直到遍历所有元素;

            最后end()方法告诉Sink所有元素遍历完毕,启动排序步骤,排序完成后将结果传递给下游的Sink;

            如果下游的Sink是短路操作,将结果传递给下游时不断询问下游cancellationRequested()是否可以结束处理。


 3> 如何执行

      终止操作触发操作,终止操作需要解决问题:

       --->梳理调用流程:

       上游Sink如何找到下游Sink:

    // AbstractPipeline.wrapSink()

// 从下游向上游不断包装Sink。如果最初传入的sink代表结束操作,
// 函数返回时就可以得到一个代表了流水线上所有操作的Sink。
final <P_IN> Sink<P_IN> wrapSink(Sink<E_OUT> sink) {
   ...
   for (AbstractPipeline p=AbstractPipeline.this; p.depth > 0; p=p.previousStage) {
      sink = p.opWrapSink(p.previousStage.combinedFlags, sink);
   }
   return (Sink<P_IN>) sink;
}

    --->执行过程

// AbstractPipeline.copyInto(), 对spliterator代表的数据执行wrappedSink代表的操作。
final <P_IN> void copyInto(Sink<P_IN> wrappedSink, Spliterator<P_IN> spliterator) {
   ...
   if (!StreamOpFlag.SHORT_CIRCUIT.isKnown(getStreamAndOpFlags())) {
      wrappedSink.begin(spliterator.getExactSizeIfKnown());// 通知开始遍历
      spliterator.forEachRemaining(wrappedSink);// 迭代
      wrappedSink.end();// 通知遍历结束
   }
   ...
}

4> 结果存储

返回类型 对应的结束操作
boolean anyMatch() allMatch() noneMatch()
Optional findFirst() findAny()
归约结果 reduce() collect()
数组 toArray()
// 错误的收集方式

ArrayList<String> results = new ArrayList<>();
stream.filter(s -> pattern.matcher(s).matches())
   .forEach(s -> results.add(s));  // Unnecessary use of side-effects!
   // 正确的收集方式
   List<String>results =
   stream.filter(s -> pattern.matcher(s).matches())
   .collect(Collectors.toList());  // No side-effects!

  • 返回boolean或者Optional的操作: 只需要在对应的Sink中记录这个值,等到执行结束时返回就可以了。
  • 归约操作: 最终结果放在用户调用时指定的容器中(容器类型通过收集器指定)。collect(), reduce(), max(), min()都是归约操作,虽然max()和min()也是返回一个Optional,但事实上底层是通过调用reduce()方法实现的。
  • 返回是数组的情况: 毫无疑问的结果会放在数组当中。这么说当然是对的,但在最终返回数组之前,结果其实是存储在一种叫做Node的数据结构中的。Node是一种多叉树结构,元素存储在树的叶子当中,并且一个叶子节点可以存放多个元素。这样做是为了并行执行方便。关于Node的具体结构,我们会在下一节探究Stream如何并行执行时给出详细说明。

六、并行操作——回头再补充

        参考: http://lvheyang.com/?p=87

Java的ForkJoin运行原理-Task继承图

abstract class AbstractPipeline<E_IN, E_OUT, S extends BaseStream<E_OUT, S>>
      extends PipelineHelper<E_OUT> implements BaseStream<E_OUT, S> {
  
   ... ...

 /**
    * Evaluate the pipeline with a terminal operation to produce a result.
    *
    * @param <R>        the type of result
    * @param terminalOp the terminal operation to be applied to the pipeline.
    * @return the result
    */

   final <R> R evaluate(TerminalOp<E_OUT, R> terminalOp) {
      assert getOutputShape() == terminalOp.inputShape();
      if (linkedOrConsumed)
         throw new IllegalStateException(MSG_STREAM_LINKED);
      linkedOrConsumed = true;

      return isParallel()
            ? terminalOp.evaluateParallel(this, sourceSpliterator(terminalOp.getOpFlags()))
            : terminalOp.evaluateSequential(this, sourceSpliterator(terminalOp.getOpFlags()));
   }
}

interface TerminalOp<E_IN, R> {
  
     ... ...

    /**
     * Performs a parallel evaluation of the operation using the specified
     * {@code PipelineHelper}, which describes the upstream intermediate
     * operations.
     *
     * @implSpec The default performs a sequential evaluation of the operation
     * using the specified {@code PipelineHelper}.
     *
     * @param helper the pipeline helper
     * @param spliterator the source spliterator
     * @return the result of the evaluation
     */
    default <P_IN> R evaluateParallel(PipelineHelper<E_IN> helper,
                                      Spliterator<P_IN> spliterator) {
        if (Tripwire.ENABLED)
            Tripwire.trip(getClass(), "{0} triggering TerminalOp.evaluateParallel serial default");
        return evaluateSequential(helper, spliterator);
    }

/**
 * A short-circuiting {@code TerminalOp} that searches for an element in a
 * stream pipeline, and terminates when it finds one.  Implements both
 * find-first (find the first element in the encounter order) and find-any
 * (find any element, may not be the first in encounter order.)
 *
 * @param <T> the output type of the stream pipeline
 * @param <O> the result type of the find operation, typically an optional
 *        type
 */
private static final class FindOp<T, O> implements TerminalOp<T, O> {
    private final StreamShape shape;
    final boolean mustFindFirst;
    final O emptyValue;
    final Predicate<O> presentPredicate;
    final Supplier<TerminalSink<T, O>> sinkSupplier;

    ... ...

    @Override
    public <P_IN> O evaluateParallel(PipelineHelper<T> helper,
                                     Spliterator<P_IN> spliterator) {
        return new FindTask<>(this, helper, spliterator).invoke();
    }
}

abstract class AbstractTask<P_IN, P_OUT, R,
                            K extends AbstractTask<P_IN, P_OUT, R, K>>
        extends CountedCompleter<R> {
    ......
  
   @Override
    public void compute() {
        Spliterator<P_IN> rs = spliterator, ls; // right, left spliterators
        long sizeEstimate = rs.estimateSize();//预估分片中的数据量
        long sizeThreshold = getTargetSize(sizeEstimate);//设置最小执行单元的数量
        boolean forkRight = false;
        @SuppressWarnings("unchecked") K task = (K) this;
        while (sizeEstimate > sizeThreshold && (ls = rs.trySplit()) != null) {//分片大小大于处理单元的阈值,尝试分片成功就进行分片
            K leftChild, rightChild, taskToFork;
            task.leftChild  = leftChild = task.makeChild(ls);
            task.rightChild = rightChild = task.makeChild(rs);
            task.setPendingCount(1);
            if (forkRight) {
                forkRight = false;
                rs = ls;
                task = leftChild;
                taskToFork = rightChild;
            }
            else {
                forkRight = true;
                task = rightChild;
                taskToFork = leftChild;
            }
            taskToFork.fork();//对左右子节点进行fork
            sizeEstimate = rs.estimateSize();
        }//任务足够小的时候推出循环

       task.setLocalResult(task.doLeaf());//task.doLeaf 完成最小计算单元的计算任务,并设置到当前任务的localResult中
        task.tryComplete();//扫尾工作
    }
}


七、补充 ForkJoin机制

参考:

http://www.cnblogs.com/chenpi/p/5581198.html

http://www.cnblogs.com/shijiaqi1066/p/4631466.html

 1、示例

import java.awt.image.BufferedImage;
import java.io.File;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import javax.imageio.ImageIO;

/**
 * ForkBlur implements a simple horizontal image blur. It averages pixels in the
 * source array and writes them to a destination array. The sThreshold value
 * determines whether the blurring will be performed directly or split into two
 * tasks.
 *
 * This is not the recommended way to blur images; it is only intended to
 * illustrate the use of the Fork/Join framework.
 */
public class ForkBlur extends RecursiveAction {
   private static final long serialVersionUID = -8032915917030559798L;
   private int[] mSource;
   private int mStart;
   private int mLength;
   private int[] mDestination;
   private int mBlurWidth = 15; // Processing window size, should be odd.

   public ForkBlur(int[] src, int start, int length, int[] dst) {
      mSource = src;
      mStart = start;
      mLength = length;
      mDestination = dst;
   }

   protected static int sThreshold = 10000;

   @Override
   protected void compute() {
      if (mLength < sThreshold) {
         computeDirectly();//核心计算
         return;
      }

      int split = mLength / 2;

      invokeAll(new ForkBlur(mSource, mStart, split, mDestination),
            new ForkBlur(mSource, mStart + split, mLength - split,
                  mDestination));//ForkJoin使用
   }

   // Plumbing follows.
   public static void main(String[] args) throws Exception {
      String srcName = "D:\\test.jpg";
      File srcFile = new File(srcName);
      BufferedImage image = ImageIO.read(srcFile);

      System.out.println("Source image: " + srcName);
      BufferedImage blurredImage = blur(image);

      String dstName = "D:\\test_out.jpg";
      File dstFile = new File(dstName);
      ImageIO.write(blurredImage, "jpg", dstFile);

      System.out.println("Output image: " + dstName);

   }

 // Average pixels from source, write results into destination.
   protected void computeDirectly() {
      int sidePixels = (mBlurWidth - 1) / 2;
      for (int index = mStart; index < mStart + mLength; index++) {
         // Calculate average.
         float rt = 0, gt = 0, bt = 0;
         for (int mi = -sidePixels; mi <= sidePixels; mi++) {
            int mindex = Math.min(Math.max(mi + index, 0), mSource.length - 1);
            int pixel = mSource[mindex];
            rt += (float) ((pixel & 0x00ff0000) >> 16) / mBlurWidth;
            gt += (float) ((pixel & 0x0000ff00) >> 8) / mBlurWidth;
            bt += (float) ((pixel & 0x000000ff) >> 0) / mBlurWidth;
         }

         // Re-assemble destination pixel.
         int dpixel = (0xff000000)
               | (((int) rt) << 16)
               | (((int) gt) << 8)
               | (((int) bt) << 0);
         mDestination[index] = dpixel;
      }
   }
 public static BufferedImage blur(BufferedImage srcImage) { int w = srcImage.getWidth(); int h = srcImage.getHeight(); int[] src = srcImage.getRGB(0, 0, w, h, null, 0, w); int[] dst = new int[src.length]; System.out.println("Array size is " + src.length); System.out.println("Threshold is " + sThreshold); int processors = Runtime.getRuntime().availableProcessors(); System.out.println(Integer.toString(processors) + " processor" + (processors != 1 ? "s are " : " is ") + "available"); ForkBlur fb = new ForkBlur(src, 0, src.length, dst); ForkJoinPool pool = new ForkJoinPool(); long startTime = System.currentTimeMillis(); pool.invoke(fb); //触发操作 long endTime = System.currentTimeMillis(); System.out.println("Image blur took " + (endTime - startTime) + " milliseconds."); BufferedImage dstImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); dstImage.setRGB(0, 0, w, h, dst, 0, w); return dstImage; }}
       

2、ForkJoin原理

    回头再补充

























  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值