Java 8 函数式编程

来源:互联网 发布:淘宝客微博推广教程 编辑:程序博客网 时间:2024/05/16 04:14

《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();

常用的操作

//collectList<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.minArtist 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();//StreamString 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;}//Streampublic 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());}//StreamMap<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 Twicepublic 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);}@Testpublic void twoLetterStringConvertedToUpperCase(){  String input = "ab";  String result = Testing.firstToUpperCase(input);  assertEquals("Ab", result);}@Testpublic 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();//lambdaMacro 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);//lambdaCompress 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");//lambdaMoon 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(){ ... }}//lambapublic 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());  }}//使用CompletableFuturepublic 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();}//创建CompleteableFutureCompleteableFuture<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();//Errorobserver.onError(new Exception());
原创粉丝点击