1. 程式人生 > >【第12天】Java集合(一)

【第12天】Java集合(一)

1 什麼是集合?有哪些分類

       集合是用來裝型別不同元素的,沒有個數限制;而陣列裝型別相同元素,有個數限制。

1.1 JCF(Java Collections FrameWork)

       意為Java集合框架。

在這裡插入圖片描述

上圖中藍色箭頭的意為介面繼承(如public interface List extends Collection 子介面為List,父介面為Collection),裡面出現的均為介面。介面不允許建立物件,只允許實現類,其中介面List的實現類有ArrayList、LinkedList、Vector、Stack等(如public class ArrayList implements List),其他的介面均有具體實現。

  • List、Set、Map是不是屬於同一個等級?
    List Set屬於Collection的子介面,Collection和Map屬於同一個等級。

  • 注意:

    • 使用這些集合需要匯入java.util包,如果不導包Java會自動去java.lang下面搜尋,找不到。
    • 所以在為類命名時,不要再使用這些已經被官方用過的名字了,這樣會使Java直接匯入你自己寫的類,找不到官方類中的方法而報錯。
    • 在賦值引用的名字時這些官方定義的類、介面的識別符號是可以用的,但是可讀性太差,要避免。
    • List x = new ArrayList<>();這樣定義是對的,相當於父類 引用 = new 子類();

2 ArrayList ★

       ArrayList集合的特點為有序,元素不唯一。底層基於陣列(Object[]) 實現。

2.1 包裝類

  • 由於ArrayList底層基於Object[]實現,這導致所有的引用資料型別都可以存放在集合裡,但是基本資料型別不可以存放其中。為了保證基本資料型別也可以放在集合裡,Java定義了包裝類。

  • 作用1 將基本資料型別打包為包裝類,然後再傳入集合中。(2.1.1)

  • 作用2 將String型別轉為包裝型別。(2.1.2)

2.1.1 對應轉換關係

  • 對應的型別
基本資料型別 boolean char byte short int long float double
對應包裝類 Boolean Character Byte Short Integer Long Float Double
  • 轉換(其他型別可類比)
		//打包(基本資料型別-->包裝類)
		int x = 45;
		//JDK5.0之前
		Integer y = Integer.valueOf(x);
		//JDK5.0開始,支援自動解包
		Integer y = x;

		//解包(包裝類-->基本資料型別)
		Integer x = new Integer(45);
		//JDK5.0之前
		int y = x.intValue();
		//JDK5.0之後,支援自動打包			
		int y = x;

也就是說JDK5.0之後,包裝類和其對應的基本資料型別可以直接賦值(自動打包/解包),當看到在集合的方法引數列表中傳入的資料型別為基本資料型別時需知道並非支援基本資料型別了,而是基本資料型別的自動打包。

另外,整數型別的包裝類會自動快取-128到127之間所有的數字,當直接給引用賦值為數字(自動打包)時,使用自動快取機制,並不是在堆記憶體中建立物件,而是直接在快取區建立值。比如:

	Long x = 120L;
	Long y = 120L;
	System.out.println(x == y);//--->true

2.1.2 將String型別轉為包裝型別

       除Character類(無法把一個字串轉成一個字元),其他包裝類還可以將String型別轉換成對應的基本資料型別。舉一例,其餘型別類推:
	String x = "120";
	int y = Integer.parseInt(x);

字串轉型別的時,如果字串中有除本型別之外的其他值,會報NumberFormatException錯誤。

轉型別boolean字串時,傳入true會轉換為true;傳入其他都報false。

  • 例:
//列印三年後張三的年齡
public class Example{
	public static void main(String[] args){

		String str = "張三:17";

		String age = str.substring(str.indexOf(":") + 1);

		int age1 = Integer.parseInt(age);
		System.out.println(age1 + 3);//--->20
		
		//或:
		//data[0]:姓名	data[1]:年齡
		//String[] data = str.split(":");
		//int age = Integer.parseInt(data[1]);
		//System.out.println("張三三年後:" + (age + 3));
	}
}

