一、陣列與集合
1.集合與陣列存盤資料概述
集合、陣列都是對多個資料進行存盤操作的結構,簡稱Java容器,
說明:此時的存盤,主要指的是記憶體層面的存盤,不涉及到持久化的存盤(.txt;.jpg;.avi,資料庫中)
2.陣列存盤的特點
一旦初始化以后,其長度就確定了,
陣列一旦定義好,其元素的型別也就確定了,我們也就只能操作指定型別的資料了,
比如:String[] arr;int[] arr1;Object[] arr2;
3.陣列存盤的弊端
一旦初始化以后,其長度就不可修改,
陣列中提供的方法非常限,對于添加、洗掉、插入資料等操作,非常不便,同時效率不高,
獲取陣列中實際元素的個數的需求,陣列沒有現成的屬性或方法可用,
陣列存盤資料的特點:有序、可重復,對于無序、不可重復的需求,不能滿足,
4.集合存盤的優點
解決陣列存盤資料方面的弊端,
二、Collection介面
1.單列集合框架結構
Collection介面:單列集合,用來存盤一個一個的物件
List介面:存盤有序的、可重復的資料,-->“動態”陣列
ArrayList、LinkedList、Vector
Set介面:存盤無序的、不可重復的資料-->高中講的“集合”
HashSet、LinkedHashSet、TreeSet
對應圖示:

2.Collection介面常用方法
| add(Object obj) | addAll(Collection coll) | size() | clear() | isEmpty() |
|---|---|---|---|---|
| contains(Object obj) | containsAll(Collection coll) | remove(Object obj) | removeAll(Collection coll) | retainsAll(Collection coll) |
| equals(Object obj) | hasCode() | toArray() | iterator() |
3.Collection集合與陣列間的轉換
//集合--->陣列:toArray()
Object[] arr=coll.toArray();
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]);
}
//拓展:陣列--->集合:呼叫Arrays類的靜態方法asList(T...t)
List<String> list=Arrays.asList(new String[]{"AA","BB","CC"});
System.out.println(list);
List arr1=Arrays.asList(new int[]{123,456});
System.out.println(arr1.size());//1
List arr2=Arrays.asList(new Integer[]{123,456});
System.out.println(arr2.size());//2
4.使用Collection集合存盤物件要求
向Collection介面的實作類的物件中添加資料obj時,要求obj所在類要重寫equals()
層次一:選擇合適的集合類去實作資料的保存,呼叫其內部的相關方法,
層次二:不同的集合類底層的資料結構為何?如何實作資料的操作的:增刪改查等,
三、Collections工具類的使用
作用:操作Collection和Map的工具類
1.常用方法:
| 方法 | 描述 |
|---|---|
| reverse(List list) | 反轉List中元素的順序 |
| shuffle(List list) | 對List集合元素進行隨機排序 |
| sort(List list) | 根據元素的自然順序對指定List集合元素升序排序 |
| sort(List list,Comparator c) | 根據指定的Comparator產生的順序對List集合元素進行排序 |
| swap(List list,int i,int j) | 將指定list集合中的i處元素和j處元素進行交換 |
| Object max(Collection c) | 根據元素的自然順序,回傳給定集合中的最大元素 |
| Object max(Collection,Comparator c) | 根據Comparator指定的順序,回傳給定集合中的最大元素 |
| Object min(Collection c) | Objectmin(Collection,Comparator) |
| int frequency(Collection c,Object o) | 回傳指定集合中指定元素的出現次數 |
| void copy(List dest,List src) | 將src中的內容復制到dest中 |
| boolean replaceAll(List list,Object oldVal,Object newVal) | 使用新值替換List物件的所有舊值 |

