【第13天】Java集合(二)---手動實現ArrayList及其他List介面實現的集合
1 ArrayList(續)
1.1 擴容與縮容
ArrayList list = new ArrayList(50);//開闢一個長度為50的集合
ArrayList list = new ArrayList();//開闢一個預設大小的集合,預設值底層設定為10
不管開闢一個定長還是預設長度的集合,其實都可以裝無數個元素,集合可以自動地擴容。
-
擴容的倍數:
JDK6.0及以前:x * 3/2 + 1(整數型別,去除結果的小數部分)
JDK7.0及以後:x + (x >> 1) (—> x * 3/2) -
在實際開發時要避免擴容,因為擴容的底層其實是建立一個更大的陣列將老陣列替換:
- 建立一個更大的陣列物件
- 將老數組裡面的元素賦值到新數組裡面
- 改變引用指向
- 回收老陣列物件
- 繼續新增元素
-
如何手動擴/縮容?
- 擴容:list.ensureCapacity(int 容量);
- 縮容:list.trimToSize();//去掉因擴容倍數而產生的空餘集合空間,將空間大小縮小為元素個數。
-
雖然陣列自動擴容,但是考慮到效率開發,最好定一個大的空間,避免因自動擴容造成效率降低。
-
手動擴容比自動擴容效率更高。
1.2 手動實現ArrayList
-
要手動實現(Customed)ArrayList類需建立的成員(2個成員變數/9個方法):
-
私有的集合的陣列底層實現,用來裝元素:private Object[] data;
-
私有表示集合大小,元素個數:private int size;
-
有參構造方法接收指定長度建立的集合:public EtoakList(int x){}
-
無參構造方法接收預設
-
獲取當前集合裡元素個數,因不需要setSize(),所以getSize()簡化為size():public int size(){}
-
獲取指定下標元素:public Object get(int x){}
-
新增元素到集合:public void add(Object obj){}
-
按下標刪除元素:public void remove(int x){}
-
按元素所屬類指定的equals()刪除obj這樣的元素:public void remove(Object obj){}
-
按元素所屬類指定的equals()判斷是否存在obj這樣的元素:public boolean contains(Object obj){}
-
重寫Object類中的toString()
-
//無泛型版本(JDK6.0之前)
public class TestCustomedArrayList1{
public static void main(String[] args){
//測試
CustomedArrayList1 caList = new CustomedArrayList1();
caList.add(32);
caList.add(40);
caList.add(6);
System.out.println(caList.size());//--->2
System.out.println(caList.get(0));//--->45
System.out.println(caList.contains(6));//--->true
caList.remove(new Integer(40));
System.out.println(caList.size());//--->2
System.out.println(caList);//[32,6]
}
}
class CustomedArrayList1{
//集合的陣列底層實現,用來裝元素
//為了保證什麼資料型別都可以裝,所以使用Object[]
private Object[] data;
//表示集合大小,元素個數
private int size;
//接收指定長度建立的集合
//CustomedArrayList1 list = new CustomedArrayList1(-45);
public CustomedArrayList1(int x){//x : 陣列空間大小
if(x < 0){//負數
System.out.println("引數異常");
}else{//x >= 0
//定義新陣列,即建立集合
this.data = new Object[x];
}
}
//接收預設長度建立的集合,預設開闢10塊空間
//CustomedArrayList1 list = new CustomedArrayList1();
public CustomedArrayList1(){
//data = new Object[10];
//直接呼叫有參構造並傳入預設值
this(10);
}
//獲取當前集合裡元素個數,因不需要setSize(),所以getSize()簡化為size()
//int x = list.size();
public int size(){
return size;
}
//獲取指定下標元素
//Object obj = list.get(-6);
public Object get(int x){//x:下標
if(0 <= x && x < size){//x --> 下標正常
return data[x];
}else{//下標超出邊界
return "下標超出邊界異常";
}
}
//新增元素到集合
//list.add(元素);
public void add(Object obj){
//向集合中新增元素時,底層其實會把這個元素,置入成員data數組裡面
//新增元素前需陣列物件是否已滿
if(data.length == size){
//如果滿了就擴容
//建立一個新的陣列物件
//JDK6.0及之前 x * 3 / 2 + 1
Object[] temp = new Object[size * 3 / 2 + 1];
//將老數組裡面的元素賦值到新數組裡
System.arraycopy(data,0,temp,0,size);
//改變引用指向,新陣列賦值給老陣列新的地址
data = temp;
//gc回收老陣列空指向的物件
//擴容後再新增元素
data[size] = obj;
//變數++
size++;
}else{
//如果沒滿
//直接新增元素
data[size] = obj;
//變數++
size++;
}
}
//按下標刪除元素
//list.remove(3);
public void remove(int x){//x:下標
//從集合裡刪除下標x對應的元素時
//就是從數組裡刪除下標x對應的元素
System.arraycopy(data, x+1, data, x, size-(x+1));
//集合長度減小
size--;
/** 類推得arraycopy引數設定:
list.remove(1);
45 66 90 15 15
45 87 66 90 15 -> ArrayList
[0] [1] [2] [3] [4]
刪除下標0 從下標1開始複製4=5-1個元素
刪除下標1 從下標2開始複製3=5-2個元素
刪除小標2 從下標3開始複製2=5-3個元素
刪除下標x 從下標x+1開始複製data.length-(x+1)個元素
*/
}
//按元素所屬類指定的equals()刪除obj這樣的元素
//list.remove("B");
public void remove(Object obj){
//底層使用obj和集合裡每個元素equals()
for(int x = 0;x < size;x++){
//x -> 下標
//data[x] -> 元素
if(obj.equals(data[x])){
//下標x對應的元素和obj視為相等物件
//刪除下標x對應的元素
//一個remove方法只能刪除一個元素
remove(x);
break;
}
}
}
//按元素所屬類指定的equals()判斷是否存在obj這樣的元素
//boolean x = list.contains(Object obj)
public boolean contains(Object obj){
//底層使用obj和集合裡每個元素equals()
//list -> 張三 李四 王五
//list.contains("李四")
for(int x = 0;x < size;x++){
//x -> 下標
//data[x] -> 元素
if(obj.equals(data[x])){
return true;
}
}
return false;
}
@Override
public String toString(){
//[元素,元素,元素]
String str = "[";
for(int x = 0;x < size;x++){
//x -> 下標
//data[x] -> 元素
str += data[x];//data[x].toString()
if(x != size-1){
str += ",";
}
}
//[元素,元素,元素]
str += "]";
return str;
}
}
//泛型版本(JDK7.0之後) ★
public class TestCustomedArrayList2{
public static void main(String[] args){
AList<Student> list = new AList<Student>();
Student stu1 = new Student("張三");
Student stu2 = new Student("張三");
list.add(stu1);
list.remove(stu2);
System.out.println(list);//--->[]
}
}
class Student{
String name;
public Student(String name){
this.name = name;
}
@Override
public String toString(){
return name;
}
@Override
public boolean equals(Object obj){
if(!(obj instanceof Student))return false;
if(obj == null )return false;
if(obj == this) return true;
return this.name.equals(((Student)obj).name);
}
}
class CustomedArrayList2<E>{//ArrayList
private Object[] data;
private int size;
public CustomedArrayList2(int x){
data = new Object[x];
}
public CustomedArrayList2(){
data = new Object[10];
}
public int size(){
return size;
}
public Object get(int x){
return data[x];
}
public void add(E obj){
if(data.length == size){
Object[] temp = new Object[size + (size>>1)];
System.arraycopy(data,0,temp,0,size);
data = temp;
}
data[size] = obj;
size++;
}
public void remove(int x){
System.arraycopy(data,x+1,data,x,size-(x+1));
size--;
}
public void remove(Object obj){
for(int x = 0;x < size;x++){
if(obj.equals(data[x])){
remove(x);
break;
}
}
}
public boolean contains(Object obj){
for(int x = 0;x < size;x++){
if(obj.equals(data[x])){
return true;
}
}
return false;
}
@Override
public String toString(){
StringBuffer buffer = new StringBuffer("[");
for(int x = 0;x < size;x++){
buffer.append(data[x].toString());
if(x != size-1){
buffer.append(",");
}
}
buffer.append("]");
return buffer.toString();
}
}
2 Vector
Vector與ArrayList使用方法(語法)相同,只是在概念上有所不同:
- 同步執行緒不同
Vector同一時間允許單個執行緒進行訪問,效率較低,但不會出現併發錯誤;
ArrayList同一時間允許多個執行緒訪問,效率較高,但可能出現併發錯誤。
JDK5.0開始,集合的工具類(Collections)裡面提供的一個方法(synchronizedList)可以將執行緒不安全的ArrayList物件變成執行緒安全的集合物件,於是Vector漸漸被淘汰。
-
擴容機制不同
Vector分構造方法,(Vector(10) -> 2倍擴容 10->20->40->80…;Vector(10,3)->定長擴容 10->13 ->16->19…);
ArrayList分版本(JDK6.0之前 x * 3 / 2 + 1;JDK7.0之後 x + (x >> 1))。 -
出現的版本不同
Vector出現於JDK1.0;
ArrayList出現於JDK1.2。
3 LinkedList
LinkedList與ArrayList使用方法(語法)相同,只是在概念上有所不同:
-
ArrayList和LinkedList底層採用的資料結構不同 導致優劣勢不同
-
ArrayList:底層基於陣列實現,
優點:陣列連續儲存,所以方便查詢遍歷和隨機訪問。
缺點:新增刪除因為要維護連續(1.建立新陣列物件 2.複製老陣列元素 3.改變引用指向 4.回收老陣列 ),所以增刪效率較低。 -
LinkedList:底層基於連結串列實現,
優點:連結串列直接通過改變地址指向進行增刪,增刪元素效率高
缺點:每次遍歷查詢都需要從頭找起,隨機訪問、遍歷查詢效率低
-
-
在開發的時候,儘量避免使用LinkedList的get(int 下標)方法
-
ArrayList更適合訪問、讀取資料,LinkedList適合增刪資料。
4 Stack
Stack與ArrayList使用方法(語法)相同,只是在概念上有所不同:這個集合的意義是用陣列模擬棧結構。
import java.util.*;
public class TestStack{
public static void main(String[] args){
Stack<Integer> ss = new Stack<>();
ss.push(666);//從棧頂壓入一個元素
ss.push(777);//從棧頂壓入一個元素
ss.push(888);//從棧頂壓入一個元素
System.out.println(ss.pop());//--->888
System.out.println(ss.pop());//--->777
System.out.println(ss.pop());//--->666
}
}