Java 8 函数式编程学习笔记
来源:互联网 发布:字体识别软件 编辑:程序博客网 时间:2024/06/05 06:06
Java 8 函数式编程学习笔记
@(JAVASE)[java8, 函数式编程, lambda]
- Java 8 函数式编程学习笔记
- 参考内容
- Java 8中重要的函数接口
- 扩展函数接口
- 常用的流操作
- reduce模式
- 基本原理
- reduce方法API
- 案例
- 使用reduce和Lambda表达式实现map
- 使用reduce和Lambda表达式实现filter
- 类库
- 基本类型
- 重载解析
- 总结
- 情况一
- 情况二
- 情况三
- 默认方法
- Optional
- 高级集合类和收集器
- 收集器
- 定制收集器
- 案例1
- 案例2
- 定制收集器
- 收集器
- 数据并行化
- 模拟系统
- 限制
- 性能
- 并行化数组操作
- 调试
- Java 8中重要的函数接口
- 参考内容
参考内容
Java 8中重要的函数接口
Predicate<T>
T boolean 这张唱片已经发行了吗 Consumer<T>
T void 输出一个值 Function<T,R>
T R 获得Artist 对象的名字 Supplier<T>
None T 工厂方法 UnaryOperator<T>
T T 逻辑非(!) BinaryOperator<T>
(T, T) T 求两个数的乘积(*)扩展函数接口
ToLongFunction<T>
T long LongFunction<R>
long R LongUnaryOperator
long long 常用的流操作
// collect @Test public void collectToList() { List<String> collected = Stream.of("a", "b", "c").collect(Collectors.toList()); assertEquals(Arrays.asList("a", "b", "c"), collected); } // map @Test public void mapToUpperCase() { List<String> collected = Stream.of("a", "b", "hello").map(String::toUpperCase).collect(toList()); assertEquals(asList("A", "B", "HELLO"), collected); } // filter @Test public void functionalStringsWithNumbers() { List<String> beginningWithNumbers = Stream.of("a", "1abc", "abc1") .filter(value -> Character.isDigit(value.charAt(0))).collect(Collectors.toList()); assertEquals(Collections.singletonList("1abc"), beginningWithNumbers); } // flatMap @Test public void flatMapCharacters() { List<Integer> together = Stream.of(Arrays.asList(1, 2), Arrays.asList(3, 4)) .flatMap(Collection::stream).collect(Collectors.toList()); assertEquals(asList(1, 2, 3, 4), together); } // min @Test public void streamsMinLength() { List<Track> tracks = asList( new Track("Bakai", 524), new Track("Violets for Your Furs", 378), new Track("Time Was", 451)); Track shortestTrack = tracks.stream().min(Comparator.comparing(Track::getLength)).get(); assertEquals(tracks.get(1), shortestTrack); } // reduce @Test public void sumUsingReduce() { int count = Stream.of(1, 2, 3).reduce(0, (acc, element) -> acc + element); assertEquals(6, count); } @Test public void expandedReduce() { BinaryOperator<Integer> accumulator = (acc, element) -> acc + element; int count = accumulator.apply(accumulator.apply(accumulator.apply(0, 1), 2), 3); assertEquals(6, count); } @Test public void countUsingReduceFor() { int acc = 0; for (Integer element : asList(1, 2, 3)) { acc = acc + element; } assertEquals(6, acc); }
reduce模式
基本原理
// 初始值 int acc = 0; for (Integer element : asList(1, 2, 3)) { // 累加处理+合并处理 acc = acc + element; }
reduce方法API
// accumulator 累加处理 Optional<T> reduce(BinaryOperator<T> accumulator); // identity 初始值 // accumulator 累加处理 T reduce(T identity, BinaryOperator<T> accumulator); // identity 初始值 // accumulator 累加处理 // combiner 合并处理 <U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
案例
使用reduce和Lambda表达式实现map
public static <I, O> List<O> map(Stream<I> stream, Function<I, O> mapper) { return stream.reduce(new ArrayList<O>(), (acc, x) -> { List<O> newAcc = new ArrayList<>(acc); newAcc.add(mapper.apply(x)); return newAcc; }, (List<O> left, List<O> right) -> { List<O> newLeft = new ArrayList<>(left); newLeft.addAll(right); return newLeft; }); }
使用reduce和Lambda表达式实现filter
public static <I> List<I> filter(Stream<I> stream, Predicate<I> predicate) { return stream.reduce(new ArrayList<I>(), (acc, x) -> { List<I> newAcc = new ArrayList<>(acc); if (predicate.test(x)) { newAcc.add(x); } return newAcc; }, (List<I> left, List<I> right) -> { List<I> newLeft = new ArrayList<>(left); newLeft.addAll(right); return newLeft; }); }
类库
基本类型
public long countRunningTime() { return countFeature(album -> album.getTracks() .mapToLong(track -> track.getLength()) .sum()); } public static void printTrackLengthStatistics(Album album) { IntSummaryStatistics trackLengthStats = album.getTracks() .mapToInt(Track::getLength).summaryStatistics(); System.out.printf("Max: %d, Min: %d, Ave: %f, Sum: %d", trackLengthStats.getMax(), trackLengthStats.getMin(), trackLengthStats.getAverage(), trackLengthStats.getSum()); }
重载解析
总结
总而言之,Lambda表达式作为参数时,其类型由它的目标类型推导得出,推导过程遵循如下规则:
- 如果只有一个可能的目标类型,由相应函数接口里的参数类型推导得出;
- 如果有多个可能的目标类型,由最具体的类型推导得出;
- 如果有多个可能的目标类型且最具体的类型不明确,则需人为指定类型。PS:三种情况分别对应,无重载、有重载且重载参数为父子关系、有重载且重载参数无关联。
情况一
private void overloadedMethod(Predicate<Integer> predicate) { System.out.print("Predicate"); } @Test public void mostSpecificPredicate() { // 输出:IntPredicate overloadedMethod((x) -> true); }
情况二
private interface IntPredicate extends Predicate<Integer>{ @Override boolean test(Integer value); } private void overloadedMethod(Predicate<Integer> predicate) { System.out.print("Predicate"); } private void overloadedMethod(IntPredicate predicate) { System.out.print("IntPredicate"); } @Test public void mostSpecificPredicate() { // 输出:IntPredicate overloadedMethod((x) -> true); }
情况三
private interface IntPredicate { public boolean test(int value); } private void overloadedMethod(Predicate<Integer> predicate) { System.out.print("Predicate"); } private void overloadedMethod(IntPredicate predicate) { System.out.print("IntPredicate"); } @Test public void mostSpecificPredicate() { // 输出:IntPredicate overloadedMethod((IntPredicate) (x) -> true); }
默认方法
三定律
1. 类胜于接口。如果在继承链中有方法体或抽象的方法声明,那么就可以忽略接口中定义的方法。
2. 子类胜于父类。如果一个接口继承了另一个接口,且两个接口都定义了一个默认方法,那么子类中定义的方法胜出。
3. 没有规则三。如果上面两条规则不适用,子类要么需要实现该方法,要么将该方法声明为抽象方法。其中第一条规则是为了让代码向后兼容。
public default void welcome() { message("Parent: Hi!"); }
Optional
@Test public void examples() { Optional<String> a = Optional.of("a"); assertEquals("a", a.get()); Optional emptyOptional = Optional.empty(); Optional alsoEmpty = Optional.ofNullable(null); assertFalse(emptyOptional.isPresent()); assertTrue(a.isPresent()); assertEquals("b", emptyOptional.orElse("b")); assertEquals("c", emptyOptional.orElseGet(() -> "c")); }
高级集合类和收集器
收集器
// 指定收集类型 public void toCollectionTreeset() { Stream<Integer> stream = Stream.of(1, 2, 3); stream.collect(Collectors.toCollection(TreeSet::new)); } // 最大值 public Optional<Artist> biggestGroup(Stream<Artist> artists) { Function<Artist, Long> getCount = artist -> artist.getMembers().count(); return artists.collect(Collectors.maxBy(comparing(getCount))); } // 平均值 public double averageNumberOfTracks(List<Album> albums) { return albums.stream() .collect(Collectors.averagingInt(album -> album.getTrackList().size())); } // 数组分块,使用Predicate函数接口判断,ture一块;false一块 public Map<Boolean, List<Artist>> bandsAndSolo(Stream<Artist> artists) { return artists.collect(Collectors.partitioningBy(Artist::isSolo)); } // 数据分组 public Map<Artist, List<Album>> albumsByArtist(Stream<Album> albums) { return albums.collect(Collectors.groupingBy(Album::getMainMusician)); } // 字符串合并 public static String formatArtists(List<Artist> artists) { return artists.stream() .map(Artist::getName) .collect(Collectors.joining(", ", "[", "]")); } // 分组后获取每组数量的总数 public Map<Artist, Long> numberOfAlbums(Stream<Album> albums) { return albums.collect(Collectors.groupingBy(Album::getMainMusician, Collectors.counting())); } // 分组后获取每组数据中的映射数据 public Map<Artist, List<String>> nameOfAlbums(Stream<Album> albums) { return albums.collect(Collectors.groupingBy(Album::getMainMusician, Collectors.mapping(Album::getName, Collectors.toList()))); }
定制收集器
案例1
// reduce实现 public static String formatArtistsRefactor4(List<Artist> artists) { return artists.stream() .map(Artist::getName) .reduce(new StringCombiner(", ", "[", "]"), StringCombiner::add, StringCombiner::merge) .toString(); } // 自定义收集器 public static String formatArtistsRefactor5(List<Artist> artists) { return artists.stream() .map(Artist::getName) .collect(new StringCollector(", ", "[", "]")); } // reducing收集器 public static String formatArtistsReducing(List<Artist> artists) { return artists.stream() .map(Artist::getName) .collect(Collectors.reducing( new StringCombiner(", ", "[", "]"), name -> new StringCombiner(",", "[", "]").add(name), StringCombiner::merge)) .toString(); }
import java.util.Collections;import java.util.Set;import java.util.function.BiConsumer;import java.util.function.BinaryOperator;import java.util.function.Function;import java.util.function.Supplier;import java.util.stream.Collector;public class StringCollector implements Collector<String, StringCombiner, String> { private static final Set<Characteristics> characteristics = Collections.emptySet(); private final String delim; private final String prefix; private final String suffix; public StringCollector(String delim, String prefix, String suffix) { this.delim = delim; this.prefix = prefix; this.suffix = suffix; } @Override public Supplier<StringCombiner> supplier() { return () -> new StringCombiner(delim, prefix, suffix); } @Override public BiConsumer<StringCombiner, String> accumulator() { return StringCombiner::add; } @Override public BinaryOperator<StringCombiner> combiner() { return StringCombiner::merge; } @Override public Function<StringCombiner, String> finisher() { return StringCombiner::toString; } @Override public Set<Characteristics> characteristics() { return characteristics; }}
public class StringCombiner { private final String prefix; private final String suffix; private final String delim; private final StringBuilder buIlder; public StringCombiner(String delim, String prefix, String suffix) { this.prefix = prefix; this.suffix = suffix; this.delim = delim; this.buIlder = new StringBuilder(); } public StringCombiner add(String word) { if (!this.areAtStart()) { this.buIlder.append(delim); } this.buIlder.append(word); return this; } public StringCombiner merge(StringCombiner other) { if (!other.equals(this)) { if (!other.areAtStart() && !this.areAtStart()) { other.buIlder.insert(0, this.delim); } this.buIlder.append(other.buIlder); } return this; } @Override public String toString() { return prefix + buIlder.toString() + suffix; } private boolean areAtStart() { return buIlder.length() == 0; }}
案例2
import java.util.*;import java.util.function.BiConsumer;import java.util.function.BinaryOperator;import java.util.function.Function;import java.util.function.Supplier;import java.util.stream.Collector;public class GroupingBy<T, K> implements Collector<T, Map<K, List<T>>, Map<K, List<T>>> { private final static Set<Characteristics> characteristics = new HashSet<>(); static { characteristics.add(Characteristics.IDENTITY_FINISH); } private final Function<? super T, ? extends K> classifier; public GroupingBy(Function<? super T, ? extends K> classifier) { this.classifier = classifier; } @Override public Supplier<Map<K, List<T>>> supplier() { return HashMap::new; } @Override public BiConsumer<Map<K, List<T>>, T> accumulator() { return (map, element) -> { K key = classifier.apply(element); List<T> elements = map.computeIfAbsent(key, k -> new ArrayList<>()); elements.add(element); }; } @Override public BinaryOperator<Map<K, List<T>>> combiner() { return (left, right) -> { right.forEach((key, value) -> left.merge(key, value, (leftValue, rightValue) -> { leftValue.addAll(rightValue); return leftValue; })); return left; }; } @Override public Function<Map<K, List<T>>, Map<K, List<T>>> finisher() { return map -> map; } @Override public Set<Characteristics> characteristics() { return characteristics; }}
数据并行化
模拟系统
import org.openjdk.jmh.annotations.*;import org.openjdk.jmh.runner.Runner;import org.openjdk.jmh.runner.RunnerException;import org.openjdk.jmh.runner.options.Options;import org.openjdk.jmh.runner.options.OptionsBuilder;import java.util.Map;import java.util.concurrent.ThreadLocalRandom;import java.util.function.IntFunction;import java.util.stream.IntStream;import static java.util.stream.Collectors.groupingBy;import static java.util.stream.Collectors.summingDouble;@State(Scope.Benchmark)@BenchmarkMode(Mode.All)public class DiceRolls { private static final int N = 1000000; public static void main(String[] args) throws RunnerException { Options options = new OptionsBuilder() .include(DiceRolls.class.getSimpleName()) .forks(1) .build(); new Runner(options).run(); } @Benchmark public Map<Integer, Double> serialDiceRolls() { double fraction = 1.0 / N; return IntStream.range(0, N) .mapToObj(twoDiceThrows()) .collect(groupingBy(side -> side, summingDouble(n -> fraction))); } @Benchmark 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))); } private static IntFunction<Integer> twoDiceThrows() { return i -> { ThreadLocalRandom random = ThreadLocalRandom.current(); int firstThrow = random.nextInt(1, 7); int secondThrow = random.nextInt(1, 7); return firstThrow + secondThrow; }; }}
限制
- 初值必须为组合函数的恒等值。例:
(acc, element) -> acc + element
,初值为0 - 组合操作必须符合结合律。例:
(4+2)+1=4+(2+1)=7 - parallel方法、sequential方法。在要对流求值时,不能同时处于两种模式,要么是并行的,要么是串行的。如果同时调用了parallel和sequential方法,最后调用的那个方法起效。
性能
- 数据大小
- 只有数据足够大、每个数据处理管道花费的时间足够多时,并行化处理才有意义。
- 源数据结构
- 数据源分割开销影响了在管道中并行处理数据时到底能带来多少性能上的提升。
- 装箱
- 处理基本类型比处理装箱类型要快。
- 核的数量
- 极端情况下,只有一个核,因此完全没必要并行化。显然,拥有的核越多,获得潜在性能提升的幅度就越大。在实践中,核的数量不单指你的机器上有多少核,更是指运行时你的机器能使用多少核。这也就是说同时运行的其他进程,或者线程关联性(强制线程在某些核或CPU 上运行)会影响性能。
- 单元处理开销
- 比如数据大小,这是一场并行执行花费时间和分解合并操作开销之间的战争。花在流中每个元素身上的时间越长,并行操作带来的性能提升越明显。
并行化数组操作
调试
public static Set<String> nationalityReportUsingPeek(Album album) { Set<String> nationalities = album.getMusicians() .filter(artist -> artist.getName().startsWith("The")) .map(artist -> artist.getNationality()) .peek(nation -> System.out.println("Found nationality: " + nation)) .collect(Collectors.<String>toSet()); return nationalities; }
———–参考《Java 8 函数式编程》
阅读全文
0 0
- Java 8函数式编程学习笔记
- Java 8 函数式编程学习笔记
- Java工作笔记:Java函数式编程学习
- 学习笔记:函数式编程
- 函数和函数式编程学习笔记
- python 学习笔记8 函数式编程functional programming
- python学习笔记(四) - 函数式编程
- Python学习笔记<函数式编程>
- Python学习笔记2:函数式编程
- 【Python学习笔记】函数式编程
- "Python"学习笔记----函数式编程
- Python学习笔记:函数式编程工具
- 【python学习笔记】Python函数式编程
- Scala学习笔记之Scala函数及函数式编程
- python 学习笔记---函数式编程之高阶函数
- 【Python学习笔记】函数式编程:高阶函数
- 【Python学习笔记】函数式编程:高阶函数filter
- 【Python学习笔记】函数式编程:高阶函数sorted
- 微信小程序开发-路由
- 11.11学习笔记之keras的sequential模型
- 在spring mvc应用中创建第一个Controller
- c语言:用switch模拟售货机
- android 使用控件webView 与 vue交互
- Java 8 函数式编程学习笔记
- Java文件操作自己做的一些小实验
- hdu 5950
- iview 爬坑 menu
- ubuntu install google chrome software
- 自动化运维平台----Django搭建之路
- 动态规划之二
- 朴素贝叶斯Naive Bayes-机器学习ML
- 抽象类与接口