說明:ArrayList和HashMap都是執行緒不安全的,如果程式要求執行緒安全,我們可以將ArrayList、HashMap轉換為執行緒安全的,
使用:synchronizedList(List list)和synchronizedMap(Map map)
面試題:Collection和Collections的區別?
四、Collection子介面:List介面
1.存盤的資料特點
存盤有序的、可重復的資料,
2.常用方法
| 作用 | 方法 |
|---|---|
| 增 | add(Object obj) |
| 刪 | remove(int index)/remove(Object obj) |
| 改 | set(int index,Object ele) |
| 查 | get(int index) |
| 插 | add(int index,Object ele) |
| 長度 | size() |
| 遍歷 | ①Iterator迭代器方式②增強for回圈③普通的回圈 |
3.常用實作類
Collection介面:單列集合,用來存盤一個一個的物件
List介面:存盤有序的、可重復的資料,-->“動態”陣列,替換原的陣列
ArrayList:作為List介面的主要實作類;執行緒不安全的,效率高;底層使用Object[] elementData存盤
LinkedList:對于頻繁的插入、洗掉操作,使用此類效率比ArrayList高;底層使用雙向鏈表存盤
Vector:作為List介面的古老實作類;執行緒安全的,效率低;底層使用Object[] elementData存盤
4.原始碼分析(難點)
4.1 ArrayList的原始碼分析
jdk7 情況下:
ArrayList list=new ArrayList();//底層創建了長度是10的Object[]陣列elementData
list.add(123);//elementData[0]=new Integer(123);
...
list.add(11);//如果此次的添加導致底層elementData陣列容量不夠,則擴容,
//默認情況下,擴容為原來的容量的1.5倍,同時需要將原有陣列中的資料復制到新的陣列中,
//結論:建議開發中使用帶參的構造器:ArrayList list=new ArrayList(int capacity)
jdk8中ArrayList的變化:
ArrayList list=new ArrayList();//底層Object[] elementData初始化為{}.并沒創建長度為10的陣列
list.add(123);//第一次呼叫add()時,底層才創建了長度10的陣列,并將資料123添加到elementData[0]
//后續的添加和擴容操作與jdk7無異,
小結:jdk7中的ArrayList的物件的創建類似于單例的餓漢式,而jdk8中的ArrayList的物件的創建類似于單例的懶漢式,延遲了陣列的創建,節省記憶體,
4.2 LinkedList的原始碼分析
LinkedList list=new LinkedList();//內部宣告了Node型別的first和last屬性,默認值為null
list.add(123);//將123封裝到Node中,創建了Node物件,
//其中,Node定義為:體現了LinkedList的雙向鏈表的說法
private static class Node<E>{
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev,E element,Node<E> next){
this.item=element;
this.next=next;
this.prev=prev;
}
}
4.3 Vector的原始碼分析
jdk7和jdk8中通過Vector()構造器創建物件時,底層都創建了長度為10的陣列,
在擴容方面,默認擴容為原來的陣列長度的2倍,
5.存盤的元素的要求
添加的物件,所在的類要重寫equals()方法
6.面試題
ArrayList、LinkedList、Vector者的異同?
同:三個類都是實作了List介面,存盤資料的特點相同:存盤有序的、可重復的資料
不同:見上(第3部分+第4部分)
五、Collection子介面:Set介面
1.存盤的資料特點
以HashSet為例說明:
無序性:不等于隨機性,存盤的資料在底層陣列中并非照陣列索引的順序添加,而是根據資料的哈希值決定的,
不可重復性:保證添加的元素按照equals()判斷時,不能回傳true.即:相同的元素只能添加一個,
2.常用方法
Set介面中沒額外定義新的方法,使用的都是Collection中宣告過的方法,
3.常用實作類
Collection介面:單列集合,用來存盤一個一個的物件
Set介面:存盤無序的、不可重復的資料-->高中講的“集合”
HashSet:作為Set介面的主要實作類;執行緒不安全的;可以存盤null值
LinkedHashSet:作為HashSet的子類;遍歷其內部資料時,可以按照添加的順序遍歷,在添加資料的同時,每個資料還維護了兩個參考,記錄此資料前一個資料和后一個資料,對于頻繁的遍歷操作,LinkedHashSet效率高于HashSet,
TreeSet:可以照添加物件的指定屬性,進行排序,
4.元素添加程序
以HashSet為例:
我們向HashSet中添加元素a,首先呼叫元素a所在類的hashCode()方法,計算元素a的哈希值,
此哈希值接著通過某種演算法計算出在HashSet底層陣列中的存放位置(即為:索引位置,判斷
陣列此位置上是否已經元素:
情況1:
- 如果此位置上沒其他元素,則元素a添加成功,
情況2:
-
如果此位置上其他元素b(或以鏈表形式存在的多個元素,則比較元素a與元素b的hash值:
-
如果hash值不相同,則元素a添加成功,
情況3:
-
如果hash值相同,進而需要呼叫元素a所在類的equals()方法:
-
equals()回傳true,元素a添加失敗,
-
equals()回傳false,則元素a添加成功,
對于添加成功的情況2和情況3而言:元素a與已經存在指定索引位置上資料以鏈表的方式存盤,
jdk7:元素a放到陣列中,指向原來的元素,
jdk8:原來的元素在陣列中,指向元素a
總結:七上八下
HashSet底層:陣列+鏈表的結構,(前提:jdk7)

5.存盤的元素的要求
HashSet/LinkedHashSet:
要求:
-
向Set(主要指:HashSet、LinkedHashSet)中添加的資料,其所在的類一定要重寫hashCode()和equals()
-
重寫的hashCode()和equals()盡可能保持一致性:相等的物件必須具有相等的散列碼,
-
重寫兩個方法的小技巧:物件中用作equals()方法比較的Field,都應該用來計算hashCode值,
TreeSet:
-
自然排序中,比較兩個物件是否相同的標準為:compareTo()回傳0.不再是equals(),
-
定制排序中,比較兩個物件是否相同的標準為:compare()回傳0.不再是equals(),
6.TreeSet的使用
向TreeSet中添加的資料,要求是相同類的物件,
兩種排序方式:自然排序(實作Comparable介面和定制排序(Comparator)
常用的排序方式
//方式一:自然排序
@Test
public void test1(){
TreeSet set=new TreeSet();
//失敗:不能添加不同類的物件
//set.add(123);
//set.add(456);
//set.add("AA");
//set.add(new User("Tom",12));
//舉例一:
//set.add(34);
//set.add(-34);
//set.add(43);
//set.add(11);
//set.add(8);
//舉例二:
set.add(new User("Tom",12));
set.add(new User("Jerry",32));
set.add(new User("Jim",2));
set.add(new User("Mike",65));
set.add(new User("Jack",33));
set.add(new User("Jack",56));
Iterator iterator=set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
//方式二:定制排序
@Test
public void test2(){
Comparator com=new Comparator(){
//照年齡從小到大排列
@Override
public int compare(Object o1,Object o2){
if(o1 instanceof User && o2 instanceof User){
User u1=(User)o1;
User u2=(User)o2;
return Integer.compare(u1.getAge(),u2.getAge());
}else{
throw new RuntimeException("輸入的資料型別不匹配");
}
}
};
TreeSet set=new TreeSet(com);
set.add(new User("Tom",12));
set.add(new User("Jerry",32));
set.add(new User("Jim",2));
set.add(new User("Mike",65));
set.add(new User("Mary",33));
set.add(new User("Jack",33));
set.add(new User("Jack",56));
Iterator iterator=set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
六、Iterator介面與foreach回圈
1.遍歷Collection的兩種方式
- 使用迭代器Iterator
- foreach回圈(或增強for回圈)
2.迭代器介面:Iterator
java.utils包下定義的迭代器介面:Iterator
-
Iterator物件稱為迭代器(設計模式的一種),主要用于遍歷Collection集合中的元素,
-
GOF給迭代器模式的定義為:提供一種方法訪問一個容器(container)物件中各個元素,而又不需暴露該物件的內部細節,迭代器模式,就是為容器而生,
作用:遍歷集合Collectiton元素
如何獲取實體:coll.iterator()回傳一個迭代器實體
遍歷的代碼實作:
Iterator iterator=coll.iterator();
//hasNext():判斷是否還下一個元素
while(iterator.hasNext()){
//next():①指標下移②將下移以后集合位置上的元素回傳
System.out.println(iterator.next());
}
圖示說明

remove()的使用:
//測驗Iterator中的remove()
//如果還未呼叫next()或在上一次呼叫next方法之后已經呼叫了remove方法,再呼叫remove都會報IllegalStateException,
//內部定義了remove(),可以在遍歷的時候,洗掉集合中的元素,此方法不同于集合直接呼叫remove()
@Test
public void test3(){
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new Person("Jerry",20));
coll.add(new String("Tom"));
coll.add(false);
//洗掉集合中"Tom"
Iterator iterator = coll.iterator();
while (iterator.hasNext()){
// iterator.remove();
Object obj = iterator.next();
if("Tom".equals(obj)){
iterator.remove();
}
}
//遍歷集合
iterator = coll.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
3.foreach回圈
jdk5.0新特性--增強for回圈:(foreach回圈)
遍歷集合舉例:
@Test
public void test1(){
Collection coll=new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new Person("Jerry",20));
coll.add(new String("Tom"));
coll.add(false);
//for(集合元素的型別區域變數:集合物件)
for(Object obj:coll){
System.out.println(obj);
}
}
說明:內部仍然呼叫了迭代器,
遍歷陣列舉例:
@Test
public void test2(){
int[] arr=new int[]{1,2,3,4,5,6};
//for(陣列元素的型別區域變數:陣列物件)
for(int i:arr){
System.out.println(i);
}
}
七、Map介面
1.常用實作類結構
雙列集合框架:Map
Map:雙列資料,存盤key-value對的資料---類似于高中的函式:y=f(x)
HashMap:作為Map的主要實作類;執行緒不安全的,效率高;存盤null的key和value
LinkedHashMap:保證在遍歷map元素時,可以照添加的順序實作遍歷,在原有的HashMap底層結構基礎上,添加了一對指標,指向前一個和后一個元素,對于頻繁的遍歷操作,此類執行效率高于HashMap,
TreeMap:保證照添加的key-value對進行排序,實作排序遍歷,此時考慮key的自然排序或定制排序,底層使用紅黑樹,
Hashtable:作為古老的實作類;執行緒安全的,效率低;不能存盤null的key和value,
Properties:常用來處理組態檔,key和value都是String型別,
HashMap的底層:陣列+鏈表(jdk7及之前),陣列+鏈表+紅黑樹(jdk8)
面試題
-
HashMap的底層實作原理?
-
HashMap和Hashtable的異同?
-
CurrentHashMap與Hashtable的異同?(暫時不講)
2.存盤結構的理解
Map中的key:無序的、不可重復的,使用Set存盤所有的key--->key所在的類要重寫equals()和hashCode()(以HashMap為例)
Map中的value:無序的、可重復的,使用Collection存盤所有的value--->value所在的類要重寫equals(),
Map中的entry:無序的、不可重復的,使用Set存盤所有的entry,一個鍵值對:key-value構成了一個Entry物件,
圖示:

3.常用方法
| 作用 | 方法 |
|---|---|
| 添加 | put(Object key,Object value) |
| 洗掉 | remove(Object key) |
| 修改 | put(Object key,Object value) |
| 查詢 | get(Object key) |
| 長度 | size() |
| 遍歷 | keySet()/values()/entrySet() |
4.記憶體結構說明:(難點)
4.1 HashMap在jdk7中實作原理
HashMap map=new HashMap():
在實體化以后,底層創建了長度是16的一維陣列Entry[] table,
...可能已經執行過多次put…
map.put(key1,value1):
首先,呼叫key1所在類的hashCode()計算key1哈希值,此哈希值經過某種演算法計算以后,得到在Entry陣列中的存放位置,
情況1:
如果此位置上的資料為空,此時的key1-value1添加成功,
如果此位置上的資料不為空,(意味著此位置上存在一個或多個資料(以鏈表形式存在)),比較key1和已經存在的一個或多個資料的哈希值:
情況2:
如果key1的哈希值與已經存在的資料的哈希值都不相同,此時key1-value1添加成功,
如果key1的哈希值和已經存在的某一個資料(key2-value2)的哈希值相同,繼續比較:呼叫key1所在類的equals(key2)方法,比較:
情況3:
如果equals()回傳false:此時key1-value1添加成功,
如果equals()回傳true:使用value1替換value2,
補充:關于情況2和情況3:此時key1-value1和原來的資料以鏈表的方式存盤,
在不斷的添加程序中,會涉及到擴容問題,當超出臨界值(且要存放的位置非空)時,擴容,默認的擴容方式:擴容為原來容量的2倍,并將原有的資料復制過來,
4.2 HashMap在jdk8中相較于jdk7在底層實作方面的不同
-
new HashMap():底層沒創建一個長度為16的陣列
-
jdk8底層的陣列是:Node[],而非Entry[]
-
首次呼叫put()方法時,底層創建長度為16的陣列
-
jdk7底層結構:陣列+鏈表,jdk8中底層結構:陣列+鏈表+紅黑樹,
-
形成鏈表時,七上八下(jdk7:新的元素指向舊的元素,jdk8:舊的元素指向新的元素)
-
當陣列的某一個索引位置上的元素以鏈表形式存在的資料個數>8且當前陣列的長度>64時,此時此索引位置上的所資料改為使用紅黑樹存盤,
4.4 HashMap底層典型屬性的屬性的說明
| 屬性 | 說明 |
|---|---|
| DEFAULT_INITIAL_CAPACITY: | HashMap的默認容量,16 |
| DEFAULT_LOAD_FACTOR: | HashMap的默認加載因子:0.75 |
| threshold: | 擴容的臨界值,=容量*填充因子:16*0.75=>12 |
| TREEIFY_THRESHOLD: | Bucket中鏈表長度大于該默認值,轉化為紅黑樹:8 |
| MIN_TREEIFY_CAPACITY: | 桶中的Node被樹化時最小的hash表容量:64 |
4.4 LinkedHashMap的底層實作原理(了解)
LinkedHashMap底層使用的結構與HashMap相同,因為LinkedHashMap繼承于HashMap,
區別就在于:LinkedHashMap內部提供了Entry,替換HashMap中的Node,
HashMap中的內部類:Node
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
}
LinkedHashMap中的內部類:Entry
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
5. TreeMap的使用
向TreeMap中添加key-value,要求key必須是由同一個類創建的物件
因為要照key進行排序:自然排序 、定制排序
//自然排序
@Test
public void test1(){
TreeMap map = new TreeMap();
User u1 = new User("Tom",23);
User u2 = new User("Jerry",32);
User u3 = new User("Jack",20);
User u4 = new User("Rose",18);
map.put(u1,98);
map.put(u2,89);
map.put(u3,76);
map.put(u4,100);
Set entrySet = map.entrySet();
Iterator iterator1 = entrySet.iterator();
while (iterator1.hasNext()){
Object obj = iterator1.next();
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey() + "---->" + entry.getValue());
}
}
//定制排序
@Test
public void test2(){
TreeMap map = new TreeMap(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof User && o2 instanceof User){
User u1 = (User)o1;
User u2 = (User)o2;
return Integer.compare(u1.getAge(),u2.getAge());
}
throw new RuntimeException("輸入的型別不匹配!");
}
});
User u1 = new User("Tom",23);
User u2 = new User("Jerry",32);
User u3 = new User("Jack",20);
User u4 = new User("Rose",18);
map.put(u1,98);
map.put(u2,89);
map.put(u3,76);
map.put(u4,100);
Set entrySet = map.entrySet();
Iterator iterator1 = entrySet.iterator();
while (iterator1.hasNext()){
Object obj = iterator1.next();
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey() + "---->" + entry.getValue());
}
}
6.使用Properties讀取組態檔
properties讀取組態檔的順序是從下往上讀取,從末尾讀到頭!
//Properties:常用來處理組態檔,key和value都是String型別
public static void main(String[] args) {
FileInputStream fis = null;
try {
Properties pros = new Properties();
fis = new FileInputStream("jdbc.properties");
pros.load(fis);//加載流對應的檔案
String name = pros.getProperty("name");
String password = pros.getProperty("password");
System.out.println("name = " + name + ", password = " + password);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
八、資料結構簡述
1.資料結構概述
資料結構(Data Structure是一門和計算機硬體與軟體都密切相關的學科,它的研究重點是在計算機的程式設計領域中探討如何在計算機中組織和存盤資料并進行高效率的運用,涉及的內容包含:資料的邏輯關系、資料的存盤結構、排序演算法(Algorithm)、查找(或搜索)等,
2.資料結構與演算法的理解
程式能否快速而高效地完成預定的任務,取決于是否選對了資料結構,而程式是否能清楚而正確地把問題解決,則取決于演算法,
所以大家認為:“Algorithms + Data Structures = Programs”(出自:Pascal之父Nicklaus Wirth)
總結:演算法是為了解決實際問題而設計的,資料結構是演算法需要處理的問題載體,
3.資料結構的研究物件
3.1 資料間的邏輯結構
集合結構:

一對一:線性結構

一對多:樹形結構

多對多:圖形結構

3.2 資料的存盤結構
線性表(順序表、鏈表、堆疊、佇列)
樹
圖
說明:習慣上把順序表和鏈表看做基本資料結構(或真實資料結構)習慣上把堆疊、佇列、樹、圖看做抽象資料型別,簡稱ADT
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/435351.html
標籤:Java
