一 java中的容器
1、容器(Container)定義
1.1 web容器
容器可以管理對象的生命週期、對象與對象之間的依賴關係,您可以使用一個配置文件(通常是XML),在上面定義好對象的名稱、如何產生(Prototype 方式或Singleton 方式)、哪個對象產生之後必須設定成爲某個對象的屬性等,在啓動容器之後,所有的對象都可以直接取用,不用編寫任何一行程序代碼來產生對象,或是建立對象與對象之間的依賴關係。 換個更直白點的說明方式:容器是一個Java 所編寫的程序,原先必須自行編寫程序以管理對象關係,現在容器都會自動幫您作好。
常用web容器:WebSphere,WebLogic,Resin,Tomcat
Spring也是一個容器,對對象進行管理。
1.2 容器類
用來存放其他類產生的對象的類,這個類,就是容器,或就叫它集合==》若干性質相近或相同的對象存放的類。通俗說法:提供一個放程序或數據等的地方;容器本身是一個組件與其他組件的區別是——他可以存放其他的組件,也就是說其他的組件可以放在他的上面。
Java容器類包含List、ArrayList、Vector及map、HashTable、HashMap、Hashset
ArrayList和HashMap是異步的,Vector和HashTable是同步的,所以Vector和HashTable是線程安全的,而 ArrayList和HashMap並不是線程安全的。因爲同步需要花費機器時間,所以Vector和HashTable的執行效率要低於 ArrayList和HashMap。
上述定義參看百度百科。
1.3 數組與集合的區別
- 數組長度聲明即固定,有序,存儲同一類型對象。
- 集合使程序變得更加靈活與高效。
- 容器不是數組,不能通過下標的方式訪問容器中的元素 。
二、數組
1 定義
(1)引用數據類型;(2)數組實際上是一個容器,“裝(存儲)數據”,數組類型相同,而且是有序的;(3)數組是一次性聲明多個相同變量的變量,變量的名稱都相同,使用序號來訪問。
(2)
2 底層原理
棧內存中存儲數據:基本數據類型的值與引用數據類型的內存地址;
基本數據類型,在進行方法調用時,傳遞的是基本數據類型的值的副本;
引用數據類型,在進行方法調用時,傳遞的是引用數據類型的內存地址的副本(引用)。
3 聲明方式
//【1】數組的聲明方式,這種方式,只聲明,是不能使用的,想要使用必須到堆裏開空間,必須new... int [] array; //建議使用這種 int arrayB[]; //【2】聲明幷分配空間 String []str = new String[20]; //因爲String是引用數據類型,所以默認值都是null double price[]=new double[4]; //默認值爲 0.0 //【3】聲明幷賦值(數組的靜態賦值,在程序運行前,值就已確定) int[]arrayA={12,23,4,53,56}; int [] arrayC=new int[]{34,43,23};
4 簡單算法
4.1 冒泡排序
package com.asd.reserve.utils.collections; /** * @author zs * @date 2020/1/7 15:47 */ public class PaiXu { //主方法用於測試 public static void main(String [] args){ int [] arr={43,34,4,44,5,42}; //調用冒泡排序的方法 PaiXu.maoPao(arr);//arr是實際參數 //調用輸出的方法 PaiXu.print(arr); System.out.println("\n\n"); int [] arrB={43,56,4,56,7,77,75,645}; //調用冒泡排序的方法 PaiXu.maoPao(arrB);//arr是實際參數 //調用輸出的方法 PaiXu.print(arrB); } /**該方法的功能,是進行排序*/ public static void maoPao(int [] arr){ for(int i=0;i<arr.length-1;i++){ //外層N-1 for(int j=0;j<arr.length-1-i;j++){//內層N-1-i //兩兩相比 if(arr[j]>arr[j+1]){ //[j]>[j+1] //交換 int temp=arr[j]; arr[j]=arr[j+1]; arr[j+1]=temp; } }//內for end }//外for end } public static void print(int []arr){ for(int i=0;i<arr.length;i++){ System.out.print(arr[i]+"\t"); } } }
內層控制的是比較方式:冒泡排序每次比較都是相鄰的兩個比較。
4.2選擇排序
public class PaiXu{ //主方法用於測試 public static void main(String [] args){ int [] arrB={43,56,4,57,7}; //調用選擇排序的方法 PaiXu.xuanZe(arrB);//arr是實際參數 //調用輸出的方法 PaiXu.print(arrB); /*int x=4,y=9; System.out.println("交換前:x="+x+",y="+y); change(x,y); System.out.println("交換後:x="+x+",y="+y);*/ } /**該方法的功能,是進行排序*/ public static void maoPao(int [] arr){ for(int i=0;i<arr.length-1;i++){ //外層N-1 for(int j=0;j<arr.length-1-i;j++){//內層N-1-i //兩兩相比 if(arr[j]>arr[j+1]){ //[j]>[j+1] //調用交換的方法 change(arr,j,j+1); } }//內for end }//外for end } /**該方法的功能,是排序, 選擇排序*/ public static void xuanZe(int [] arr){ for(int i=0;i<arr.length-1;i++){ //比較的輪數 for(int j=i+1;j<arr.length;j++){ //比較的次數 if(arr[i]>arr[j]){ //調用交換的方法 change(arr,i,j); } } } } /**交換的方法*/ public static void change(int [] arr,int a,int b){ //arr是數組,a與b,是數組中元素的下標 int temp=arr[a]; //整個這個交換是交換的堆裏的數據 arr[a]=arr[b]; arr[b]=temp; } public static void print(int []arr){ for(int i=0;i<arr.length;i++){ System.out.print(arr[i]+"\t"); } }
4.3遞歸
一種計算過程,如果其中每一步都要用到前一步或前幾步的結果,稱爲遞歸的。
參看百度百科。
package com.luna.base; public class Plus { //1加到100 public int sum(int i) { if (i == 1) { return 1; } return i + sum(i - 1); } public static void main(String[] args) { Plus plus = new Plus(); System.out.println("計算結果:" + plus.sum(100) + "!"); }
參看鏈接:https://blog.csdn.net/u011635492/article/details/80715832
5 二維數組
第一種聲明方式
int [] arrA={12,32,34,5}; //一維數組,arrA是引用數據類型 int [] arrC={32,45}; int [] arrD={43,45,43,46,78,9}; //聲明一個二維數組,用於存儲3個一維數組,每個一維數組的長度可以不相同 int [] [] array=new int [3][]; //賦值--》賦的內存地址, 存儲的是一維數組內存地址 array[0]=arrA; array[1]=arrC; array[2]=arrD; for(int i=0;i for(int j=0;j System.out.print(array[i][j]+"\t"); } System.out.println("\n=================================="); }
第二種聲明方式
int [] ar[]={{1223,32,32},{4,32,3},{43,43,4},{3,43,45}};
第三種聲明方法
//聲明一個長度爲4的二維數組,用於存儲4個一維數組,每個一維數組長度是2,默認爲0 int a[][]=new int[4][2];
三、集合
1 定義
容器,存儲數據。主要是放對象,當前臺訪問,將數據封裝到一個對象,而對象可能是多個,在與數據庫進行交互時,一般都用集合來存放數據。它的長度可變。
2 與數組比較
- 數組一旦聲明,其長度固定,類型一致,不夠靈活;
- 數組的插入,刪除效率低。
3 分類
3.1 Collection:
總的接口:無序且不唯一;
3.1.1 兩個子接口:List和Set。還有一個不常用的Vector接口。
- List接口:有序但不唯一,添加順序,有兩個實現類:
-
- ArryList:有序,不唯一;底層是數組。查詢,修改快。
-
- LinkList:無序,唯一。底層是鏈表。添加刪除快。
- Set接口:無序,唯一,兩個實現類:HashSet和TreeSet。
- HashSet:無序唯一,底層數據結構爲哈希表。重寫hashCode和equals方法。
- TreeSet:有序唯一,底層爲二叉樹,但必須實現Comparable或Comparator接口。
3.2Map接口:
鍵值對key,value形式存儲數據,鍵不能重複,值可以。
3.2.1 Map接口分別兩個實現類
- HashSet:底層爲哈希表,key無序,唯一。
- TreeSet: 底層爲二叉樹,key有序,唯一。
4 List
4.1 底層原理
4.1.1 ArryList
ArrayList是List接口的可變數組非同步實現,並允許包括null在內的所有元素。底層使用數組實現該集合是可變長度數組,數組擴容時,會將老數組中的元素重新拷貝一份到新的數組中,每次數組容量增長其容量的1.5倍,這種操作的代價很高。採用了Fail-Fast機制,面對併發的修改時,迭代器很快就會完全失敗,而不是冒着在將來某個不確定時間發生任意不確定行爲的風險remove方法會讓下標到數組末尾的元素向前移動一個單位,並把最後一位的值置空,方便GC。
優點:操作讀取操作效率高,基於數組實現的,可以爲null值,可以允許重複元素,有序,異步。
缺點:由於它是由動態數組實現的,不適合頻繁的對元素的插入和刪除操作,因爲每次插入和刪除都需 要移動數組中的元素。
4.1.2 LinkedList
LinkedList底層是由雙向鏈表的數據結構實現的。
由上圖可以看到:雙向鏈表是由三個部分組成:prev、data、next.
prev:由用來存儲上一個節點的地址;
data:是用來存儲要存儲的數據;
next:是用來存儲下一個節點的地址。
上圖可以看出雙向鏈表每個元素之間的聯繫。我故意將每個鏈表畫的分佈不均勻是因爲它不像數組一樣是連續排列的,雙向鏈表是可以佔用一段不連續的內存空間。
當我們有新元素插入時,只需要修改所要插入位置的前一個元素的next值和後一個元素的prev值即可。比如我們在數據2與數據6之間插入一個數據4的元素,那麼只需要修改數據2的next值和數據6的prev值。如下圖
刪除也是同理,比如要刪除數據8的元素,只需要修改數據7的next值和數據9的prev值即可,然後數據8沒有元素指向它,它就成了垃圾對象,最後被回收。因此在增加和刪除的時候只需要更改前後元素的next和prev值,效率非常高。但是在查詢的時候需要從第一個元素開始查找,直到找到我們需要的數據爲止,因此查詢的效率比較低。
4.2線程不安全
Array List是線程不安全的,這點很重要,也是面試最常問的問題。那爲什麼是不安全的呢,下面簡單總結以下。
情況一:
假設現在有A,B兩個線程同時執行add方法,而現在size = 9,於是:
1,A經過以上步驟發現初始化容量爲10,不需要進行數組擴容。
2,同時B也在執行add方法,它判斷數組初始化容量也是10(size的值還是9),於是不進行數組擴容。接着A便執行add方法,元素添加成功後,size = 10;此時B開始執行add方法,但這個時候數組元素已經添加滿了,B再添加就會造成數組下標越界異常。
情況二:
elementData[size++] = e;
這一步也有可能出現問題。
列表大小爲0,即size=0。
線程A開始添加一個元素,值爲A。此時它執行第一條操作,將A放在了elementData下標爲0的位置上。
接着線程B剛好也要開始添加一個值爲B的元素,且走到了第一步操作。此時線程B獲取到size的值依然爲0,於是它將B也放在了elementData下標爲0的位置上。
而最終size大小爲:
- 線程A開始將size的值增加爲1
- 線程B開始將size的值增加爲2
針對這種情況,可以使用以下方式解決:
List<String> list1 = Collections.synchronizedList(new ArrayList<String>());
5 Map
5.1 關於緩存
準備金系統有完整的利用Map創建的緩存機制工具類。
參看鏈接https://www.cnblogs.com/henuyuxiang/p/7486120.html也有緩存的例子。
5.1 底層原理
5.1.1 底層管理
參看鏈接https://www.cnblogs.com/vole/p/12164982.html
5.1.2 關於hashcode() 與equals()方法
參看鏈接https://www.cnblogs.com/vole/p/12165284.html
6 遍歷方式
6.1 迭代器
- 在開發中,經常使用的還是for-each循環來遍歷來Collection,不經常使用Iterable(迭代器)的,下面記錄一下terable是一般用法:
- 迭代器是一種設計模式,它是一個對象,它可以遍歷並選擇序列中的對象,而開發人員不需要了解該序列的底層結構。迭代器通常被稱爲“輕量級”對象,因爲創建它的代價小。
- Java中的Iterator功能比較簡單,並且只能單向移動.
list l = new ArrayList(); l.add("aa"); l.add("bb"); l.add("cc"); for (Iterator iter = l.iterator(); iter.hasNext();) { String str = (String)iter.next(); System.out.println(str); } /*迭代器用於while循環 Iterator iter = l.iterator(); while(iter.hasNext()){ String str = (String) iter.next(); System.out.println(str); } */
(1) 使用方法iterator()要求容器返回一個Iterator。第一次調用Iterator的next()方法時,它返回序列的第一個元素。注意:iterator()方法是java.lang.Iterable接口,被Collection繼承。
(2) 使用next()獲得序列中的下一個元素。
(3) 使用hasNext()檢查序列中是否還有元素。
(4) 使用remove()將迭代器新返回的元素刪除。
Iterator是Java迭代器最簡單的實現,爲List設計的ListIterator具有更多的功能,它可以從兩個方向遍歷List,也可以從List中插入和刪除元素。
6.2 加強for循環
7 常用方法
* 【1】增 add(Object obj);addAll(Collection col);add(int index,Object obj) * 【2】刪 clear(),remove(Object obj),remove(int index) * 【3】改 set(int index,Object obj) * 【4】查size(),itrator(),listIterator(),get(int index) * 【5】判 isEmpty(),contains(Object obj)
四、鏈表與二叉樹
1.1 鏈表
-
鏈表:linked list,由一系列結點node(鏈表中每一個元素稱爲結點)組成,結點可以在運行時動態生成。每個結點包括兩個部分:一個是存儲數據元素的數據域,另一個是存儲下一個結點地址的指針域。我們常說的鏈表結構有單向鏈表與雙向鏈表,那麼這裏給大家介紹的是單向鏈表。
簡單的說,採用該結構的集合,對元素的存取有如下的特點:
- 多個結點之間,通過地址進行連接。例如,多個人手拉手,每個人使用自己的右手拉住下個人的左手,依次類推,這樣多個人就連在一起了。
-
查找元素慢:想查找某個元素,需要通過連接的節點,依次向後查找指定元素。
-
增刪元素快:
-
增加元素:只需要修改連接下個元素的地址即可。
-
刪除元素:只需要修改連接下個元素的地址即可。
-
1.2,紅黑樹
紅黑樹是一種自平衡二叉查找樹,是計算機科學領域中的一種數據結構,典型的用途是實現關聯數組,存儲有序的數據。
紅黑樹本身就是一顆二叉查找樹,將節點插入後,該樹仍然是一顆二叉查找樹。也就意味着,樹的鍵值仍然是有序的。
紅黑樹的約束:
- 節點可以是紅色的或者黑色的。
- 根節點是黑色的。
- 葉子節點(特指空節點)是黑色的。
- 每個紅色節點的子節點都是黑色的。
- 任何一個節點到其每一個葉子節點的所有路徑上黑色節點數相同。
紅黑樹的特點:
速度特別快,趨近平衡樹,查找葉子元素最少和最多次數不多於二倍。