2.2 基本用法與特點

  • 如何建立ArrayList物件

    • JDK5.0之前,預設往集合裡面存放的都是Object型別的物件,取元素後型別需強轉。
      ArrayList list = new ArrayList();

    • JDK5.0及以後,可以加泛型(不加泛型預設傳入元素為Object型別)
      ArrayList<泛型> list = new ArrayList<泛型>();

    • JDK7.0及以後,後面的泛型會自動推斷(不加泛型預設傳入元素為Object型別)
      ArrayList<泛型> list = new ArrayList<>();

  • 如何新增元素

    • 一次新增一個元素:
      list.add(元素);

    • 一次新增多個元素:
      Collections.addAll(集合物件, 元素, 元素, 元素)

      	面試題:
      	Collection和Collections之間的區別?
      		Collection是所有單值型別集合統一的父介面,
      		Collections是集合的工具類。
      
    • 如何得到集合的大小
      list.size();

    • 如何得到集合裡面的某一個元素
      list.get(int 下標)

    • 如何判斷集合裡面是否出現指定的元素
      list.contains(元素)

    • 如何遍歷集合物件

      	    //方法1 for + 下標(執行按下標查詢時使用)
      		for(int x = 0;x < list.size();x++){
      			//x -> 下標
      			//list.get(x) -> 元素
      		}
    
      		//方法2 foreach
      		//底層使用迭代器實現
      		//執行與下標無關、不對集合進行增刪的一些操作使用這個比較簡單
      		for(泛型 x : list){
      			//x -> 元素
      		}
    
    
      		//方法3 迭代器 ★
      		//如果集合是無序的,無法使用下標依次刪除,使用迭代器
      		for(得到迭代器物件; 迭代器是否還有下一個元素; ){
      			//取出下一個元素
      		}
      		
      		//Java命名慣例,當getter/setter只存在一個時,可以不寫前三個字母(iterator方法命名由來)
      		for(Iterator<泛型> car = list.iterator(); car.hasNext(); ){
      			//car.next() -> 元素
      		}
    
  • 迭代器:可類比為word文件中的游標,開始時迭代器在首元素的左邊。遍歷時使用這個功能可取出集合中的元素,for和while中均可以使用,foreach的底層實現方式是迭代器。

    • hasNext():判斷迭代器當前所在位置的下一個是否還有元素。
    • next():取出下一個元素。
    • remove():移除當前元素。
  • 需要注意的是,在同一方法內使用同一個迭代器進行第二次遍歷時會因為第一次已迭代到集合末尾使得.hasNext()方法為false,第二次不能使用。

    • 當使用for迴圈時要注意將iterator方法定義於條件的首位(上面迭代器的第二個例子),定義區域性變數。
    • 使用while時每次迴圈都要定義新的iterator。
  • 例:

import java.util.*;
public class Example{
	public static void main(String[] args){

		ArrayList<String> list = new ArrayList<>();

		list.add("張三");
		Collections.addAll(list,"李四","張三","王五","王五");

		System.out.println(list);
		
		ArrayList<String> temp = new ArrayList<>();
		for(String name : list){
			if(!temp.contains(name)){
				temp.add(name);
			}
		}

		System.out.println(temp);
	}
}

2.3 刪除元素

       一個remove方法只能刪除一個元素。

2.3.1 指定下標刪除

       list.remove(int 下標)需注意:當傳入Integer型別時可能存在歧義,在這裡傳入int、char、short、byte型別不會自動打包,一定識別為下標;其他基本資料型別可自動打包為包裝類。

模擬實現:

public class Test{

	public static void main(String[] args){
		Integer x = 10;
		remove(x);--->元素

		Integer x1 = new Integer(10);
		remove(x1);--->元素
		
		remove(10);--->下標
	}

	public static void remove(int x){
			System.out.println("下標");
	}

	public static void remove(Object obj){
		System.out.println("元素");
	}
}

2.3.2 指定元素刪除與equals()

       list.remove(Object obj),底層遵循equals()比較機制。

       此處傳入的obj僅僅作為一個參照,並不是這個集合的元素。用這個參照刪除集合中“參照.equals()”為true的元素。

       如果這個呼叫remove(Object)的集合泛型重寫了equals()方法,remove(Object)會使用這個新的equals()方法判斷哪些值可以被刪除。

       可以定製(重寫)equals()自定義remove(Object)的刪除規則。

import java.util.*;
public class Test1{
	public static void main(String[] args){
		ArrayList<Teacher> list = new ArrayList<>();

		Teacher t1 = new Teacher();
		Teacher t2 = new Teacher();

		list.add(t1);
		System.out.println(list.size());//--->1
		list.remove(t2);
		//t2.equals(t1) ---> 無論什麼情況下都是true
		//此時無論remove中傳入什麼,只要傳入的是Teacher類的引用,都會將t1刪除
		System.out.println(list.size());//--->0
	}
}

