整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          2022年java面試基礎篇圈重點,錯過拍大腿系列

          2022年java面試基礎篇圈重點,錯過拍大腿系列

          礎篇要點:算法、數據結構、基礎設計模式

          1. 二分查找

          • 能夠用自己語言描述二分查找算法
          • 能夠手寫二分查找代碼
          • 能夠解答一些變化后的考法

          算法描述

          1. 前提:有已排序數組 A(假設已經做好)
          2. 定義左邊界 L、右邊界 R,確定搜索范圍,循環執行二分查找(3、4兩步)
          3. 獲取中間索引 M=Floor((L+R) /2)
          4. 中間索引的值 A[M] 與待搜索的值 T 進行比較 ① A[M]==T 表示找到,返回中間索引 ② A[M] > T,中間值右側的其它元素都大于 T,無需比較,中間索引左邊去找,M - 1 設置為右邊界,重新查找 ③ A[M] < T,中間值左側的其它元素都小于 T,無需比較,中間索引右邊去找, M + 1 設置為左邊界,重新查找
          5. 當 L > R 時,表示沒有找到,應結束循環

          更形象的描述請參考:binary_search.html

          算法實現

          public static int binarySearch(int[] a, int t) {
              int l=0, r=a.length - 1, m;
              while (l <=r) {
                  m=(l + r) / 2;
                  if (a[m]==t) {
                      return m;
                  } else if (a[m] > t) {
                      r=m - 1;
                  } else {
                      l=m + 1;
                  }
              }
              return -1;
          }

          測試代碼

          public static void main(String[] args) {
              int[] array={1, 5, 8, 11, 19, 22, 31, 35, 40, 45, 48, 49, 50};
              int target=47;
              int idx=binarySearch(array, target);
              System.out.println(idx);
          }

          解決整數溢出問題

          當 l 和 r 都較大時,l + r 有可能超過整數范圍,造成運算錯誤,解決方法有兩種:

          int m=l + (r - l) / 2;

          還有一種是:

          int m=(l + r) >>> 1;

          其它考法

          1. 有一個有序表為 1,5,8,11,19,22,31,35,40,45,48,49,50 當二分查找值為 48 的結點時,查找成功需要比較的次數
          2. 使用二分法在序列 1,4,6,7,15,33,39,50,64,78,75,81,89,96 中查找元素 81 時,需要經過( )次比較
          3. 在擁有128個元素的數組中二分查找一個數,需要比較的次數最多不超過多少次

          對于前兩個題目,記得一個簡要判斷口訣: 奇數二分取中間,偶數二分取中間靠左。對于后一道題目,需要知道公式:

          n=log_2N=log_{10}N/log_{10}2n=log2N=log10?N/log10?2

          其中 n 為查找次數,N 為元素個數

          2. 冒泡排序

          • 能夠用自己語言描述冒泡排序算法
          • 能夠手寫冒泡排序代碼
          • 了解一些冒泡排序的優化手段

          算法描述

          1. 依次比較數組中相鄰兩個元素大小,若 a[j] > a[j+1],則交換兩個元素,兩兩都比較一遍稱為一輪冒泡,結果是讓最大的元素排至最后
          2. 重復以上步驟,直到整個數組有序

          更形象的描述請參考:bubble_sort.html

          算法實現

          public static void bubble(int[] a) {
              for (int j=0; j < a.length - 1; j++) {
                  // 一輪冒泡
                  boolean swapped=false; // 是否發生了交換
                  for (int i=0; i < a.length - 1 - j; i++) {
                      System.out.println("比較次數" + i);
                      if (a[i] > a[i + 1]) {
                          Utils.swap(a, i, i + 1);
                          swapped=true;
                      }
                  }
                  System.out.println("第" + j + "輪冒泡"
                                     + Arrays.toString(a));
                  if (!swapped) {
                      break;
                  }
              }
          }
          • 優化點1:每經過一輪冒泡,內層循環就可以減少一次
          • 優化點2:如果某一輪冒泡沒有發生交換,則表示所有數據有序,可以結束外層循環


          進一步優化

          public static void bubble_v2(int[] a) {
              int n=a.length - 1;
              while (true) {
                  int last=0; // 表示最后一次交換索引位置
                  for (int i=0; i < n; i++) {
                      System.out.println("比較次數" + i);
                      if (a[i] > a[i + 1]) {
                          Utils.swap(a, i, i + 1);
                          last=i;
                      }
                  }
                  n=last;
                  System.out.println("第輪冒泡"
                                     + Arrays.toString(a));
                  if (n==0) {
                      break;
                  }
              }
          }
          • 每輪冒泡時,最后一次交換索引可以作為下一輪冒泡的比較次數,如果這個值為零,表示整個數組有序,直接退出外層循環即可


          3. 選擇排序

          • 選擇排序_演示 03:46
          • 選擇排序_實現 05:44
          • 選擇排序_vs_冒泡排序
          • 能夠用自己語言描述選擇排序算法
          • 能夠比較選擇排序與冒泡排序
          • 理解非穩定排序與穩定排序

          算法描述

          1. 將數組分為兩個子集,排序的和未排序的,每一輪從未排序的子集中選出最小的元素,放入排序子集
          2. 重復以上步驟,直到整個數組有序

          更形象的描述請參考:selection_sort.html

          算法實現

          public static void selection(int[] a) {
              for (int i=0; i < a.length - 1; i++) {
                  // i 代表每輪選擇最小元素要交換到的目標索引
                  int s=i; // 代表最小元素的索引
                  for (int j=s + 1; j < a.length; j++) {
                      if (a[s] > a[j]) { // j 元素比 s 元素還要小, 更新 s
                          s=j;
                      }
                  }
                  if (s !=i) {
                      swap(a, s, i);
                  }
                  System.out.println(Arrays.toString(a));
              }
          }
          • 優化點:為減少交換次數,每一輪可以先找最小的索引,在每輪最后再交換元素


          與冒泡排序比較

          1. 二者平均時間復雜度都是 O(n^2)O(n2)
          2. 選擇排序一般要快于冒泡,因為其交換次數少
          3. 但如果集合有序度高,冒泡優于選擇
          4. 冒泡屬于穩定排序算法,而選擇屬于不穩定排序
          • 穩定排序指,按對象中不同字段進行多次排序,不會打亂同值元素的順序
          • 不穩定排序則反之



          穩定排序與不穩定排序

          System.out.println("=================不穩定================");
          Card[] cards=getStaticCards();
          System.out.println(Arrays.toString(cards));
          selection(cards, Comparator.comparingInt((Card a) -> a.sharpOrder).reversed());
          System.out.println(Arrays.toString(cards));
          selection(cards, Comparator.comparingInt((Card a) -> a.numberOrder).reversed());
          System.out.println(Arrays.toString(cards));
          
          System.out.println("=================穩定=================");
          cards=getStaticCards();
          System.out.println(Arrays.toString(cards));
          bubble(cards, Comparator.comparingInt((Card a) -> a.sharpOrder).reversed());
          System.out.println(Arrays.toString(cards));
          bubble(cards, Comparator.comparingInt((Card a) -> a.numberOrder).reversed());
          System.out.println(Arrays.toString(cards));

          都是先按照花色排序(????),再按照數字排序(AKQJ…)

          • 不穩定排序算法按數字排序時,會打亂原本同值的花色順序
          [[?7], [?2], [?4], [?5], [?2], [?5]] 
          [[?7], [?5], [?5], [?4], [?2], [?2]]

          原來 ?2 在前 ?2 在后,按數字再排后,他倆的位置變了

          • 穩定排序算法按數字排序時,會保留原本同值的花色順序,如下所示 ?2 與 ?2 的相對位置不變
          [[?7], [?2], [?4], [?5], [?2], [?5]] 
          [[?7], [?5], [?5], [?4], [?2], [?2]]

          4. 插入排序

          • 能夠用自己語言描述插入排序算法
          • 能夠比較插入排序與選擇排序

          算法描述

          1. 將數組分為兩個區域,排序區域和未排序區域,每一輪從未排序區域中取出第一個元素,插入到排序區域(需保證順序)
          2. 重復以上步驟,直到整個數組有序

          更形象的描述請參考:insertion_sort.html

          算法實現

          // 修改了代碼與希爾排序一致
          public static void insert(int[] a) {
              // i 代表待插入元素的索引
              for (int i=1; i < a.length; i++) {
                  int t=a[i]; // 代表待插入的元素值
                  int j=i;
                  System.out.println(j);
                  while (j >=1) {
                      if (t < a[j - 1]) { // j-1 是上一個元素索引,如果 > t,后移
                          a[j]=a[j - 1];
                          j--;
                      } else { // 如果 j-1 已經 <=t, 則 j 就是插入位置
                          break;
                      }
                  }
                  a[j]=t;
                  System.out.println(Arrays.toString(a) + " " + j);
              }
          }

          與選擇排序比較

          1. 二者平均時間復雜度都是 O(n^2)O(n2)
          2. 大部分情況下,插入都略優于選擇
          3. 有序集合插入的時間復雜度為 O(n)O(n)
          4. 插入屬于穩定排序算法,而選擇屬于不穩定排序

          提示

          插入排序通常被所輕視,其實它的地位非常重要。小數據量排序,都會優先選擇插入排序

          5. 希爾排序

          • 能夠用自己語言描述希爾排序算法

          算法描述


          1. 首先選取一個間隙序列,如 (n/2,n/4 … 1),n 為數組長度
          2. 每一輪將間隙相等的元素視為一組,對組內元素進行插入排序,目的有二 ① 少量元素插入排序速度很快 ② 讓組內值較大的元素更快地移動到后方
          3. 當間隙逐漸減少,直至為 1 時,即可完成排序

          更形象的描述請參考:shell_sort.html

          算法實現

          private static void shell(int[] a) {
              int n=a.length;
              for (int gap=n / 2; gap > 0; gap /=2) {
                  // i 代表待插入元素的索引
                  for (int i=gap; i < n; i++) {
                      int t=a[i]; // 代表待插入的元素值
                      int j=i;
                      while (j >=gap) {
                          // 每次與上一個間隙為 gap 的元素進行插入排序
                          if (t < a[j - gap]) { // j-gap 是上一個元素索引,如果 > t,后移
                              a[j]=a[j - gap];
                              j -=gap;
                          } else { // 如果 j-1 已經 <=t, 則 j 就是插入位置
                              break;
                          }
                      }
                      a[j]=t;
                      System.out.println(Arrays.toString(a) + " gap:" + gap);
                  }
              }
          }

          參考資料

          https://en.wikipedia.org/wiki/Shellsort


          6. 快速排序

          • 能夠用自己語言描述快速排序算法
          • 掌握手寫單邊循環、雙邊循環代碼之一
          • 能夠說明快排特點
          • 了解洛穆托與霍爾兩種分區方案的性能比較

          算法描述

          1. 每一輪排序選擇一個基準點(pivot)進行分區
          2. 讓小于基準點的元素的進入一個分區,大于基準點的元素的進入另一個分區
          3. 當分區完成時,基準點元素的位置就是其最終位置

          2.在子分區內重復以上過程,直至子分區元素個數少于等于 1,這體現的是分而治之的思想 (divide-and-conquer) 3.從以上描述可以看出,一個關鍵在于分區算法,常見的有洛穆托分區方案、雙邊循環分區方案、霍爾分區方案

          更形象的描述請參考:quick_sort.html

          單邊循環快排(lomuto 洛穆托分區方案)

          1. 選擇最右元素作為基準點元素
          2. j 指針負責找到比基準點小的元素,一旦找到則與 i 進行交換
          3. i 指針維護小于基準點元素的邊界,也是每次交換的目標索引
          4. 最后基準點與 i 交換,i 即為分區位置
          public static void quick(int[] a, int l, int h) {
              if (l >=h) {
                  return;
              }
              int p=partition(a, l, h); // p 索引值
              quick(a, l, p - 1); // 左邊分區的范圍確定
              quick(a, p + 1, h); // 左邊分區的范圍確定
          }
          
          private static int partition(int[] a, int l, int h) {
              int pv=a[h]; // 基準點元素
              int i=l;
              for (int j=l; j < h; j++) {
                  if (a[j] < pv) {
                      if (i !=j) {
                          swap(a, i, j);
                      }
                      i++;
                  }
              }
              if (i !=h) {
                  swap(a, h, i);
              }
              System.out.println(Arrays.toString(a) + " i=" + i);
              // 返回值代表了基準點元素所在的正確索引,用它確定下一輪分區的邊界
              return i;
          }


          雙邊循環快排(不完全等價于 hoare 霍爾分區方案)

          1. 選擇最左元素作為基準點元素
          2. j 指針負責從右向左找比基準點小的元素,i 指針負責從左向右找比基準點大的元素,一旦找到二者交換,直至 i,j 相交
          3. 最后基準點與 i(此時 i 與 j 相等)交換,i 即為分區位置

          要點

          基準點在左邊,并且要先 j 后 i

          while( i < j && a[j] > pv ) j–

          while ( i < j && a[i] <=pv ) i++

          private static void quick(int[] a, int l, int h) {
              if (l >=h) {
                  return;
              }
              int p=partition(a, l, h);
              quick(a, l, p - 1);
              quick(a, p + 1, h);
          }
          
          private static int partition(int[] a, int l, int h) {
              int pv=a[l];
              int i=l;
              int j=h;
              while (i < j) {
                  // j 從右找小的
                  while (i < j && a[j] > pv) {
                      j--;
                  }
                  // i 從左找大的
                  while (i < j && a[i] <=pv) {
                      i++;
                  }
                  swap(a, i, j);
              }
              swap(a, l, j);
              System.out.println(Arrays.toString(a) + " j=" + j);
              return j;
          }


          快排特點

          1. 平均時間復雜度是 O(nlog_2?n )O(nlog2??n),最壞時間復雜度 O(n^2)O(n2)
          2. 數據量較大時,優勢非常明顯
          3. 屬于不穩定排序

          洛穆托分區方案 vs 霍爾分區方案


          • 霍爾的移動次數平均來講比洛穆托少3倍
          • https://qastack.cn/cs/11458/quicksort-partitioning-hoare-vs-lomuto


          補充代碼說明

          • day01.sort.QuickSort3 演示了空穴法改進的雙邊快排,比較次數更少
          • day01.sort.QuickSortHoare 演示了霍爾分區的實現
          • day01.sort.LomutoVsHoare 對四種分區實現的移動次數比較


          7. ArrayList

          • 掌握 ArrayList 擴容規則

          擴容規則

          1. ArrayList() 會使用長度為零的數組
          2. ArrayList(int initialCapacity) 會使用指定容量的數組
          3. public ArrayList(Collection<? extends E> c) 會使用 c 的大小作為數組容量
          4. add(Object o) 首次擴容為 10,再次擴容為上次容量的 1.5 倍
          5. addAll(Collection c) 沒有元素時,擴容為 Math.max(10, 實際元素個數),有元素時為 Math.max(原容量 1.5 倍, 實際元素個數)

          其中第 4 點必須知道,其它幾點視個人情況而定

          提示

          • 測試代碼見 day01.list.TestArrayList ,這里不再列出
          • 注意的是,示例中用反射方式來更直觀地反映 ArrayList 的擴容特征,但從 JDK 9 由于模塊化的影響,對反射做了較多限制,需要在運行測試代碼時添加 VM 參數 --add-opens java.base/java.util=ALL-UNNAMED 方能運行通過,后面的例子都有相同問題

          代碼說明

          day01.list.TestArrayList#arrayListGrowRule 演示了 add(Object) 方法的擴容規則,輸入參數 n 代表打印多少次擴容后的數組長度

          8. Iterator

          • 掌握什么是 Fail-Fast、什么是 Fail-Safe

          Fail-Fast 與 Fail-Safe

          • ArrayList 是 fail-fast 的典型代表,遍歷的同時不能修改,盡快失敗
          • CopyOnWriteArrayList 是 fail-safe 的典型代表,遍歷的同時可以修改,原理是讀寫分離


          提示

          • 測試代碼見 day01.list.FailFastVsFailSafe,這里不再列出


          9. LinkedList

          • LinkedList_vs_ArrayList_隨機訪問性能比較 07:49
          • LinkedList_vs_ArrayList_增刪性能比較 08:55
          • LinkedList_vs_ArrayList_局部性原理_空間占用
          • 能夠說清楚 LinkedList 對比 ArrayList 的區別,并重視糾正部分錯誤的認知

          LinkedList

          1. 基于雙向鏈表,無需連續內存
          2. 隨機訪問慢(要沿著鏈表遍歷)
          3. 頭尾插入刪除性能高
          4. 占用內存多

          ArrayList

          1. 基于數組,需要連續內存
          2. 隨機訪問快(指根據下標訪問)
          3. 尾部插入、刪除性能可以,其它部分插入、刪除都會移動數據,因此性能會低
          4. 可以利用 cpu 緩存,局部性原理


          代碼說明

          day01.list.ArrayListVsLinkedList#randomAccess 對比隨機訪問性能

          day01.list.ArrayListVsLinkedList#addMiddle 對比向中間插入性能

          day01.list.ArrayListVsLinkedList#addFirst 對比頭部插入性能

          day01.list.ArrayListVsLinkedList#addLast 對比尾部插入性能

          day01.list.ArrayListVsLinkedList#linkedListSize 打印一個 LinkedList 占用內存

          day01.list.ArrayListVsLinkedList#arrayListSize 打印一個 ArrayList 占用內存

          10. HashMap

          • 掌握 HashMap 的基本數據結構
          • 掌握樹化
          • 理解索引計算方法、二次 hash 的意義、容量對索引計算的影響
          • 掌握 put 流程、擴容、擴容因子
          • 理解并發使用 HashMap 可能導致的問題
          • 理解 key 的設計

          1)基本數據結構

          • 1.7 數組 + 鏈表
          • 1.8 數組 + (鏈表 | 紅黑樹)
          更形象的演示,見資料中的 hash-demo.jar,運行需要 jdk14 以上環境,進入 jar 包目錄,執行下面命令
          java -jar --add-exports java.base/jdk.internal.misc=ALL-UNNAMED hash-demo.jar

          2)樹化與退化

          樹化意義

          • 紅黑樹用來避免 DoS 攻擊,防止鏈表超長時性能下降,樹化應當是偶然情況,是保底策略
          • hash 表的查找,更新的時間復雜度是 O(1)O(1),而紅黑樹的查找,更新的時間復雜度是 O(log_2?n )O(log2??n),TreeNode 占用空間也比普通 Node 的大,如非必要,盡量還是使用鏈表
          • hash 值如果足夠隨機,則在 hash 表內按泊松分布,在負載因子 0.75 的情況下,長度超過 8 的鏈表出現概率是 0.00000006,樹化閾值選擇 8 就是為了讓樹化幾率足夠小

          樹化規則

          • 當鏈表長度超過樹化閾值 8 時,先嘗試擴容來減少鏈表長度,如果數組容量已經 >=64,才會進行樹化

          退化規則

          • 情況1:在擴容時如果拆分樹時,樹元素個數 <=6 則會退化鏈表
          • 情況2:remove 樹節點時,若 root、root.left、root.right、root.left.left 有一個為 null ,也會退化為鏈表

          3)索引計算

          索引計算方法


          • 首先,計算對象的 hashCode()
          • 再進行調用 HashMap 的 hash() 方法進行二次哈希
          • 二次 hash() 是為了綜合高位數據,讓哈希分布更為均勻
          • 最后 & (capacity – 1) 得到索引

          數組容量為何是 2 的 n 次冪

          1. 計算索引時效率更高:如果是 2 的 n 次冪可以使用位與運算代替取模
          2. 擴容時重新計算索引效率更高: hash & oldCap==0 的元素留在原來位置 ,否則新位置=舊位置 + oldCap

          注意

          • 二次 hash 是為了配合 容量是 2 的 n 次冪 這一設計前提,如果 hash 表的容量不是 2 的 n 次冪,則不必二次 hash
          • 容量是 2 的 n 次冪 這一設計計算索引效率更好,但 hash 的分散性就不好,需要二次 hash 來作為補償,沒有采用這一設計的典型例子是 Hashtable

          4)put 與擴容

          put 流程

          1. HashMap 是懶惰創建數組的,首次使用才創建數組
          2. 計算索引(桶下標)
          3. 如果桶下標還沒人占用,創建 Node 占位返回
          4. 如果桶下標已經有人占用
          5. 已經是 TreeNode 走紅黑樹的添加或更新邏輯
          6. 是普通 Node,走鏈表的添加或更新邏輯,如果鏈表長度超過樹化閾值,走樹化邏輯
          7. 返回前檢查容量是否超過閾值,一旦超過進行擴容


          1.7 與 1.8 的區別

          1. 鏈表插入節點時,1.7 是頭插法,1.8 是尾插法
          2. 1.7 是大于等于閾值且沒有空位時才擴容,而 1.8 是大于閾值就擴容
          3. 1.8 在擴容計算 Node 索引時,會優化

          擴容(加載)因子為何默認是 0.75f


          1. 在空間占用與查詢時間之間取得較好的權衡
          2. 大于這個值,空間節省了,但鏈表就會比較長影響性能
          3. 小于這個值,沖突減少了,但擴容就會更頻繁,空間占用也更多


          5)并發問題

          擴容死鏈(1.7 會存在)

          1.7 源碼如下:

          void transfer(Entry[] newTable, boolean rehash) {
              int newCapacity=newTable.length;
              for (Entry<K,V> e : table) {
                  while(null !=e) {
                      Entry<K,V> next=e.next;
                      if (rehash) {
                          e.hash=null==e.key ? 0 : hash(e.key);
                      }
                      int i=indexFor(e.hash, newCapacity);
                      e.next=newTable[i];
                      newTable[i]=e;
                      e=next;
                  }
              }
          }
          • e 和 next 都是局部變量,用來指向當前節點和下一個節點
          • 線程1(綠色)的臨時變量 e 和 next 剛引用了這倆節點,還未來得及移動節點,發生了線程切換,由線程2(藍色)完成擴容和遷移



          • 線程2 擴容完成,由于頭插法,鏈表順序顛倒。但線程1 的臨時變量 e 和 next 還引用了這倆節點,還要再來一遍遷移


          ?

          • 第一次循環
          • 循環接著線程切換前運行,注意此時 e 指向的是節點 a,next 指向的是節點 b
          • e 頭插 a 節點,注意圖中畫了兩份 a 節點,但事實上只有一個(為了不讓箭頭特別亂畫了兩份)
          • 當循環結束是 e 會指向 next 也就是 b 節點


          • 第二次循環
          • next 指向了節點 a
          • e 頭插節點 b
          • 當循環結束時,e 指向 next 也就是節點 a



          ?

          • 第三次循環
          • next 指向了 null
          • e 頭插節點 a,a 的 next 指向了 b(之前 a.next 一直是 null),b 的 next 指向 a,死鏈已成
          • 當循環結束時,e 指向 next 也就是 null,因此第四次循環時會正常退出

          ?

          數據錯亂(1.7,1.8 都會存在)


          • 代碼參考 day01.map.HashMapMissData,具體調試步驟參考視頻


          補充代碼說明

          • day01.map.HashMapDistribution 演示 map 中鏈表長度符合泊松分布
          • day01.map.DistributionAffectedByCapacity 演示容量及 hashCode 取值對分布的影響
          • day01.map.DistributionAffectedByCapacity#hashtableGrowRule 演示了 Hashtable 的擴容規律
          • day01.sort.Utils#randomArray 如果 hashCode 足夠隨機,容量是否是 2 的 n 次冪影響不大
          • day01.sort.Utils#lowSameArray 如果 hashCode 低位一樣的多,容量是 2 的 n 次冪會導致分布不均勻
          • day01.sort.Utils#evenArray 如果 hashCode 偶數的多,容量是 2 的 n 次冪會導致分布不均勻
          • 由此得出對于容量是 2 的 n 次冪的設計來講,二次 hash 非常重要
          • day01.map.HashMapVsHashtable 演示了對于同樣數量的單詞字符串放入 HashMap 和 Hashtable 分布上的區別


          6)key 的設計

          key 的設計要求

          1. HashMap 的 key 可以為 null,但 Map 的其他實現則不然
          2. 作為 key 的對象,必須實現 hashCode 和 equals,并且 key 的內容不能修改(不可變)
          3. key 的 hashCode 應該有良好的散列性

          如果 key 可變,例如修改了 age 會導致再次查詢時查詢不到

          public class HashMapMutableKey {
              public static void main(String[] args) {
                  HashMap<Student, Object> map=new HashMap<>();
                  Student stu=new Student("張三", 18);
                  map.put(stu, new Object());
          
                  System.out.println(map.get(stu));
          
                  stu.age=19;
                  System.out.println(map.get(stu));
              }
          
              static class Student {
                  String name;
                  int age;
          
                  public Student(String name, int age) {
                      this.name=name;
                      this.age=age;
                  }
          
                  public String getName() {
                      return name;
                  }
          
                  public void setName(String name) {
                      this.name=name;
                  }
          
                  public int getAge() {
                      return age;
                  }
          
                  public void setAge(int age) {
                      this.age=age;
                  }
          
                  @Override
                  public boolean equals(Object o) {
                      if (this==o) return true;
                      if (o==null || getClass() !=o.getClass()) return false;
                      Student student=(Student) o;
                      return age==student.age && Objects.equals(name, student.name);
                  }
          
                  @Override
                  public int hashCode() {
                      return Objects.hash(name, age);
                  }
              }
          }

          String 對象的 hashCode() 設計

          • 目標是達到較為均勻的散列效果,每個字符串的 hashCode 足夠獨特
          • 字符串中的每個字符都可以表現為一個數字,稱為 S_iSi?,其中 i 的范圍是 0 ~ n - 1
          • 散列公式為: S_0?31^{(n-1)}+ S_1?31^{(n-2)}+ … S_i ? 31^{(n-1-i)}+ …S_{(n-1)}?31^0S0??31(n?1)+S1??31(n?2)+…Si??31(n?1?i)+…S(n?1)??310
          • 31 代入公式有較好的散列特性,并且 31 * h 可以被優化為
          • 即 ?h -h $
          • 即 2^5 ?h -h25?h?h
          • 即 h?5 -hh?5?h



          11. 單例模式

          • 單例模式_方式1_餓漢式 12:44
          • 方式2_枚舉餓漢式 11:41
          • 單例模式_方式3_懶漢式 07:33
          • 單例模式_方式4_DCL懶漢式 05:43
          • 單例模式_方式4_DCL懶漢式_為何加volatile 12:02
          • 單例模式_方式5_內部類懶漢式 05:35
          • 單例模式_在jdk中的體現
          • 掌握五種單例模式的實現方式
          • 理解為何 DCL 實現時要使用 volatile 修飾靜態變量
          • 了解 jdk 中用到單例的場景

          餓漢式

          public class Singleton1 implements Serializable {
              private Singleton1() {
                  if (INSTANCE !=null) {
                      throw new RuntimeException("單例對象不能重復創建");
                  }
                  System.out.println("private Singleton1()");
              }
          
              private static final Singleton1 INSTANCE=new Singleton1();
          
              public static Singleton1 getInstance() {
                  return INSTANCE;
              }
          
              public static void otherMethod() {
                  System.out.println("otherMethod()");
              }
          
              public Object readResolve() {
                  return INSTANCE;
              }
          }


          • 構造方法拋出異常是防止反射破壞單例
          • readResolve() 是防止反序列化破壞單例


          枚舉餓漢式

          public enum Singleton2 {
              INSTANCE;
          
              private Singleton2() {
                  System.out.println("private Singleton2()");
              }
          
              @Override
              public String toString() {
                  return getClass().getName() + "@" + Integer.toHexString(hashCode());
              }
          
              public static Singleton2 getInstance() {
                  return INSTANCE;
              }
          
              public static void otherMethod() {
                  System.out.println("otherMethod()");
              }
          }
          • 枚舉餓漢式能天然防止反射、反序列化破壞單例


          懶漢式

          public class Singleton3 implements Serializable {
              private Singleton3() {
                  System.out.println("private Singleton3()");
              }
          
              private static Singleton3 INSTANCE=null;
          
              // Singleton3.class
              public static synchronized Singleton3 getInstance() {
                  if (INSTANCE==null) {
                      INSTANCE=new Singleton3();
                  }
                  return INSTANCE;
              }
          
              public static void otherMethod() {
                  System.out.println("otherMethod()");
              }
          
          }
          • 其實只有首次創建單例對象時才需要同步,但該代碼實際上每次調用都會同步
          • 因此有了下面的雙檢鎖改進


          雙檢鎖懶漢式

          public class Singleton4 implements Serializable {
              private Singleton4() {
                  System.out.println("private Singleton4()");
              }
          
              private static volatile Singleton4 INSTANCE=null; // 可見性,有序性
          
              public static Singleton4 getInstance() {
                  if (INSTANCE==null) {
                      synchronized (Singleton4.class) {
                          if (INSTANCE==null) {
                              INSTANCE=new Singleton4();
                          }
                      }
                  }
                  return INSTANCE;
              }
          
              public static void otherMethod() {
                  System.out.println("otherMethod()");
              }
          }

          為何必須加 volatile:

          • INSTANCE=new Singleton4() 不是原子的,分成 3 步:創建對象、調用構造、給靜態變量賦值,其中后兩步可能被指令重排序優化,變成先賦值、再調用構造
          • 如果線程1 先執行了賦值,線程2 執行到第一個 INSTANCE==null 時發現 INSTANCE 已經不為 null,此時就會返回一個未完全構造的對象


          內部類懶漢式

          public class Singleton5 implements Serializable {
              private Singleton5() {
                  System.out.println("private Singleton5()");
              }
          
              private static class Holder {
                  static Singleton5 INSTANCE=new Singleton5();
              }
          
              public static Singleton5 getInstance() {
                  return Holder.INSTANCE;
              }
          
              public static void otherMethod() {
                  System.out.println("otherMethod()");
              }
          }
          • 避免了雙檢鎖的缺點

          JDK 中單例的體現

          • Runtime 體現了餓漢式單例
          • Console 體現了雙檢鎖懶漢式單例
          • Collections 中的 EmptyNavigableSet 內部類懶漢式單例
          • ReverseComparator.REVERSE_ORDER 內部類懶漢式單例
          • Comparators.NaturalOrderComparator.INSTANCE 枚舉餓漢式單例

          以上內容參考黑馬程序員b站內容整理,如侵刪

          結:截止今日,標普500指數已經從今年二月份的1850點反彈至了如今的2170點,上漲了接近20%。美國以外地區股市的平均漲幅也有12%。在一波上漲之前基金經理在二月份的現金配置達到2001年以來的最高水平的。那么這些基金經理們追上這波上漲了嗎?數據顯示,這些年薪拿著百萬美元的基金經理在過去幾個月買入債券,同時降低了股票倉位…

          Remarkably, allocations to cash are now even higher than in February, and fundmanagers are now underweight equities for the first time in 4 years. Fundmanagers have pushed into bonds, with income allocations rising to a 3 1/2 yearhigh in June and July. Overall, fund managers' defensivepositioning supports higher equity prices in the month(s) ahead.

          值得注意的是,現在的現金配置比二月還高,與此同時,基金四年來首次集體減持了股票基金買進債券,同債券在資產配置里的占比在六月七月達到了三年半以來的新高。

          Allocations to US equitieshad been near 8-year lows over the past year, during which the US hasoutperformed most of the world. That has now changed: exposure to the US is ata 17-month high. There is room for exposure to move higher, but the tailwind forthe US due to excessive bearish sentiment has mostly passed. That's also thecase for emerging markets which have been the best performing equity region sofar in 2016. European equity markets, which have been the consensusoverweight and also the world's worst performing region, are now underweightedby fund managers for the first time in 3 years.

          去年美國基金經理的股票配置接近八年來最低水平,但是在此期間美國股市還是跑贏了世界大多數國家。現在的情況已經不同了:現在美國市場風險敞口達到了17個月的最高值。盡管風險敞口還是有更大的可能但是由于看跌情緒充斥了整個市場所以這一波順風優勢基本上已經刮過去了。這同時也是2016年涌現出的市場里表現的最好的了。大家一直在加持的表現最差的歐洲股本市場,現在也三年來首次被基金經理減持。

          * * *

          Among the various ways of measuring investorsentiment, the BAML survey of global fund managers is one of the better as theresults reflect how managers are allocated in various asset classes. Thesemanagers oversee a combined 0b in assets.

          衡量投資者情緒的方法有很多,BAML關于全球基金經理做的調查無疑其中的最佳選擇之一,該調查結果說明了在多元化的資產類別中基金經理是如何分配的。他們監管著將近6000億美金的資產組合。

          The data should be viewed mostly from acontrarian perspective; that is, when equities fall in price, allocations tocash go higher and allocations to equities go lower as investors becomebearish, setting up a buy signal. When prices rise, the opposite occurs,setting up a sell signal. We did a recap of this pattern in December 2014 (post).

          我們最好從逆向思維的角度來分析這些數據;也就是說,當股本價格下跌時,現金配置就順勢走高,股本配置則隨著投資者的悲觀情緒一路唱衰,隨之就放出了買入信號。當價格上漲時,與上述情況相反的現象就發生了,那么我們就看到了賣出的信號。

          Let's review the highlights from the pastmonth.

          我們一起來看看上個月一些值得注意的現象。

          Cash: Fund managers cash levels at the equity low in February were 5.6%,the highest since the post-9/11 panic in November 2001 and lower than at anytime during the 2008-09 bear market. This was an extreme that has normally beenvery bullish for equities. Remarkably, with the SPX having since risen nearly20%, cash in July is now even higher (5.8%) and at the highest level in 14years (since November 2001). Even November 2001, which wasn't a bearmarket low, saw equities rise nearly 10% in the following 2 months; that rallyfailed when cash levels fell under 4%. This is supportive of further gains inequities.

          現金:在股價低的二月份基金經理的現金水平是5.6%,是2001年11月的后911恐慌期以來最高,但是又比2008-2009年期間的熊市低。這種現象在股票行情大幅度上漲的時候是非常極端的。值得我們注意的是,隨著標普500上漲了接近20%,7月份的現金水平竟然比現在還要高一點了(5.8%)。這意味著美股的基金經理大多數人還是偏保守的,擔心美股風險正在加劇。

          Global equities: Fund managers were just+5% overweight equities at their low in February; since 2009, allocations hadonly been lower in mid-2011 and mid-2012, periods which were notable lows forequity prices during this bull market. Despite the rally since February, allocationsare now even lower, dropping to -1% underweight in July. This is 1.2 standarddeviations below the long term mean. Fund managers are underweight equities forthe first time in 4 years.

          全球股票:在股價低迷的二月,基金經理只加持了5%;自2009以來,這樣的配置之比2011年和2012年中期低,那段時間股價是牛市中罕見的低價。盡管二月有所回升,現在的配置還是有所下降,7月已經減持到-1%。這就意味著其1.2的標準差低于長期平均水平。

          US equities: US exposure has been nearan 8 year low during the past year, during which US equities haveoutperformed.US equities have been under-owned. That has now changed, with allocation risingto +9% overweight, the first overweight in 17 months. There is room for this tomove higher, but the tailwind for the US due to excessive bearish sentiment hasmostly passed.

          美國股票:美國基金經理美股的配置去年達到了八年來的最低水平。從去年開始,這些基金經理美國股票的持有量就偏低。現在情況已經大大不同了,配置已高達+9%的加持,是17個月以來第一次加持。這從側面說明美國經濟雖然增速緩慢,但是經濟下滑的可能并不大。

          European equities: Fund managers have been excessivelyoverweight European equities for more than a year, during which time EZequities have underperformed. For the first time in 3 years, allocations to EZare underweight (by 4%). The underweight can become more extreme but thelargest risk to underperformance due to excessive bullish sentiment has passed.

          歐洲股票:基金理已加持歐洲股本一年多了,在這段時間歐洲股本表現也很不盡人意。但是從今年2月份開始基金經理出現了三年來首次對歐洲股本進行減持(減了4%)。

          Japanese equities: Allocations to Japanhave been falling but are flat m-o-m at -7% underweight, which is thelowest since December 2012. The region has been underperforming in 2016.

          日本股票:日本的配置一直在下跌但是在過去幾個月基本持平在-7%,達到了2012年12以來的最低水平。日本股票在2016年表現也差強人意。

          Emerging markets equities: In January, allocations toemerging markets fell to the second lowest in the survey's history (-33%underweight), an extreme comparable only to early-2014 from which theregion began to strongly outperform for the next half a year. Allocations havesince risen to +10% overweight, the highest in 22 months but still 0.4 standarddeviations below the long term mean. The region has outperformed the rest ofthe world so far 2016. There is room for exposure to increase further butallocations are now back to where the rally in mid-2014 failed.

          新興市場股票:一月份的時候新興市場配置跌倒了該調查歷史以來第二低(-33%減持),極端到只能和2014年早期比較,到2014年下半年該市場就猛烈發力勢如破竹了。配置漲到+10%的加持以后就達到了22個月以來的最高,但是還是低于長期平均水平0.4的標準差。該市場在2016年比世界其他市場表現都好。未來風險敞口有可能擴大但是配置已經回到了2014年中期水平。

          Global bonds: Fund managers are +35% outweightbonds, near a 3.5 year high allocation. This is a big rise from -64%underweight in December (a 2-year low allocation). Bonds outperformed in the 10months before the current equity rally began in February. Note that bonds havehistorically started to underperform when allocations rise to -20% underweight(red shading). Current allocations are back to their long term mean.

          全球債券市場:基金經理增持了35%的債券,接近3.5年來新高。這要比12月的-64%要高多了(兩年來最低)。債券方面這十個月來的表現比自二月起開始回漲的股票要好得多。值得注意的是債券歷史性的在減持升至-20%的時候(紅色陰影部分)表現開始走下坡路。目前的配置已回至長期平均水平。

          In February, 16% of fundmanagers expected a weakereconomy in the next 12 months, thelowest since December 2011. Investors are still pessimistic, with only 2%expecting a stronger economy in the next year. This explains the lowallocations to equities and high allocations to cash.

          二月,16%的基金經理預計未來一年內經濟發展較弱,可能會是自2011年12月以來的最低水平。者情依舊悲,只有2%的人認為下一年經濟勢頭強勁這也解釋了股本配置低金配置高漲這象。

          Commodities: Allocations to commoditiesimproved to a 3.5 year high at -4% underweight. This is neutral relative to thelong term mean. In comparison, in February, allocations were near one of thelowest levels in the survey's history (-29% underweight). The improvement incommodity allocations goes together with that for emerging markets.

          商品期貨:商品期貨的配置遭到了-4%的減持,創3.5年來的新高。這與長期平均水平接近持平。相比之下,二月份的資本配置基本上接近本調查有史以來的最低水平(-29%減持)。商品期貨方面的配置也隨著新興市場提高了。

          Sectors: Relative to history,managers are extremely overweight cash. They are far more overweight bonds thanequities. Overall, this is very defensive positioning.

          總體行業:對比歷史數據,經理人在大量加持現金配置。相對于股票,他們依舊持有更大量的債券。總體來看,這絕對是一個防守性定位。

          Fund managers risk appetiteis the lowest since July 2012, a level from which SPX rose 10% over thefollowing two months.

          基金經理人的風險偏好達到了2012年七月以來的最低點。

          A record percent of fundmanagers have bought downside protection for the next 3 months.

          下表顯示了基金經理人們在未來三個月內購入的下行保護。

          Survey details are below.調查的具體信息見下文:

          1. Cash (5.8%): Cashbalances increased to 5.8% from 5.7%. This is higher than in February (5.6%)and the highest since November 2001. Typical range is 3.5-5%. BAML has a 4.5%contrarian buy level but we consider over 5% to be a better signal. More onthis indicatorhere.

          現金(5.8%):現金結余從5.7%升至5.8%。這個數據已經高于2月的(5.6%)同時也是自2001年11月以來的最高值。正常浮動范圍是在3.5-5%。BAML的反向購買水平達到了4.5%,但是我們認為超過5%才是一個理想的信號。更多信息點擊這里http://fat-pitch.blogspot.jp/2013/01/fund-manager-cash-balance-at-extremes.html

          2. Equities (-1%): A net -1% are underweight global equities, down from +1% in June andbelow the +5% overweight in February. Over +50% is bearish. A washout low(bullish) is under +15%. More on this indicatorhere.

          股票(-1%):全球股本減持從六月的+1%跌至-1%,也低于二月的+5%的加持。超過50%就可以看作是熊市了。牛市就是低于+15%。更多信息點這里http://fat-pitch.blogspot.jp/2013/01/fund-manager-equity-exposure-at-extremes.html

          3. Regions:

          地區

          1. US (+9%):Exposure to the US rose from -15% underweight in May to +5% overweight in July;this is the first overweight for the US in 17 months.

          美國(+9%):美國的風險敞口從五月的-15%減持增加到七月+5%加持;這是美國17個月以來首次加持。

          2. Europe (-4%): Exposure to Europe dropped from +26% overweight in June to -4%underweight. This is the first underweight for Europe in 3 years.

          歐洲(-4%):歐洲的風險敞口從六月的+26%加持跌至-4%減持。這是歐洲三年來首次減持。

          3. Japan (-7%):Exposure to Japan was unchanged at -7% underweight. Funds were -20% underweightin December 2012 when the Japanese rally began.

          日本(-7%):日本的風險敞口維持在-7%沒有變化。自從日本股市2012年12月回升開始基金就維持在-20%減持。

          4. EM (+10%): Exposureto EM rose from +6% overweight in June to +10% overweight in July - a 22-monthhigh. Exposure was -33% underweight in January when the regional rally began. -34% underweight in September 2015 was the lowest in the survey'shistory.

          +10%):新風險敞口從六月的+6%加持升至七月的+10%加持——22個月以來的新高。一月區域內回升時風險敞口還是-33%減持。2015年9月的數據為-34%減持,是調查有史以來最低。

          4. Bonds (+35%): A net +35% are underweight bonds, a rise from -64% in December butunchanged from-34% in June. This is near a 3.5 year high allocation.

          債券(+35%):美國基金經理在過去5個月里增持了35%的債券倉位。這幾乎是3.5年來的最高值。

          Commodities(-4%): Anet -4% are underweight commodities - a 3.5 year high - an improvement from-12% last month. Higher commodity exposure goes in hand with improved sentimenttowards EM.

          商品期貨(-4%):此項數據為凈-4%減持——3.5年來新高——較上個月的-12%有所提高。商品期貨的風險敞口會隨著新興市場的市場情緒好轉走高。

          5. Macro: Just2% expect a stronger global economy over the next 12 months; in February, 16%expected a weaker economy, the most pessimistic since December 2011.

          分析:只有2%的經理人預測未來一年全球經濟發展迅猛;二月時,16%預測未來經濟發展緩慢較弱,是2011年12月以來最悲觀的觀點。

          CNBC這篇文章,美國的信用卡利息最高年化后超30%。可以說,美聯儲加息了375個基點后,對消費的打擊即將顯露。

          那么高的信用卡利息,如果不能打擊借貸消費,就會引發信用卡債務危機。當然,收3年的利息就回本了。

          https://www.cnbc.com/2022/11/15/as-retail-credit-card-interest-rates-soar-know-this-before-you-apply.html

          據該文,零售類信用卡的平均利息是年化后26.72%。普通信用卡的利息平均是年化后19.04%。

          可以說,這個利息是相當的高。

          今天,美股再度大漲,道指盤中拉升了333點,看樣子能突破3.4萬點。納指拉升了2.6%,看樣子能拉回1.2萬點。

          美股這次反彈的頂部在哪里?可能會有點高。

          不過,12月再加息50個基點后,美國的信用卡利率就更高了。

          到時候,消費就會下降。然后,可能大盤會在出數據的時候,回撤一波。

          然后,就是1月份再加息25基點的時候。

          總之,現在就是在拉升頂部。拉出一個熊市反彈的牛頂。

          BTC也反彈回了1.7萬美元。

          可以說,高風險資產都在用拉升誘惑韭菜。

          歐元已經漲回了1.04,相當于歐洲對美國的出口漲價了5%左右。這樣,就是1-3個月后,歐洲把美國輸入的通脹再還回去。

          美元暴力走強,是輸出通脹。歐元暴力走強,是把通脹再還給美國。

          這個,就好比踢皮球。

          歐元這么漲,就是對歐洲央行接下來的幾個75基點的追趕式加息的提前的Price In。

          今天盤中,中概股主流大漲,美股漲是綠的,跌是紅的。網易漲了近10個點,拼多多和京東都漲了8個點。

          可以說,資本市場對網易,拼多多和京東的價值還是認同的。

          中概股的拉升,主要是中美關系緩解,一個重要的利空消失。

          連百度也反彈了超8%,市值是341.9億美元。

          現在,網易是470億美元市值,百度才341億市值。

          可見,百度當年的BAT是妄得虛名。像百度搞搜索,不太容易出海。而網易搞游戲,拼多多則在北美登陸。百度不可能去搶谷歌的飯碗。

          這個,就是賽道的重要性。

          https://www.cnbc.com/2022/11/15/ad-market-worse-now-than-during-pandemic-lows-david-zaslav-says.html

          據CNBC的這篇文章,華納兄弟的discovery的CEO表示,最近廣告市場比2020年疫情爆發后還疲軟。

          可以說,很多數據都指向:美股不應該暴漲。但是,由于美聯儲縮表很拖拉,最近才縮表掉了2000多億美元,市場上流動性還是很多。

          流動性多,反彈就給力。


          主站蜘蛛池模板: 成人区精品一区二区不卡| 熟妇人妻AV无码一区二区三区| 女人18毛片a级毛片一区二区| 久久99国产精品一区二区| 国产成人高清精品一区二区三区| 91亚洲一区二区在线观看不卡| 一区二区三区精品高清视频免费在线播放| 精品国产一区二区三区香蕉| 国产吧一区在线视频| 亚洲中文字幕乱码一区| 日韩人妻无码免费视频一区二区三区| 国产在线乱子伦一区二区| 国产婷婷色一区二区三区深爱网| 国产美女一区二区三区| 精品人伦一区二区三区潘金莲 | 国产免费无码一区二区| 亚洲AV无码一区二区三区系列| 亚洲国产精品一区二区久久hs| 91精品一区二区| 国产凸凹视频一区二区| 一区二区在线视频免费观看| 精品视频一区二区三区免费| 日韩精品无码中文字幕一区二区| 蜜臀AV免费一区二区三区| 亚洲精品伦理熟女国产一区二区| 激情久久av一区av二区av三区| 成人国产一区二区三区| 国产精品日韩欧美一区二区三区| 福利视频一区二区牛牛| 一本久久精品一区二区| 中文字幕乱码一区二区免费| 亚洲AV无码一区二区三区电影| 久久se精品一区精品二区国产| 天堂资源中文最新版在线一区| 精品人妻一区二区三区四区在线| 日本免费电影一区二区| 亚洲国产专区一区| 日韩一区二区三区电影在线观看| 久久久精品一区二区三区| 亚洲av无码一区二区三区网站| 国产综合视频在线观看一区|