Collections.sort(words, new Comparator<String>() { public int compare(String s1, String s2) { return Integer.compare(s1.length(), s2.length()); } });
上述使用了策略模式,Comparator接口爲排序的抽象策略,匿名內部類爲具體實現策略,可是匿名內部類的實現過於冗長。html
在java8中,若是一個接口只有一個方法,那麼這個接口能夠看做一個函數接口,功能接口的實現類能夠經過lambda來實現,lambda與匿名內部類相似,可是更加簡潔。java
lamdba:常規正則表達式
Collections.sort(words,(s1, s2) -> Integer.compare(s1.length(), s2.length()));
參數爲String類型,返回值爲int類型,編譯器是如何知道的呢?數組
編譯器使用稱爲類型推斷的過程從上下文中推導出這些類型,可是編譯器不是萬能的,有時候仍然須要顯式設定。安全
lamdba:方法引用數據結構
Collections.sort(words, comparingInt(String::length)); words.sort(comparingInt(String::length));
public enum Operation { PLUS("+") { public double apply(double x, double y) { return x + y; } }, MINUS("-") { public double apply(double x, double y) { return x - y; } }, TIMES("*") { public double apply(double x, double y) { return x * y; } }, DIVIDE("/") { public double apply(double x, double y) { return x / y; } }; private final String symbol; Operation(String symbol) { this.symbol = symbol; } @Override public String toString() { return symbol; } public abstract double apply(double x, double y); }
public enum Operation { PLUS ("+", (x, y) -> x + y), MINUS ("-", (x, y) -> x - y), TIMES ("*", (x, y) -> x * y), DIVIDE("/", (x, y) -> x / y); private final String symbol; private final DoubleBinaryOperator op; Operation(String symbol, DoubleBinaryOperator op) { this.symbol = symbol; this.op = op; } @Override public String toString() { return symbol; } public double apply(double x, double y) { return op.applyAsDouble(x, y); } }
// lambda代碼塊 map.merge(key, 1, (count, incr) -> count + incr); // 方法引用 map.merge(key, 1, Integer::sum);
方法引用類型 | 例子 | Lambda等效方案 |
---|---|---|
Static | Integer::parseInt | str -> Integer.parseInt(str) |
Bound | Instant.now()::isAfter | Instant then = Instant.now();<br/>t -> then.isAfter(t) |
Unbound | String::toLowerCase | str -> str.toLowerCase() |
Class Constructor | TreeMap::new | () -> new TreeMap |
Array Constructor | int[]::new | len -> new int[len] |
// 模板方法 abstract class A { public void print() { System.out.println("A"); doSubThing(); } abstract void doSubThing(); } class B extends A { @Override void doSubThing() { System.out.println("B"); } } // lambda class A { private Supplier<String> supplier; public A(Supplier<String> supplier) { this.supplier = supplier; } public void print() { System.out.println("A"); System.out.println(supplier.get()); } } public static void main(String[] args) { A a = new A(() -> "B"); a.print(); }
接口 | 函數簽名 | 例子 |
---|---|---|
UnaryOperator<T> | T apply(T t) | String::toLowerCase |
BinaryOperator<T> | T apply(T t1, T t2) | BigInteger::add |
Predicate<T> | boolean test(T t) | Collection::isEmpty |
Function<T> | R apply(T t) | Arrays::asList |
Supplier<T> | T get() | Instant::now |
Consumer<T> | void accept(T t) | System.out::println |
具體請參考:JAVA8的java.util.function包併發
流:無限或有限的數據元素序列。app
管道:對流中的元素進行多級計算。ide
流的源:集合、數組、文件、正則表達式或模式匹配器、僞隨機數生成器或其餘流。函數
管道操做:由源流後跟着零個或多箇中間操做和一個終止操做。
中間操做:某種轉換流的方式,如:元素映射或元素過濾等。
終止操做:執行最終計算,如:流裝入容器中或是消費掉。
// 普通方式 // 讀取文件中的單詞,檢查單詞的字母,相同字母的單詞收集在一塊兒 public class Anagrams { public static void main(String[] args) throws IOException { File dictionary = new File(args[0]); int minGroupSize = Integer.parseInt(args[1]); Map<String, Set<String>> groups = new HashMap<>(); try (Scanner s = new Scanner(dictionary)) { while (s.hasNext()) { String word = s.next(); groups.computeIfAbsent(alphabetize(word), (unused) -> new TreeSet<>()).add(word); } } for (Set<String> group : groups.values()) if (group.size() >= minGroupSize) System.out.println(group.size() + ": " + group); } private static String alphabetize(String s) { char[] a = s.toCharArray(); Arrays.sort(a); return new String(a); } } // 過分使用流:雖然很簡潔,可是對流不瞭解的開發人員可能沒法理解。 // 打個比方,有些動漫是隻有死宅纔看的:永生之酒。 public static void main(String[] args) throws IOException { Path dictionary = Paths.get(args[0]); int minGroupSize = Integer.parseInt(args[1]); try (Stream<String> words = Files.lines(dictionary)) { words.collect( groupingBy(word -> word.chars().sorted() .collect(StringBuilder::new, (sb, c) -> sb.append((char) c), StringBuilder::append).toString()) ) .values().stream() .filter(group -> group.size() >= minGroupSize) .map(group -> group.size() + ": " + group) .forEach(System.out::println); } } // 合適使用流方式 // 有的動漫是你們都看的:龍珠。對動漫不須要太瞭解也可以接收。 public static void main(String[] args) throws IOException { Path dictionary = Paths.get(args[0]); int minGroupSize = Integer.parseInt(args[1]); try (Stream<String> words = Files.lines(dictionary)) { words.collect(groupingBy(word -> alphabetize(word))) .values().stream() .filter(group -> group.size() >= minGroupSize) .forEach(group -> System.out.println(group.size() + ": " + group)); } }
public static void main(String[] args) { primes().map(p -> TWO.pow(p.intValueExact()).subtract(ONE)) // (1-1/50)=98%表明isProbablePrime只有當98%概率爲素數才返回true。 .filter(mersenne -> mersenne.isProbablePrime(50)) .limit(20) // mp.bitLength等於p值,反向運算來獲取上一個流的值。 .forEach(mp -> System.out.println(mp.bitLength() + ": " + mp)); } static Stream<BigInteger> primes() { return Stream.iterate(TWO, BigInteger::nextProbablePrime); }
private static List<Card> newDeck() { List<Card> result = new ArrayList<>(); for (Suit suit : Suit.values()) for (Rank rank : Rank.values()) result.add(new Card(suit, rank)); return result; } // flatMap 用於展平一個序列,如:List<String> -> String. private static List<Card> newDeck() { return Stream.of(Suit.values()) .flatMap(suit -> Stream.of(Rank.values()) .map(rank -> new Card(suit, rank))) .collect(toList()); }
名稱 | 做用 |
---|---|
toCollection toList toSet | 流轉換爲集合 |
toMap | 流轉換爲Map |
partitioningBy groupingBy groupingByConcurrent | 分組 |
minBy maxBy | 最值 |
counting | 計數 |
summingInt averagingInt | 和 平均值 |
joining mapping | 合併 映射 |
// 不遵照範式,forEach應該只用於呈現流執行的計算結果 Map<String, Long> freq = new HashMap<>(); try (Stream<String> words = new Scanner(file).tokens()) { words.forEach(word -> freq.merge(word.toLowerCase(), 1L, Long::sum)); } // 正確地使用流來初始化頻率表 Map<String, Long> freq; try (Stream<String> words = new Scanner(file).tokens()) { freq = words.collect(groupingBy(String::toLowerCase, counting())); } // 按照頻次獲取前十個元素 List<String> topTen = freq.keySet().stream() .sorted(comparing(freq::get).reversed()) .limit(10) .collect(toList()); // groupingByConcurrent返回併發Map ConcurrentHashMap<String, Long> freq; try (Stream<String> words = new Scanner(file).tokens()) { freq = words.collect(groupingByConcurrent(String::toLowerCase, counting())); }
List<String> words = new ArrayList<>(); words.add("1"); words.add("1"); words.add("2"); words.add("3"); Map<Boolean, List<String>> map = words.stream().collect( partitioningBy(s -> s.equals("1")) ); System.out.println(map); // {false=[2, 3], true=[1, 1]} Map<String, List<String>> map = words.stream().collect( groupingBy(String::toLowerCase) ); System.out.println(map); // {1=[1, 1], 2=[2], 3=[3]}
// List轉Map的正確實現 Map<Integer, Data> collect = words.stream().collect(toMap(Data::getId, e -> e)); // key值重複時,獲取銷量最大的Album Map<Artist, Album> topHits = albums.collect( toMap(Album::artist, a->a,maxBy(comparing(Album::sales))) ); // 後訪問的覆蓋先訪問的 Map<Artist, Album> topHits = albums.collect( toMap(Album::artist, a->a,(v1, v2) -> v2) ); // 指定返回Map的類型 HashMap<Artist, Album> topHits = albums.collect( toMap(Album::artist, a->a,(v1, v2) -> v2,HashMap::new) );
// joining List<String> words = new ArrayList<>(); words.add("2"); words.add("1"); words.add("1"); words.add("3"); String join1 = words.stream().collect(joining()); String join2 = words.stream().collect(joining(",")); String join3 = words.stream().collect(joining(",","[","]")); System.out.println(join1); // 2113 System.out.println(join2); // 2,1,1,3 System.out.println(join3); //[2,1,1,3] // mapping和map相似 List<String> words = new ArrayList<>(); words.add("2"); words.add("1"); words.add("1"); words.add("3"); List<Integer> list1 = words.stream().collect(mapping(e -> Integer.valueOf(e), toList())); List<Integer> list2 = words.stream().map(e -> Integer.valueOf(e)).collect(toList()); System.out.println(list1); // [2, 1, 1, 3] System.out.println(list2); // [2, 1, 1, 3]
List<String> words = new ArrayList<>(); words.add("2"); words.add("1"); words.add("3"); // 求和 Integer sum1 = words.stream().collect(summingInt(value -> Integer.valueOf(value))); Integer sum2 = words.stream().mapToInt(value -> Integer.valueOf(value)).sum(); // 平均值 Double avg = words.stream().collect(averagingInt(value -> Integer.valueOf(value))); // 最大值 String max1 = words.stream().max(comparing(Integer::valueOf)).get(); String max2 = words.stream().collect(maxBy(comparing(Integer::valueOf))).get(); // 總結值 IntSummaryStatistics summary = words.stream().collect(summarizingInt(Integer::valueOf)); System.out.println(summary.getAverage()); System.out.println(summary.getSum()); System.out.println(summary.getCount()); System.out.println(summary.getMax()); System.out.println(summary.getMin());
// need to cast for (ProcessHandle ph : (Iterable<ProcessHandle>)ProcessHandle.allProcesses()::iterator){ ... } // Adapter from Stream<E> to Iterable<E> public static <E> Iterable<E> iterableOf(Stream<E> stream) { return stream::iterator; } for (ProcessHandle p : iterableOf(ProcessHandle.allProcesses())) { // Process the process }
// spliterator用於並行迭代 public static <E> Stream<E> streamOf(Iterable<E> iterable) { return StreamSupport.stream(iterable.spliterator(), false); } // 例子:並行計算1+2+...+10000 public static void main(String[] args) throws InterruptedException { List<String> words = new ArrayList<>(); for (int i = 1; i <= 10000; i++) { words.add(i + ""); } final AtomicInteger atomicInteger = new AtomicInteger(0); int count = 10; CountDownLatch latch = new CountDownLatch(count); final List<Spliterator<String>> splitList = split(words, count); for (int i = 0; i < count; i++) { int finalI = i; new Thread(() -> { try { splitList.get(finalI) .forEachRemaining(s -> atomicInteger.getAndAdd(Integer.valueOf(s))); } finally { latch.countDown(); } }, "Thread:" + i).start(); } latch.await(); System.out.println(atomicInteger.get()); } public static <T> List<Spliterator<T>> split(List<T> list, int size) { List<Spliterator<T>> returnList = new ArrayList<>(); returnList.add(list.spliterator()); if (size > 1) spliterator(returnList, 2, size); return returnList; } private static <T> void spliterator(List<Spliterator<T>> returnList, int i, int size) { int j = i / 2 - 1; returnList.add(returnList.get(j).trySplit()); if (size == i) return; spliterator(returnList, i + 1, size); }
// {a,b,c}的冪集爲{{},{a},{b},{c},{a,b},{a,c},{b,c},{a,b ,c}} public class PowerSet { public static final <E> Collection<Set<E>> of(Set<E> s) { List<E> src = new ArrayList<>(s); if (src.size() > 30) throw new IllegalArgumentException("Set too big " + s); return new AbstractList<Set<E>>() { @Override public int size() { // 若是size > 31將致使溢出int的範圍 return 1 << src.size(); // 2^size } @Override public boolean contains(Object o) { return o instanceof Set && src.containsAll((Set)o); } @Override public Set<E> get(int index) { Set<E> result = new HashSet<>(); for (int i = 0; index != 0; i++, index >>= 1) if ((index & 1) == 1) result.add(src.get(i)); return result; } }; } }
public class SubLists { public static <E> Stream<List<E>> of(List<E> list) { return Stream.concat(Stream.of(Collections.emptyList()), prefixes(list).flatMap(SubLists::suffixes)); } // (a,b,c) => ((a),(a,b),(a,b,c)) private static <E> Stream<List<E>> prefixes(List<E> list) { return IntStream.rangeClosed(1, list.size()) .mapToObj(end -> list.subList(0, end)); } // (a,b,c) => ((a,b,c),(b,c),(c)) private static <E> Stream<List<E>> suffixes(List<E> list) { return IntStream.range(0, list.size()) .mapToObj(start -> list.subList(start, list.size())); } }
// [1,3,2] => [[1], [1, 3], [1, 3, 2], [3], [3, 2], [2]] public static <E> Stream<List<E>> of(List<E> list) { return IntStream.range(0, list.size()) .mapToObj(start -> IntStream.rangeClosed(start + 1, list.size()) .mapToObj(end -> list.subList(start, end)))// subList使用閉區間 .flatMap(x -> x); }
ArrayList、HashMap、HsahSet、CouncurrentHashMap、數組、int範圍流和long範圍流的並行性性能效益最佳。
它們的範圍能夠肯定,而執行任務的抽象爲spliterator。
數組存儲的元素在內存中相近,數據定位更快。而上面涉及的數據結構基本都基於數組實現。
流的終止操做會影響並行執行的有效性。而流的reduce操做或預先打包(min、max、count和sum)是並行流的最佳實踐。
流的中間操做(anyMatch、allMatch和noneMatch)也適合並行操做。
流的collect操做則不適合。
本身實現Stream、Iterable或Collection且但願有良好的並行性能,則須要覆蓋spliterator方法。
並行流是基於fork-join池實現的。
當沒法寫出正確的並行流,將致使異常或者錯誤的數據。
注:程序的安全性、正確性比性能更重要。
// 串行,10^8須要30秒 static long pi(long n) { return LongStream.rangeClosed(2, n) .mapToObj(BigInteger::valueOf) .filter(i -> i.isProbablePrime(50)) .count(); } // 並行,10^8須要9秒 static long pi(long n) { return LongStream.rangeClosed(2, n) .parallel() .mapToObj(BigInteger::valueOf) .filter(i -> i.isProbablePrime(50)) .count(); }