class Teacher{

	@Override
	public boolean equals(Object obj){
		return true;
	}
}
import java.util.*;
public class Test2{
	public static void main(String[] args){
		ArrayList<Teacher> list = new ArrayList<>();

		Teacher t1 = new Teacher();
		Student s1 = new Student();

		list.add(t1);
		System.out.println(list.size());//--->1
		list.remove(s1);
		//s1.equals(t1) ---> Student中的equals()方法未被重寫,兩個物件地址不同,返回必為false
		System.out.println(list.size());//--->1
	}
}

class Teacher{

	@Override
	public boolean equals(Object obj){
		return true;
	}
}

class Student{
}
import java.util.*;
public class Test3{
	public static void main(String[] args){
		ArrayList<Teacher> list = new ArrayList<>();

		Teacher t1 = new Teacher();
		Student s1 = new Student();

		list.add(t1);
		System.out.println(list.size());//--->1
		list.remove(s1);
		//s1.equals(t1) ---> Student中的equals()方法被重寫,且返回必為true
		//此時無論remove中傳入什麼,只要傳入的是Teacher或Student類的引用,都會將t1刪除
		System.out.println(list.size());//--->0
	}
}

class Teacher{

	@Override
	public boolean equals(Object obj){
		return true;
	}
}

class Student{

	@Override
	public boolean equals(Object obj){
		return true;
	}
}

2.3.3 清空當前陣列內全部元素

       list.clear()

  • 使用集合給出的方法進行清空的其他辦法:
		ArrayList<Integer> list = new ArrayList<>();

		Collections.addAll(list,1,2,3,4,5);

		//for迴圈時為什麼要倒序遍歷?
		//這種執行結果是[2,4],因為當1被刪除後,2成為第0個元素,第二個被刪除的是3,同理第三個被刪除的是5
		//因陣列的長度小於當前下標數而停止刪除,結果出錯
		//刪除元素後,後面的元素向前移動,但下標不斷增長造成漏刪
		for(int x = 0; x < list.size(); x++){
				list.remove(x);
		}

		//倒序刪除
		for(int x = list.size() - 1; x >= 0; x--){
			list.remove(x);
		}
		//或
		while(list.size() != 0){
			list.remove(0);
		}

		System.out.println(list);

2.3.4 按元素內容遍歷刪除指定元素

       因為後面用到的集合不僅僅是有序集合,所以使用for + 下標遍歷刪除指定元素不一定可行。
       需要使用到迭代器進行對指定內容元素的刪除。但當使用迭代器遍歷集合的過程中,如果需要一邊遍歷,一邊新增/刪除,直接使用物件引用呼叫方法進行新增/刪除操作,會觸發ConcurrentModificationException(CME,併發修改異常)。 此時需要使用迭代器的.remove()方法對游標下一個指向的元素進行刪除。

  • 例題:
import java.util.*;
public class Example{
	public static void main(String[] args){

		ArrayList<Integer> list = new ArrayList<>();

		Collections.addAll(list,45,38,77,62,59,83);

		//刪除所有不及格的學生成績

		//方法1 for + 下標 不可行
		//刪除元素後,後面的元素向前移動,但下標不斷增長造成漏刪
		for(int x = 0; x < list.size(); x++){
			//x -> 下標
			Integer score = list.get(x);
			if(score < 60){
				list.remove(x);
			}
		}
		
		//方法2 for + 下標(倒序)不提倡使用
		//後面會遇到很多無序的集合
		for(int x = list.size() - 1; x >= 0; x--){
			//x -> 下標
			Integer score = list.get(x);
			if(score < 60){
				list.remove(x);
			}
		}

		//方法3 foreach 不可行
		//底層使用迭代器實現
		//遍歷並使用集合方法增刪(add/remove),會報ConcurrentModificationException(CME)異常
		for(Integer score : list){
			if(score < 60){
				list.remove(score);//remove(Object 元素)
			}
		}
		
		//方法4
		//當需要遍歷並在其中集合執行add/remove時,使用這種方法
		for(Iterator<Integer> car = list.iterator(); car.hasNext(); ){
			Integer score = car.next();

			if(score < 60){
				//如果使用“list.remove(score);”不可行,出異常
				car.remove();
			}
		}

		System.out.println(list);
	}
}