目錄
數組
數組也是一個對象,所以它被存放在堆(heap)中。
一維數組
數組的聲明
int[] array;
這只是聲明瞭一個數組的引用,只是在堆中建立起了一個可以用來指向一個整數數組的引用,並沒有真正分配數組的內存。
真正創建對象數組要用new
數組的創建
array = new new int[4];
或直接 int[] array = {1,2,3,4,5};
數組的初始化
1. 基本數據類型數組
語句 int[] ints = new int[4];
執行完後,因爲int類型數組初始化的缺省值爲0,因此內存中現在就已經有了4個int(即0)
語句 char[] chars = new char[4];
char類型數組初始化的元素缺省值爲‘\0’,是一個不可見字符。現在內存裏面已經有4個char(即‘\0’)
語句 bool[] bools new bool[4];
bool類型數組的元素缺省值爲false。現在內存裏面已經有4個bool值(即false)
2. 對象類型數組
語句 String[] str = new String[4];
因爲在string這種對象類型數組中存放的是一個對象的引用。所以在該語句執行完後,內存中一個String都沒有。這一點與基本數據類型數組不同。
多維數組
多維數組在java內存中的存放
多維數組在C/C++的內存中,都是以整行優先連續排列的(內存都是一維的)。
而在Java中,多維數組在內存中的存放如上圖所示。內存不是連續的,每一維都指出來了。相當於是一維數組的一維數組。
多維數組的創建與聲明
int[][] ints = new int[4][3];
或者
int[][] ints = new int[4][]; //jagged array 鋸齒數組
ints[0] = new int[4];
ints[1] = new int[3];
這裏的第二種聲明創建方法,就是先只創建了第一維的那個數組,然後再爲每一維創建被指出來的第二維數組。
由此可見,在Java中,每一維數組的維數可以不一樣。這種數組被稱爲jagged array(鋸齒數組)。Java只支持這一種數組,只是這種數組也可以實現類似於regular array的每一維大小相等的特殊情況。而C/C++只支持regular array。
數組的初始化
int[][] ints = {{1,2,3}, {4,5}};
數組的enhance for loop
一維數組
for(int i : ints){
system.out.println(ints[i]);
}
多維數組
for(int ii : ints){ //這一維取出的仍是一個一維數組
for(int i : ii){ //所以還需要用 i 遍歷這個一維數組 ii
system.out.println(ints[i]);
}
}
但是,數組有個很大的缺點:一旦創建大小就不能更改————解決方法:集合Collection
集合Collection
集合Collection是一個泛型接口:public interface Collection<E>,可通過E來指定數據類型。
Collection主要分爲兩大類:List,Set
數組是協變的,即:若A -> B,則List(A) -> List(B) (A與B爲父類與子類的關係)
而集合是不變的,即不存在上述父類與子類的List 間的關係。List(A) 與 List(B) 沒有任何關係。
List
也是一個接口。有點像向量/序列:(x, y, z, ...)
每個元素都有自己的順序,即都有position。因此,x和y可以同時爲1,即(1, 1, 1, ...)是可以存在的。
ArrayList
import java.util.List;
import java.util.ArrayList;
list的創建
List list = new ArrayList();
後面的括號中當然也可以填上數值,如1。但由於這裏是一個 initial capacity,爲初始容量,而List的大小可以動態更改,所以沒有必要,一般不在後面寫。
創建的時候,前面要寫List而不寫ArrayList(雖然當然也是對的)。這樣會方便很多(同時有着父類List與子類ArrayList的功能)
如上這樣寫時,也會出現警告:原因是E沒有給參數。若只希望這個List放Integer,則:
List<Integer> intList = new ArrayList<>(); //<>也可以省略
並且如果這樣的話,若傳一個非Integer的給這個list,編譯器會在編譯階段就報錯,這是很方便與編程的。
注意,ArrayList長度動態更改的實質:
ArrayList在底層實際上仍是使用數組來存取元素。只是在要超過數組capacity時,會同普通array一樣複製原有元素到一個更大的新的數組中。只是它是自動幹了這件事。
取list中的元素
list.get(2);
即獲取類似於數組中下標爲2的位的數值。
LinkedList
是用鏈表,即指針把集合中的各個元素串起來的。
ArrayList的好處:可以通過位置隨機訪問;而LInkedList需要一個指針一個指針移動着來查找。
ArrayList的缺點:插入元素時(數據結構);而LInkedList就會很簡單。
建議以後都寫爲:
Person sp = new Student(); //向上類型轉換
而少寫爲 Student s = new Student();
這樣寫的好處:
若有: public f(Person p ){...},則此時也可以傳入Student類對象sp,即 f(new Student())。
若要向下類型轉換,則必須強制(可能會出錯):Student s = (Student)p;
Set
與數學上的集合類似,既不能有重複的元素。有兩種類型的實現:HashSet 和 TreeSet。
HashSet
判斷是否有重複元素的方法(不是遍歷與每一個元素比較):
兩個不同的對象經過hash函數之後,會算出兩個散列值。不同對象的散列值可能相同(散列值有限),但散列值不同的對象一定不相同。
因此在添加元素時,若此元素算出的散列值與原有元素的都不相同,則可直接加入set;若相同,則只用將該元素與與其散列值相同的那些元素進行比較。(可以把散列值看作一個一個的桶,若桶爲空,則可直接放入;若不爲空,則與桶內已有的元素進行比較即可。)
hashCode()方法 —— 獲取hash值,應與equals()方法同時進行重寫,原因:
hash值不同的兩個對象必須(一定)是不同的,所以不應該存在兩個hashcode()結果不同的對象,經過equals()方法,反而是相同的。所以hashCode()方法應與equals()方法配套進行重寫。
在equals()方法自動生成的代碼中,有一個if語句:
public boolean equals(Person obj){
...
if(getClass() != obj.getClass())
return flase;
...
}
而有時它並不是我們需要的,因爲我們有時會需要將子類對象與父類對象相比。如Person類的p和Student類的s:p.equals(s),有時是需要的。
TreeSet
TreeSet是二叉樹的實現,不允許放入null值,是自動排序的。
在放入新元素(add)的時候,會比較元素誰大誰小,才能往樹中放,小的在左,大的在右。
TreeSet也是爲了加速search的過程(add是要檢查(search)是否有重複值),時間複雜度爲對數結構。