Java 8 函数式编程

《Java 8 函数式编程》的笔记

简单mark下里面的代码
习题解:https://github.com/RichardWarburton/java-8-lambdas-exercises

2.Lamba表达式

相当于匿名方法,代码即数据,闭包且适用于函数接口。

Lamba可应用在

匿名内部类 button.addActionListenr( event -> System.out.println("button clicked") ) javac(编译器)会根据程序上下文推断出参数类型(有时不一定),类型推断始于Java7,`new HashMap<>中的<>就会推断泛型参数的类型

局部变量BinaryOperator<Long> add = (x, y) -> x+y;,这样add相当于x+y这行代码,而目标类型为Long(上下文环境类型,就像数组对象和null赋值后才知道类型)。

引用所在方法变量时,声明为final,实际引用的是该值非变量。如

final String name = getUserName();
button.addActionListener( event -> System.out.println("hi"+name) );

这时final在Java8中可省。

函数接口有

  Predicate<T> test
  Consumer<T> accept
  Function<T,R> apply
  Supplier<T> get
  UnaryOperator<T>
  BinaryOperator<T>

3.Stream

Stream为内部迭代(集合类中实现,无需在程序中编写),如stream()返回对象,而非iterator()返回用于外部迭代的Iterator对象

int count = 0;
for (Artist artist : allArtists)
  if (artist.isFrom("London"))
    count++;
//前者为封装了迭代的语法糖
int count = 0;
Iterator<Artist> iterator = allArtists.iterator();
while (iterator.hasNext()){
  Artist artist = iterator.next();
    if (artist.isFrom("London"))
    count++;
}

链式调用由多个Lazy操作组成,以Eager操作结束,只迭代一次。

allArtists.stream()
  .filter( artist -> artist.isFrom("London") )
  .count();

常用的操作

//collect
List<String> collected = Stream.of("a", "b", "c")
  .collect( Collectors.toList() );
assertEquals( Arrays.asList("a", "b", "c"), collected );

//map( Function<T, R> )
List<String> collected = Stream.of("a", "b", "c")
  .map( string -> string.toUpperCase() )
  .collect( Collectors.toList() );

//filter( Predicate<T, boolean> )
List<String> collected = Stream.of("a", "bb", "c")
  .filter( string -> string.length >1 )
  .collect( Collectors.toList() );

//flatMap( Function<T, Stream> )
List<Integer> together = Stream.of( Arrays.asList(1, 2), Arrays.asList(1, 2) )
  .flatMap(numbers -> numbers.stream())
  .collect( Collectors.toList() );

artists.stream()
  .flatMap(artist -> Stream.of(artist.getName(), artist.getNationality()))
  .collect(toList());

//max.min
Artist artist = allArtist.stream()
  .min( Comparator.comparing(artist -> artist.getName().length ) )
  .get();

reduce,从一组值生成一个值的操作(如count,min,max)。

int count = Stream.of(1, 2, 3)
  .reduce(0, (acc, element) -> acc + element );

//展开后
BinaryOperator<Integer> accumulator = (acc, element) -> acc + element;
int count = accumulator.apply(
  accumulator.apply(
    accumulator.apply(0, 1),
    2),
  3);
//重构前
for (Album album : albums){
  for (Track track : album.getTrackList()){
    if (track.getLength() > 60){
      String name = track.getName();
      trackNames.add(name);
    }
  }
}
//重构后
albums.stream()
  .flatMap( album -> album.getTracks() )
  .filter( track -> track.getLength() > 60)
  .map( track -> track.getName() )
  .collect( toSet() )

尽量避免副作用,即不改变程序或外界的状态。

4.类库

Java泛型是基于对泛型参数类型的擦除(Object实例),List实际上得到的List,整型元素(4B)变为指向整型对象(16B)内存的指针。

Stream对此区分(int,long,double),减少性能开销。

public static void printTrackLengthStatistics(Album album){
  IntSummaryStatistics trackLengthStats 
    = album.getTracks()
    .mapToInt(track -> track.getLength()) //返回IntStream
    .summaryStatitics();
  System.out.println("Max: %d, Min: %d, Ave: %d, Sum: %d",
                    trackLengthStats.getMax(),
                    trackLengthStats.getMin(),
                    trackLengthStats.getAverage(),
                    trackLengthStats.getSum())
}

重载解析时,lamba作为参数时,类型由目标类型推到得出。从函数接口参数或最具体的类型推导出。

函数接口需要添加@FunctionalInterface

为了二进制接口的兼容性(Collection接口添加了stream方法),Java8添加默认方法。

//Iterable 
default void foreach(Consumer< ? super T> action){
  for (T t : this){
    action.accept(t);
  }
}

与静态方法相反,与类定义的方法产生冲突时,优先选择类的具体方法。

因此产生的多重继承(避免对象状态的继承),需要在子类中Override

public class MusicalCarriage implements Carriage, Jukebox{
  @Override
  public String rock(){
    return Carriage.super.rock();
  }
}

同时添加了接口的静态方法特性,如Stream.of。

为了避免引用null值的变量,可以使用Optional。

Optional<Stirng> a = Optional.of("a");
Optional emptyOptional = Optional.empty();
Optional emptyOptional = Optional.ofNullable(null);
assertEquals(a.isPresent());
assertEquals("b", emptyOptional.ofElse("b"));
assertEquals("c", emptyOptional.ofElseGet( ()->"c" ));

5.高级集合类和收集器

方法引用,即ClassName::methodName,如Artist::new、String[]::new。

流中元素的出现顺序与创建的集合有关,有序较友好,按序处理用forEachOrdered。

转换为其他集合、值

stream.collect(toCollection(TreeSet::new));

//找出最大
public Optional<Artist> biggestGroup(Stream<Artist> artists){
    Function<Artist, Long> getCount = artist -> artist.getMemebers().count();
    artists.collect(maxBy(comparing(getCount)));
}

//找出平均数
public double averageNumberOfTracks(List<Album> albums){
  return albums.stream().
    .collect(averageingInt(album -> album.getTrackList().sizeA() ));
}

数据分块

public Map<Boolean, List<Artist>> bandAndSolo(Stream<Artist> artists){
  return artists.collect(partitioningBy(artist -> artist.isSolo()));
}

数据分组

public Map<Boolean, List<Artist>> bandAndSolo(Stream<Artist> artists){
  return artists.collect(groupingBy(album -> album.getMainMusician()));
}

字符串

StringBuilder builder = new StringBuilder("[");
for(Artist artist : artists){
  if (builder.length()>1)
    builder.append(", ");
  String name = artist.getName();
  builder.append(name);
}
builder.append("]");
String result = builder.toString();

//Stream
String result = artists.stream()
  .map( artist -> artist.geName() )
  .collect( Collectors.joining(", ", "[", "]") ); 

组合收集器

Map<Artist, Long> a = albums.collect(groupingBy(album -> album.getMainMusician(), counting()));

Map<Artist, List<String>> b = albums.collect(groupingBy(Album::getMainMusician, mapping(Album::getName, toList())));

重构收集器

StringCombiner combined = artists.stream()
  .map(Artist::getName)
  .reduce(new StringCombiner(", ", "[", "]"),
         StringCombiner::add, StringCombiner::merage);
String result = combined.toString();

//定制化
StringCombiner combined = artists.stream()
  .map(Artist::getName)
  .collect(Collectors.reducing(new StringCombiner(", ", "[", "]"), 
                              name -> new StringCombiner(", ", "[", "]").add(name), StringCombiner::merge));

一些细节

//读缓存
public Artist getArtist(String name){
  Artist artist = artistCache.get(name);
  if(artist == null){
    artist = readArtistFromDB(name);
    artistCache.put(name);
  }
  return artist;
}

//Stream
public Artist getArtist(String name){
  return artistCache.computeIfAbsent(name, this::readArtistFromDB);
}

//在Map上迭代
Map<Artist, Integer> countOfAlbums = new HashMap<>();
for(Map.Entry<Artist, List<Album>> entry:albumsByArtist.entrySet()){
  Artist artist = entry.getKey();
  List<Album> albums = entry.getValue();
  aountOfAlbums.put(artist, albums.size());
}

//Stream
Map<Artist, Integer> countOfAlbums = new HashMap<>();
albumsByArtist.forEach( (artist, albums) -> {aountOfAlbums.put(artist, albums.size());} );

6.数据并行化

并发为共享时间,并行为同时发生,有数据并行化和任务并行化,利用多核。

parallel和parallelStream

//蒙特卡洛模拟法并行化模拟掷骰子事件
public Map<Integer, Double> parallelDiceRolls() {
  double fraction = 1.0/N;
  return IntStream.range(0, N)
    .parallel()
    .mapToObj(twoDiceThrows())
    .collect(groupingBy(side -> side, summingDouble(n -> fraction)));
}


//手动模拟

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadLocalRandom;

public class ManualDiceRolls {
      private static final int N = 1000;

      private final double fraction;
      private final Map<Integer, Double> results;
      private final int numberOfThreads;
      private final ExecutorService executor;
      private final int workPerThread;

      public static void main(String[] args){
          ManualDiceRolls roles = new ManualDiceRolls();
          roles.simulateDiceRoles();
      }

      public ManualDiceRolls(){
          fraction = 1.0/N;
          results = new ConcurrentHashMap<>();
          numberOfThreads = Runtime.getRuntime().availableProcessors();
          executor = Executors.newFixedThreadPool(numberOfThreads);
          workPerThread = N / numberOfThreads;
      }

      public void simulateDiceRoles(){
          List<Future<?>> futures = submitJobs();
          awaitCompletion(futures);
          printResults();
      }

      private void printResults(){
          results.entrySet().forEach(System.out::println);
      }

      private List<Future<?>> submitJobs(){
          List<Future<?>> futures = new ArrayList<>();
          for ( int i=0; i<numberOfThreads; i++){
              futures.add(executor.submit(makeJob()));
          }
          return futures;
      }

      private Runnable makeJob(){
          return () -> {
              ThreadLocalRandom random = ThreadLocalRandom.current();
              for ( int i=0; i<workPerThread; i++){
                  int entry = twoDiceThrows(random);
                  accumlateResult(entry);
              }
          };
      }

      private void accumlateResult(int entry){
          results.compute( entry,
                  (key, previous) -> previous==null ?fraction :previous+fraction
                          );
      }

      private int twoDiceThrows(ThreadLocalRandom random){
          int firstThrow = random.nextInt(1, 7);
          int secondThrow = random.nextInt(1, 7);
          return firstThrow+secondThrow;
      }

      private void awaitCompletion(List<Future<?>> futures){
          futures.forEach( (future) -> {
                        try {
                            future.get();
                        } catch (InterruptedException | ExecutionException e) {
                            e.printStackTrace();
                        }
                }

            );
          executor.shutdown();
      }
}

限制

reduce操作在并行时,初值为组合函数的恒等值,必须符合结合律。

性能

主要由数据大小,源数据结构,装箱,核的数量,单元处理开销。

按源数据结构(分解方式:对半),性能好:ArrrayList、IntStream.range等;性能一般:HashSet、TreeSet等;性能差:LinkedList、Streams.iterate、BufferedRead.lines。

无状态并行操作,性能更好,如map、filter、faltMap,反之sorted、distinct、limit。

private int addIntegers(List<Integer> values){
  return values.parallelStream()
    .mapToInt( i -> i )
    .sum();
}

并行化数组操作

//for循环
public static double[] imperativeInitilize(int size){
  double[] values = new double[size];
  for (int i=0; i<values.length; i++){
    values[i] = i;
  }
  return values;
}

//并行化初始
public static double[] parallelInitialize(int size){
  double[] values = new double[size];
  Arrays.parallelSetAll(values, i->i);
  return values;
}

//对时间序列计算简单滑动平均数
public static double[] simpleMovingAverage(double[] values, int n){
  double[] sums = Arrays.copyOf(values, values.length);
  Arrays.parallelPrefix(sums, Double::sum);
  int start = n-1;
  return IntStream.range(start, sums.length)
    .mapToDouble(i -> {
      double prefix = i==start ? 0: sums[i-n];
      return (sums[i]-prefix)/n;
    })
    .toArray();
}

7.测试、调试、重构

//if(logger.isDebugEnabled())
Logger logger = new Loggger();
logger.debug(()->"Look at"+expensiveOperation());

//匿名类
ThreadLocal<Album> thisAlbum = ThreadLocal.withInitial( ()->database.lookupCurrentAlbum() );

//Write Everything Twice
public long countFeature(ToLongFunction<Album> function){
  return albums.stream()
    .mapToLong(function)
    .sum();
}

public logn countTracks(){
  return countFeature( album -> album.getTracks().count());
}

public long countRunningTime(){
  return countFeature(album -> album.getTracks()
                     .mapToLong( track -> track.getLength() )
                     .sum());
}

单元测试

使用方法引用

public static List<String> elementFirstToUppercase(List<String> words){
  return words.stream()
    .map(Testing::firstToUppercase)
    .collect(Collectors.<String>toList());
}

public static String firstToUppercase(Stirng value){
  char firstChar = Character.toUpperCase(value.charAt(0));
  return firstChar+value.substring(1);
}

@Test
public void twoLetterStringConvertedToUpperCase(){
  String input = "ab";
  String result = Testing.firstToUpperCase(input);
  assertEquals("Ab", result);
}

@Test
public void canCountFeatures(){
  OrderDomain order = new OrderDomain( asList(
    newAlbum("A"),newAlbum("B"),newAlbum("C"),newAlbum("D")
  ) );
  assertEquals(8, order.countFeature(album->2));
}

//结合Mockito框架
List<String> list = mock(List.class);
when (list.size()).thenAnswer(inv->otherList.size());
assertEquals(3, list.size());

使用peek,记录中间值。查看流中的每个值并操作,调试

Set<String> nationalities = album.getMusicians()
  .filter(artist -> artist.getName().startsWith("The"))
  .map(artist -> artist.getNationality())
  .peek(nation -> System.out.println("Nation:"+nation))
  .collect(Collectors.<String>toSet());

8.设计和架构的原则

lambda改变设计模式

命令行模式

public interface Editor{
  public void open();
  public void save();
  public void close();
}

public interface Action{
  public void perform();
}

public class Open implements Action{
  private final Editor editor;
  public Open(Editor editor){
    this.editor = editor;
  }
  @Override
  public void perform(){
    editor.open();
  }
}

publvi class Macro {
  private final List<Action> actions;
  public Macro(){
    actions = new ArrayList<Action>;
  }
  public void record(Action action){
    actions.add(action);
  }
  public void run(){
    acitons.forEach(Action::perform);
  }
}

Macro macro = new Macro();
macro.record(new Open(editor));
macro.record(new Save(editor));
macro.record(new Close(editor));
marco.run();

//lambda
Macro macro = new Macro();
macro.record(editor::open);
macro.record(editor::save);
macro.record(editor::close);
marco.run();

策略模式

public class Compressor{
  private final CompressionStrategy strategy;
  public Compressor(CompressionStrategy strategy){
    this.strategy = strategy;
  }
  public void compress(Path inFile, File outFile) throws IOException {
    try(OutputStream outStream = new FileOutputStream(outFile)){
      File.copy(inFile, strategy.compress(outStream));
    }
  }
}

public class GzipCompressionStrategy implements CompressionStrategy{
  @Override
  public OutputStream compress(OutputStream data) throws IOException{
    return new GZIPOutputStream(data);
  }
}

Compress gzipCompressor = new Compress(new GzipCompressionStrategy);
gzipCompressor.compress(inFile, outFile);
Compress zipCompressor = new Compress(new ZipCompressionStrategy);
zipCompressor.compress(inFile, outFile);

//lambda
Compress gzipCompressor = new Compress(GZIPOutputStream::new);
gzipCompressor.compress(inFile, outFile);
Compress zipCompressor = new Compress(ZIPOutputStream::new);
zipCompressor.compress(inFile, outFile);

观察者模式

public class Nasa implements LandingObserver{
  @Override
  public void observeLanding(String name){
    if(name.contains("Apollo")){
      System.out.println("We made it~");
    }
  }
}

public class Moon{
  private final List<LandingObserver> observers = new ArrayList<>();
  public void land(String name){
    for (LandingObserver observer: observers){
      observer.observeLanding(name);
    }
  }
  public void startSpying(LandingObserver observer) {
    observers.add(observer);
  }
}

Moon moon = new Moon();
moon.startSpying(new Nasa());
moon.startSpying(new Aliens());
moon.land("An asteroid");moon.land("Apollo 11");

//lambda
Moon moon = new Moon();
moon.startSpying(name -> {
      if(name.contains("Apollo")){
      System.out.println("We made it~");
    }
});
moon.startSpying(name -> {
      if(name.contains("Apollo")){
      System.out.println("They're distracted.");
    }
});
moon.startSpying(new Aliens());
moon.land("An asteroid");moon.land("Apollo 11");

模板方法模式

public abstract class LoanApplication{
  public void checkLoanApplication() throws ApplicationDenied{
    checkIdentity();
    checkCreditHistory();
    checkIncomeHistroy();
    reportFindings();
  }
  public void checkLoanApplication() throws ApplicationDenied;
  public void checkCreditHistory() throws ApplicationDenied;
  public void checkIncomeHistroy() throws ApplicationDenied;
  private void reportFindings(){ ... }
}



//lamba
public class LoanApplication{
  private final Criteria identity;
  private final Criteria creditHistory;
  private final Criteria incomeHistroy;

  public void LoanApplication(Criteria identity,Criteria creditHistory,Criteria incomeHistroy){
    this.identity = identity;
    this.creditHistory = creditHistory;
    this.incomeHistroy = incomeHistroy;
  }
  public void checkLoanApplication() throws ApplicationDenied{
    identity.check();
    creditHistory.check();
    incomeHistroy.check();
    reportFindings();
  }
  private void reportFindings(){ ... }
}

public interface Criteria{
  publci void check() throws ApplicationDenied;
}

public CompanyLoanApplication extends LoanApplication{
  public CompanyLoanApplication(Company company){
    super(company::checkHistory,company::checkHistoricalDebt,company::checkProfitAndLoss);
  }
}

使用lambda表达式的DSL

流畅性依赖于善用IDE的自动补全

public static void describe(String name, Suite behavior){
  Description description = new Description(name);
  behavior.specifySuite(description);
}

public void should(String description, Specification specification){
  try{
    Except except = new Except();
    specification.specifyBehaviour(except);
    Runner.current.recordSuccess(suite, description);
  }catch(AssertionError cause){
    Runner.current.recordFailure(suite, description, cause);
  }catch(Throwable cause){
    Runner.current.recordError(suite, description, cause);
  }
}

//except.that(stack.pop()).isEqualTo(2);
public final class Except{
  public BoundException that(Object value){
    return new BoundException(value);
  }
}

public class StackSepc{{
  ...
}}
//==
public class StackSepc{
  public StackSepc(){
  ...
  }
}

使用lambda表达式的SOLID

单一功能原则

public long countPrimes(int upTo){
  long tally = 0;
  for (int i=0; i<upTo; i++){
    boolean isPrime = true;
    for (int j=2; j<i; i++){
      if(i%j==0){
        isPrime = false;
      }
    }
    if(isPrime){
        tally++;
    }
  }
  return tally;
}

//重构
public long countPrimes(int upTo){
  long tally = 0;
  for (int i=0; i<upTo; i++){
    boolean isPrime = true;
    if(isPrime(i)){
        tally++;
    }
  }
  return tally;
}

private boolean isPrime(int number){
    for (int j=2; j<number; i++){
      if(i%j==0){
        isPrime = false;
      }
    }
}

//集合流重构
public long countPrimes(int upTo){
  return IntStream.range(1, upTo)
    //.parallel() //并行
    .filter(this::Prime)
    .count();
}
public long isPrime(int number){
  return IntStream.range(2, number)
    .allMatch(x -> (number%x) != 0);
}

开闭原则

如不可变对象String,首次调用hashCode方法缓存了生成的哈希值

ThreadLocal<DateFormat> localFormatter = ThreadLocal.withInitial(()->new SimpleDateFormat());
DateFormat formatter = localFormatter.get();

AtomicInteger threadId = new AtomicInteger();
ThreadLocal<Integer> localId = ThreadLocal.withInitial(()->threadId.getAndIncrement());
int idForThisThread = localId.get();

依赖反转原则

细节反而以来抽象(如上层逻辑依赖底层抽象)

public List<String> findHeadings(Reader input){
  try(BufferedReader reader = new BufferedReader(input)){
    return reader.lines()
      .filter(line -> line.endsWith(":"))
      .map(line -> line.substring(0, line.length()-1))
      .collect(toList());
  }catch(IOException e){
    throw new HadingLookuoException(e);
  }
}

//重构
public List<String> findHeadings(Reader input){
  return withLinesOf(reader.lines(), lines -> lines.filter(line -> line.endsWith(":"))
      .map(line -> line.substring(0, line.length()-1))
      .collect(toList(), HadingLookuoException::new));
}

public <T> T withLinesOf(Reader input, Function<Stream<String>> handler, Function<IoException, RuntimeException error){
  try(BufferedReader reader = new BufferedReader(input)){
    return handler.apply(reader.lines());
  }catch(IOException e){
    throw error.apply(e);
  }
}

9.使用lambda表达式编写并发程序

使用Vert.x和RxJava框架,非阻塞式IO

回调

//Vert.x下聊天应用
//接受TCP连接
public class ChatVerticle extends Verticle{
  public void start(){
    vertx.createNetServer()
      .connectHandler(socket -> { //回调
        container.logger().info("socket connected");
        socket.dataHandler(new User(socket, this));
      }
      )
      .listen(10_000);
  }
}

//处理用户连接
public class User implements Handler<Buffer>{
  private static final Pattern newline = Pattern.compile("\\n");

  private final NetSocket socket;
  private final Set<String> names;
  private final EventBus eventBus;

  private Optional<String> name;

  public User(NetSocket socket, Verticle verticle){
    Vertx vertx = verticle.getVertx();
    this.socket = socket;
    names = vertx.sharedData().getSet("names");
    eventBus = vertx.eventBus();
    name = Optional.empty();
  }

  @Override
  public void handler(Buffer buffer){
    newline.splitAsStream(buffer.toString())
      .forEach(line -> {
        if(!name.isPresent())
          setName(line);
        else
          handlerMessage(line);
      });

    ....
  }

  //注册聊天消息
  eventBus.registerHandler(name, (Message<String> msg) -> {
    sendClient(msg.body);
  } );
  //发送聊天信息
  eventBus.send(user, name.get()+">"+message);
  //群发消息
  private void broadcastMessage(String message){
    String name = this.name.get();
    eventBus.publish(name+".followers", name+">"+message);
  }
  //接受群发消息
  private void followUser(String user){
    eventBus.registerHandler(user+".followers", (Message<String> msg) -> {
      sendClient(msg.body);
    } );
  }

消息传递架构

Vert.x通过复制发送的消息,避免共享状态。易于测试,隔离错误,不必重启JVM,重启本地Verticle对象即可。

@Test //聊天程序服务端
public void canMessageFriend(){
  withModule(this::messageFriendWithModule);
}

private void messageFriendWithModule(){
  withConection(richard -> {
    checkBobReplies(richard);
    richard.write("richard\n");
    messageBob(richard);
  });
}

private void checkBobReplies(NetSocket richard){
    richard.handler(data -> {
    assertEquals("bob>oh its you!", data.toString());
    moduleTestComplete();
  });
}

private void messageBob(NetSocket richard){
  withConection(messageBobWithConnection(richard));
}

private Handler<NetSocket> messageBobWithConnection(NetSocket bob){
  return bob -> {
      checkRichardMessageYou(bob);
    bob.write("bob\n");
    vertx.setTimer(6, id -> richard.write("bob<hai"));
  };
}

private void checkRichardMessageYou(NetSocket bob){
  bob.handler(data -> {
    assertEquals("richard>hai", data.toString());
    bob.write("richard<oh its you!");
  });
}

使用Future并行操作

public Album lookupByName(String albumName){
  FUture<Credentials> trackLogin = loginTo("track");
  FUture<Credentials> artistLogin = loginTo("artist");
  try{//上两者阻塞
    Future<List<Track>> tracks = lookupTracks(albumName, trackLogin.get());
    Future<List<Artist>> aritists = lookupTracks(albumName, artistLogin.get());
    return new Album(albunName, tracks.get(), artitsts.get());
  }catch(InterruptedException | ExecutionException e){
    throw new AlbumLookupException(e.getCause());
  }
}

//使用CompletableFuture
public Album lookupByName(String albumName){
  CompletaleFuture<List<Artist>> artistLookup = loginTo("artist")
    .thenCompose(artistLogin -> lookupArtists(album, artistLogin));
  return loginTo("track")
    .thenCompose(trackLogin -> lookupTracks(album, trackLogin))
    .thenCombine(artistLookup, (tracks, artists) -> new Album(albunName, tracks, artitsts) )
    .join();
}
//创建CompleteableFuture
CompleteableFuture<Artist> createFuture(String id){
  CompleteableFuture<Artist> future = new CompleteableFuture<>();
  startJob(future);//在另外线程模型中后续赋值
  return future;
}
//提供值
future.complete(artist);
//异步执行
CompleteableFuture<Track> lookupTrack(String id){
  return CompleteableFuture.supplyAsync(()->{
    ...
      return track;
  }, service);
}
//出错时
future.completeExceptionally(AlbumLookupException("Unable to find "+name));

响应式编程

//RxJava 组合异步和基于事件的系统流程,处理数据流
public Observable<Artist> search(String searchedName, String searchedNationality, int maxResults){
  return getSavedArtists()
    .filter(name -> name.contains(searchedName))
    .flatMap(this::lookupArtist)
    .filter(artist -> artist.getNationality().contains(searchedNationality))
    .tack(maxResults);
}
//传值
observer.onNext("a");
observer.onCompleted();
//Error
observer.onError(new Exception());
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值