java8以前HashMap存儲結構以下圖,將Map中的key進行哈希運算獲得hashCode,當出現hashCode相同但equals不一樣時稱此現象爲碰撞
,發生碰撞時會造成鏈表結構,取值時會遍歷整個鏈表結構效率較低。java
java8採用的HashMap存儲結構以下圖,當發生碰撞造成的鏈表上元素個數大於8時,總容量大於64會將鏈表轉換爲紅黑樹。此種狀況下除了添加元素慢一些,其他操做(查詢,刪除)均高於鏈表結構。算法
java8以前concurrentHashMap爲HashTable的組合達到線程安全的效果,默認併發級別爲16即concurrentHashMap由16個HashTable組成。java8採用CAS算法到達線程安全的效果,數據結構爲java8的HashMap結構。數據庫
注:hashMap起始默認容器大小爲16,當容器元素個數到達75%(擴容因子)開始擴容,擴容一倍大小從新計算位置。編程
歷史:以前不少公司生產的JVM早已沒有永久代,只是SUN的JVM尚未淘汰永久代數組
java8將永久代變爲元空間(MetaSpace)。以前永久代在JVM中分配,永久代基本不回收佔用JVM內存空間。java8廢棄永久代改成元空間,元空間在操做系統的內存上進行分配。安全
在函數式語言中,咱們只須要給函數分配變量,並將這個函數做爲參數傳遞給其它函數就可實現特定的功能。而java如前言中所述,不能直接將方法看成一個參數傳遞。同時匿名內部類又存在諸多不便:語法過於冗餘,匿名類中的this和變量名容易令人產生誤解,類型載入和實例建立語義不夠靈活,沒法捕獲非final的局部變量等。 Lambda 表達式的出現爲 Java 添加了缺失的函數式編程特色,使咱們能將函數當作一等公民看待。數據結構
Lambda 表達式在Java 語言中引入了一個新的語法元素和操做符。這個操做符爲->
,該操做符被稱爲Lambda 操做符或箭頭操做符。它將Lambda 分爲兩個部分: 左側:指定了Lambda 表達式須要的全部參數 右側:指定了Lambda 體,即Lambda 表達式要執行的功能併發
Lambda表達式實現的必須是函數式接口。app
只包含一個抽象方法的接口,稱爲函數式接口。能夠經過Lambda 表達式來建立該接口的對象。(若Lambda 表達式拋出一個受檢異常,那麼該異常須要在目標接口的抽象方法上進行聲明)。咱們能夠在任意函數式接口上使用@FunctionalInterface
註解,這樣作能夠檢查它是不是一個函數式接口,同時javadoc也會包含一條聲明,說明這個接口是一個函數式接口。框架
/** * 情景一:無參數,無返回值,一條語句 */ @Test public void test() throws Exception { Runnable r = () -> System.out.println("Hello Lambda"); r.run(); }
/** * 情景二:有一個參數,而且無返回值,一條語句 */ @Test public void test() throws Exception { Consumer<String> consumer = (x) -> System.out.println("Hello " + x); consumer.accept("Lambda"); }
/** * 情景三:有兩個以上參數,多條語句 */ @Test public void test() throws Exception { Comparator<Integer> comparator = (x, y) -> { System.out.println("多條語句"); return Integer.compare(x, y); }; }
注:
{}
和return
,通常狀況下省略。類型推斷
。若是要寫須要所有寫上類型。①消費型接口
@FunctionalInterface public interface Consumer<T> { void accept(T t); //鏈式調用,以後繼續調用消費型接口 default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; } }
測試實例--消費型接口
/** * 消費型接口Consumer<T> */ @Test public void test1 () { consumo(500, (x) -> System.out.println(x)); } public void consumo (double money, Consumer<Double> c) { c.accept(money); }
②供給型接口
@FunctionalInterface public interface Supplier<T> { T get(); }
測試實例-供給型接口
/** * 供給型接口,Supplier<T> */ @Test public void test2 () { Random ran = new Random(); List<Integer> list = supplier(10, () -> ran.nextInt(10)); for (Integer i : list) { System.out.println(i); } } /** * 隨機產生sum個數量得集合 * @param sum 集合內元素個數 * @param sup * @return */ public List<Integer> supplier(int sum, Supplier<Integer> sup){ List<Integer> list = new ArrayList<Integer>(); for (int i = 0; i < sum; i++) { list.add(sup.get()); } return list; }
③函數型接口
@FunctionalInterface public interface Function<T, R> { R apply(T t); //鏈式調用,在調用此方法以前調用傳入的函數式接口 default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } //鏈式調用,在調用此方法以後調用傳入的函數式接口 default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } //返回輸入的參數,Function.identity().apply("AAA")返回AAA static <T> Function<T, T> identity() { return t -> t; } }
函數型接口測試實例
/** * 函數型接口:Function<R, T> */ @Test public void test3 () { String s = strOperar(" asdf ", x -> x.substring(0, 2)); System.out.println(s); String s1 = strOperar(" asdf ", x -> x.trim()); System.out.println(s1); } /** * 字符串操做 * @param str 須要處理得字符串 * @param fun Function接口 * @return 處理以後得字符傳 */ public String strOperar(String str, Function<String, String> fun) { return fun.apply(str); }
④斷言型接口
@FunctionalInterface public interface Predicate<T> { boolean test(T t); //鏈式調用,須要同時知足條件 default Predicate<T> and(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) && other.test(t); } //取相反值 default Predicate<T> negate() { return (t) -> !test(t); } //鏈式調用,只須要知足一個條件便可 default Predicate<T> or(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) || other.test(t); } static <T> Predicate<T> isEqual(Object targetRef) { return (null == targetRef) ? Objects::isNull : object -> targetRef.equals(object); } }
斷言型接口測試實例
/** * 斷言型接口:Predicate<T> */ @Test public void test4 () { List<Integer> l = new ArrayList<>(); l.add(102); l.add(172); l.add(13); l.add(82); l.add(109); List<Integer> list = filterInt(l, x -> (x > 100)); for (Integer integer : list) { System.out.println(integer); } } /** * 過濾集合 * @param list * @param pre * @return */ public List<Integer> filterInt(List<Integer> list, Predicate<Integer> pre){ List<Integer> l = new ArrayList<>(); for (Integer integer : list) { if (pre.test(integer)) l.add(integer); } return l; }
除了上述得4種類型得接口外還有其餘的一些接口供咱們使用:
參數類型有2個,爲T,U,返回值爲R,其中方法爲R apply(T t, U u)
參數爲T,對參數爲T的對象進行一元操做,並返回T類型結果,其中方法爲T apply(T t)
當要傳遞給Lambda體的操做,已經有實現的方法了,可使用方法引用(實現抽象方法的參數列表和返回類型,必須與方法引用方法的參數列表和返回類型保持一致)
方法引用:使用操做符::
將方法名和對象或類的名字分隔開來。以下三種主要使用狀況:
對象::實例方法
@Test public void test() throws Exception { PrintStream ps = System.out; Consumer<String> consumer = ps::println; consumer.accept("Lambda"); }
類::靜態方法
@Test public void test() throws Exception { Comparator<Integer> comparator = Integer::compare; }
類::實例方法,第一個參數是此方法的調用者,第二個參數是此方法的參數時可使用ClassName::MethodName
@Test public void test() throws Exception { BiPredicate<String, String> predicate = String::equals; }
public void test() throws Exception { Supplier<Employee> supplier = Employee::new;//與函數式接口參數列表匹配調用對應的構造方法 supplier.get(); }
@Test public void test() throws Exception { //傳入的參數必須是Integer,返回是一個數組 Function<Integer, Employee[]> function = Employee[]::new;//new Employee[num] }
Stream 是Java8 中處理集合的關鍵抽象概念,它能夠指定你但願對集合進行的操做,能夠執行很是複雜的查找、過濾和映射數據等操做。使用Stream API 對集合數據進行操做,就相似於使用SQL 執行的數據庫查詢。也可使用Stream API 來並行執行操做。簡而言之,Stream API 提供了一種高效且易於使用的處理數據的方式。
流是數據渠道,用於操做數據源(集合、數組等)所生成的元素序列。「集合講的是數據,流講的是計算」
1、建立Stream
@Test public void test() throws Exception { //一、經過Collection系列集合提供的stream()或parallelStream()獲取 List<String> list = new ArrayList<String>(); Stream<String> stream1 = list.stream(); //二、經過Arrays中的靜態方法stream()獲取數組流 Employee[] employees = new Employee[10]; Stream<Employee> stream2 = Arrays.stream(employees); //三、經過Stream類中的靜態方法of() Stream<String> stream3 = Stream.of("AAA","BBB","CCC"); //四、迭代建立無限流 Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 1); //五、生成建立無限流 Stream<Double> stream5 = Stream.generate(Math::random); }
2、中間操做
篩選
/** * filter——接受Lambda,從流中排除某些元素 * limit——截斷流,使其元素不超過給定數量 * skip(n)——跳過元素,返回一個扔掉了前n個元素的流。若流中元素不足n個,則返回一個空流。與limit互補
*/ @Test public void test() throws Exception { emps.stream().filter((e) -> e.getSalary() > 5000); emps.stream().limit(5); emps.stream().skip(5); emps.stream().distinct(); //此操做會發生短路,取出工資大於5000的兩個其他再也不遍歷 emps.stream().filter((e) -> e.getSalary() > 5000).limit(2); } ```
映射
/** * map——接收Lambda,將元素轉換爲其餘形式或提取信息。接收一個函數做爲參數,該參數會被應用到每一個元素上,並將其映射成一個新元素
*/ @Test public void test() throws Exception { emps.stream().map(Employee::getName); //會將【【A】,【B】,【C】】轉換爲【A,B,C】 List<List<String>> lls = Arrays.asList(Arrays.asList("A"),Arrays.asList("B"),Arrays.asList("C")); lls.stream().flatMap((ls) -> ls.stream()); } ```
排序
/** * sorted():天然排序
*/ @Test public void test() throws Exception { List<String> arr = Arrays.asList("AAA","BBB","CCC"); arr.stream().sorted();//天然排序 emps.stream().sorted((x, y) -> x.getAge().compareTo(y.getAge()));//定製排序 } ```
3、終止操做(終端操做)
查找與匹配
/** * allMatch:檢查是否匹配全部元素 * anyMatch:檢查是否至少匹配一個元素 * noneMatch:檢查是否沒有匹配全部元素 * findFirst:返回第一個元素 * findAny:返回任意一個元素 * count:返回元素個數 * max:返回流中最大元素
*/ @Test public void test() throws Exception { boolean allMatch = emps.stream().allMatch((e) -> e.getAge() > 18); //employees是否年齡都大於18 boolean anyMatch = emps.stream().anyMatch((e) -> e.getSalary() > 6000); //employees是否存在工資大於6k boolean noneMatch = emps.stream().noneMatch((e) -> e.getName().equals("小明"));//employees是否沒有人叫小明 Optional<Employee> findFirst = emps.stream().findFirst(); //返回第一個元素 Optional<Employee> findAny = emps.parallelStream().findAny(); //返回任意一個元素,stream()會一直返回第一個 long count = emps.stream().count(); //返回元素個數 Optional<Employee> max = emps.stream().max((x, y) -> Integer.compare(x.getAge(),y.getAge())); //返回年齡最大的 Optional<Employee> min = emps.stream().min((x, y) -> Integer.compare(x.getAge(),y.getAge())); //返回年齡最小的 } ```
歸約與收集
/** * 歸約:reduce(T indetity, BinaryOperator) / reduce(BinaryOperator):能夠將流中的元素反覆結合起來獲得一個新值
*/ @Test public void testReduce() throws Exception { emps.stream().map(Employee::getSalary).reduce(0.0, Double::sum); //計算工資總和 } /** * 收集:collect - 將流轉換爲其餘形式,接收一個Collector接口實現,用於數據彙總 Collectors:工具類用於產生Collector實例 */ @Test public void testCollect() throws Exception { List<String> names = emps.stream().map(Employee::getName).collect(Collectors.toList()); // 轉換爲自定義數據類型 HashSet<Double> salarys = emps.stream().map(Employee::getSalary).collect(Collectors.toCollection(HashSet::new)); // 用收集器獲得總個數 long count = emps.stream().collect(Collectors.counting()); // 取平均值 Double avg = emps.stream().collect(Collectors.averagingDouble(Employee::getSalary)); // 取總和 Double sum = emps.stream().collect(Collectors.summingDouble(Employee::getSalary)); // 取最大值 Optional<Employee> max = emps.stream().collect(Collectors.maxBy( (x, y) -> Double.compare(x.getSalary(), y.getSalary()))); //拼接字符串 String joinNames = emps.stream().map(Employee::getName).collect(Collectors.joining(",")); // 經過summaryStatistics得到值 DoubleSummaryStatistics summaryStatistics = emps.stream().collect(Collectors.summarizingDouble(Employee::getSalary));// 取總和 summaryStatistics.getAverage(); summaryStatistics.getCount(); summaryStatistics.getMax(); summaryStatistics.getMin(); summaryStatistics.getSum(); //分組,也能夠進行多級分組 Map<Double, List<Employee>> groupSalary = emps.stream().collect(Collectors.groupingBy(Employee::getSalary)); //以工資分組 Map<String, List<Employee>> groupAge = emps.stream().collect(Collectors.groupingBy( (e) -> e.getAge() > 35 ? "中年" : "青年")); //以年齡分組 //分區是一種特殊的分組,結果 map至少包含兩個不一樣的分組一個true,一個false Map<Boolean, List<Employee>> partitionSalary = emps.stream().collect(Collectors.partitioningBy((e) -> e.getSalary() > 5000)); } ```
遍歷
@Test public void test() throws Exception { emps.stream().forEach(System.out::println); }
Fork/Join 框架:就是在必要的狀況下,將一個大任務,進行拆分(fork)成若干個小任務(拆到不可再拆時),再將一個個的小任務運算的結果進行join 彙總。
採用工做竊取
模式(work-stealing): 當執行新的任務時它能夠將其拆分分紅更小的任務執行,並將小任務加到線程隊列中,而後再從一個隨機線程的隊列中偷一個並把它放在本身的隊列中。相對於通常的線程池實現,fork/join框架的優點體如今對其中包含的任務的處理方式上。在通常的線程池中,若是一個線程正在執行的任務因爲某些緣由沒法繼續運行,那麼該線程會處於等待狀態。而在fork/join框架實現中,若是某個子問題因爲等待另一個子問題的完成而沒法繼續運行。那麼處理該子問題的線程會主動尋找其餘還沒有運行的子問題來執行。這種方式減小了線程的等待時間,提升了性能。
Fork/Join的好處
Fork/Join示例
public class ForkJoinCalculate extends RecursiveTask<Long> { private static final long serialVersionUID = 1L; private long start; private long end; private static final long THRESHOLD = 10000; public ForkJoinCalculate(long start, long end) { this.start = start; this.end = end; } @Override protected Long compute() { long length = end - start; if(length <= THRESHOLD) { long sum = 0; for (long i = start; i <= end; i++) { sum += i; } return sum; }else { long middle = (start + end) / 2; ForkJoinCalculate left = new ForkJoinCalculate(start,middle); left.fork(); ForkJoinCalculate right = new ForkJoinCalculate(middle + 1,end); right.fork(); return left.join() + right.join(); } } public static void main(String[] args) { ForkJoinPool pool = new ForkJoinPool(); ForkJoinCalculate calculate = new ForkJoinCalculate(1, 1000000000L); Long result = pool.invoke(calculate); System.out.println(result); } }
底層依舊使用的Fork/Join框架,使用的公共的ForkJoinPool,大大簡化了Fork/Join框架的使用難度
@Test public void test() throws Exception { OptionalLong result = LongStream.rangeClosed(1, 10000000L).parallel().reduce(Long::sum); System.out.println(result.getAsLong()); }
Optional 類(java.util.Optional) 是一個容器類,表明一個值存在或不存在,原來用null 表示一個值不存在,如今Optional 能夠更好的表達這個概念。而且能夠避免空指針異常。
經常使用方法:
Java 8中容許接口中包含具備具體實現的方法,該方法稱爲默認方法
,使用 ==default==關鍵字修飾。Java 8中容許接口中定義和實現靜態方法。
接口默認方法的」類優先」原則
若一個接口中定義了一個默認方法,而另一個父類或接口中又定義了一個同名的方法時選擇父類中的方法。若是一個父類提供了具體的實現,那麼接口中具備相同名稱和參數的默認方法會被忽略。
接口衝突
若是一個父接口提供一個默認方法,而另外一個接口也提供了一個具備相同名稱和參數列表的方法(無論方法是不是默認方法),那麼必須覆蓋該方法來解決衝突。
interface Foo { default String getFoo() { // 默認方法 return "foo"; } static String showFoo() { // 靜態方法 System.out.println("foo"); } }
在可重複註解上使用@Repeatable
標註且提供容器類
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Repeatable(MyAnnotations.class) public @interface MyAnnotation { String value(); }
容器類必須提供可重複註解[] value()
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface MyAnnotations { MyAnnotation[] value(); }
ElementType.TYPE_PARAMETER(Type parameter declaration) 用來標註類型參數
@Target(ElementType.TYPE_PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface TypeParameterAnnotation { } // 以下是該註解的使用例子 public class TypeParameterClass<@TypeParameterAnnotation T> { public <@TypeParameterAnnotation U> T foo(T t) { return null; } }
ElementType.TYPE_USE(Use of a type) 能標註任何類型名稱
public class TestTypeUse { @Target(ElementType.TYPE_USE) @Retention(RetentionPolicy.RUNTIME) public @interface TypeUseAnnotation { } public static @TypeUseAnnotation class TypeUseClass<@TypeUseAnnotation T> extends @TypeUseAnnotation Object { public void foo(@TypeUseAnnotation T t) throws @TypeUseAnnotation Exception { } } // 以下註解的使用都是合法的 @SuppressWarnings({ "rawtypes", "unused", "resource" }) public static void main(String[] args) throws Exception { TypeUseClass<@TypeUseAnnotation String> typeUseClass = new @TypeUseAnnotation TypeUseClass<>(); typeUseClass.foo(""); List<@TypeUseAnnotation Comparable> list1 = new ArrayList<>(); List<? extends Comparable> list2 = new ArrayList<@TypeUseAnnotation Comparable>(); @TypeUseAnnotation String text = (@TypeUseAnnotation String)new Object(); java.util. @TypeUseAnnotation Scanner console = new java.util.@TypeUseAnnotation Scanner(System.in); } }
java8提供的日期時間均是線程安全的,原始的Data、DateFormat、Calendar等均線程不安全