天天看點

java 集合 Collection(List、Set)集合

集合

集合架構

1、Collection

java.util.Collection 接口

集合是用來儲存一組元素的,不同的實作類,實作了不同的資料結構

Collection是所有集合的頂級接口,規定了所有集合都必須具備的功能

集合與數組一樣,儲存一組元素,但是操作元素的方法集合提供了

Collection下面有兩個常見的子接口(分類)

2、List(可重複集合)

java.util.List:線性表,特點是可以存放重複元素,并且有序

3、Set(不可重複集合)

java.util.Set:不可以重複集合,大部分實作類是無序的

是否為重複元素是根據元素自身equals比較的結果判定的

集合持有對象的引用

集合中存儲的都是引用類型元素

并且集合隻儲存每個元素對象的引用,而并非将元素對象本身存入集合

1、集合隻能放引用類型元素,不能放基礎類型

Collection c = new ArrayList();
		c.add(1);
		System.out.println(c);
           

若放的是基礎類型,在jdk5之後或觸發其自動拆裝箱特性,将基本類型轉換為對應的引用類型

2、集合隻儲存每個元素對象的引用(元素位址,非元素對象)

public class CollectionDemo2 {
	public static void main(String[] args) {
		Point p = new Point(1,2);
		Collection c = new ArrayList();
		//把p對象添加到集合中(添加的是p對象的位址)
		c.add(p);
		System.out.println("p:"+p);//(1,2)
		System.out.println("c:"+c);//[(1,2)]
		//修改p對象的值,發現集合中的元素也發生改變
		p.setX(2);
		System.out.println("p:"+p);//(2,2)
		System.out.println("c:"+c);//[(2,2)]
	}
}
           
java 集合 Collection(List、Set)集合

面試考題

問輸出結果(考點,記憶體的存儲,方法區、棧與堆)

package collection;

import java.util.ArrayList;
import java.util.Collection;

public class Test {
	public static void main(String[] args) {
		int a = 1;
		String s = "hellow";
		Point p = new Point(1,2);
		Collection c = new ArrayList();
		c.add(p);
		test(a,s,p,c);
		System.out.println("a:"+a);//a:1
		System.out.println("s:"+s);//s:hellow
		System.out.println("p:"+p);//p:(2,2)
		System.out.println("c:"+c);//c:[(2,6)]
		//注意最後一個不是(5,6)
		//因為在add後,p修改了x為2了,這時c中p指向的也發生改變了
	}
	public static void test(int a,String s,Point p,Collection c){
		a++;
		s = s+"world";
		p.setX(2);
		p = new Point(5,6);
		c.clear();
		c.add(p);
		p.setX(a);
		c = new ArrayList();
		c.add(new Point(7,8));
	}
}
           

集合中的常用方法

1、添加元素(add)

boolean add(E e)

向集合中添加元素,若成功添加則傳回值為true

Collection c = new ArrayList();
		c.add("one");
		c.add("two");
		c.add("three");
		c.add("four");
		c.add("five");
		System.out.println(c);
           

2、添加元素(集合之間求并集 addAll)

boolean addAll(Collection c)

向集合中添加另外一個集合的所有元素(set集合隻會添加不重複的)

若有元素添加成功則傳回值為true

注意:相當于求兩集合的并集,如果是Set(不可重複)清單,還可以去重

public class CollectionDemo3 {
	public static void main(String[] args) {
		Collection c1 = new ArrayList();
		c1.add("java");
		c1.add("c");
		c1.add("c++");
		System.out.println("c1:"+c1);
		
		Collection c2 = new ArrayList();
		c2.add("php");
		c2.add("java");
		c2.add("python");
		c2.add("go");
		System.out.println("c2:"+c2);
		
		c1.addAll(c2);
		System.out.println("c1:"+c1);
		//c1:[java, c, c++, php, java, python, go]
	}
}
           

3、檢視集合長度(size)

int size() 傳回目前集合的元素個數

相當于數組中的.length()方法

int size = c.size();
        System.out.println("size:"+size);
           

4、判斷集合是否為空集(isEmpty)

boolean isEmpty() 判斷目前集合是否為空集

**注意:**這裡的空集是,集合存在,集合不為null,集合裡面沒有存放東西,集合裡面是空的

boolean isEmpty = c.isEmpty();
        System.out.println(isEmpty);
           

5、清空集合(clear)

集合.clear(); 清空集合中的元素,不會删除集合

c.clear();
		System.out.println("集合已經清空");
		System.out.println(c.isEmpty());
           

6、判斷給定元素是否被包含在集合中(contains)

boolean contains(Object o) 該方法會用于判斷給定的元素是否被包含在集合中

若包含則傳回true,否則傳回false

**注意:**集合在判斷元素是否被包含在集合中是根據每個元素的equals()方法進行比較後的結果

是以通常我們自定義的類要作為集合元素的話,有必要重寫equals()保證contains()方法的合理結果

equals方法在不重寫的情況下,Object定義的是使用"=="比較的

public class ContainsDemo {
	public static void main(String[] args) {
		Collection c = new ArrayList();
        //point為我們自定義的點類(包含x,y坐标)
		c.add(new Point(1,2));
		c.add(new Point(3,4));
		c.add(new Point(5,6));
		c.add(new Point(7,8));
        //輸出的集合元素是根據自定義類中的toString方法輸出
		System.out.println(c);
		
		Point p = new Point(1,2);
		//該判斷根據equals判斷(point類中我們重寫過equals方法)
		boolean contains = c.contains(p);
		System.out.println("元素是否被包含:"+contains);
	}
}

           

**注意:**集合輸出的元素,根據元素所屬類自定義的toString()方法來輸出,預設輸出引用位址

是以通常我們自定義的類要作為集合元素的話,也有必要重寫toString()方法,來保證格式化輸出

7、判斷集合是否被真包含(containsAll)

boolean 目前集合.containsAll(給定集合);

判斷目前集合是否包含給定集合中的所有元素

public class CollectionDemo3 {
	public static void main(String[] args) {
		Collection c1 = new ArrayList();
		c1.add("java");
		c1.add("c");
		c1.add("c++");
		System.out.println("c1:"+c1);
		
		Collection c3 = new ArrayList();
		c3.add("c++");
		c3.add("c");
		System.out.println("c3:"+c3);
		//判斷c1是否包含c3中的所有元素
		boolean containsAll = c1.containsAll(c3);
		System.out.println("全包含:"+containsAll);
	}
}

           

8、删除集合元素(remove)

boolean remove(Object o) 删除集合中的給定元素

**注意:**該方法也是根據equals判斷找出,然後删除,若有重複元素,調用一次隻會删除第一個

public class RemoveDemo {
	public static void main(String[] args) {
		Collection c = new ArrayList();
		c.add(new Point(1,2));
		c.add(new Point(3,4));
		c.add(new Point(5,6));
		c.add(new Point(7,8));
		System.out.println(c);
		
		Point p = new Point(1,2);
		//remove也是依靠equals比較
		//有重複元素,調用一次隻會删除第一個
		c.remove(p);
		System.out.println(c);
	}
}

           

9、删除交集元素(removeAll)

boolean 目前集合.removeAll(給定集合);

删除目前集合中與給定集合的共有元素,給定集合元素不發生變化

注意:隻是删除調用方法的集合元素,删除的是兩個集合共有的元素

public class CollectionDemo3 {
	public static void main(String[] args) {
		Collection c1 = new ArrayList();
		c1.add("java");
		c1.add("c");
		c1.add("c++");
		System.out.println("c1:"+c1);
		
		Collection c3 = new ArrayList();
		c3.add("c++");
		c3.add("c");
		System.out.println("c3:"+c3);
		//删除c1中c3的所有元素
		c1.removeAll(c3);
		System.out.println("c1:"+c1);
	}
}

           

集合的周遊(疊代器)

1、周遊集合

集合提供了統一的周遊操作

無論哪種資料結構實作的集合都提供了該周遊方式:疊代器模式

Iterator iterator()

Collection提供的iterator方法可以擷取一個用于周遊目前集合的疊代器

java.util.Iterator接口

該接口是疊代器接口,規定了周遊集合的相關操作

所有集合都有一個用于周遊自身的疊代器實作類

我們無需關注它們的類名,以多态的角度用該接口看待并調用相關周遊方法即可

使用疊代器周遊集合的統一方式遵循為:

問->取->删 (其中删除元素不是必須操作)

2、實作步驟:

  1. 先建立疊代器對象(Iterator it),接收集合調用iterator方法擷取的一個用于周遊目前集合的疊代器

    Iterator it = c.iterator();

    該步可以利用向上造型,無需關心疊代器的類名,使用Iterator接口接收即可

  2. 問:it.hasNext(),疊代器通過hasNext方法檢視有沒有下一個元素,若有傳回true
  3. 取:it.next(),疊代器通過next方法,取出該元素,傳回值為Object類(若确定傳回值的具體類型,可以采取相應的強轉)

**注意:**問與取要交替調用,問一次取一次,這個取并不會對原集合造成影響

public class IteratorDemo {
	public static void main(String[] args) {
		Collection c = new ArrayList();
		c.add("one");
		c.add("two");
		c.add("three");
		c.add("four");
		c.add("five");
		System.out.println("c:"+c);
		//建立疊代器引用,接收傳回的疊代器對象
		Iterator it = c.iterator();
		//問:疊代器通過hasNext方法檢視有沒有下一個元素,若有傳回true
		while(it.hasNext()){
			//取:疊代器通過next方法,取出下一個元素,傳回值為Object類
            //這裡強轉為了String類
			String str = (String)it.next();
			System.out.println(str);
		}
	}
}

           

3、通過疊代器删除元素

疊代器提供了remove方法

該方法不需要傳入參數,删除的就是本次周遊通過next擷取的元素

删除元素也遵循問取删原則

**注意:**在疊代器周遊元素時,是不允許通過集合操作,增删元素的,否則會抛異常

java.util.ConcurrentModificationException

public class IteratorDemo {
	public static void main(String[] args) {
		Collection c = new ArrayList();
		c.add("one");
		c.add("#");
		c.add("two");
		c.add("#");
		c.add("three");
		c.add("#");
		c.add("four");
		c.add("#");
		c.add("five");
		System.out.println("c:"+c);
		
		Iterator it = c.iterator();
		//問:疊代器通過hasNext方法檢視有沒有下一個元素,若有傳回true
		while(it.hasNext()){
			//取:疊代器通過next方法,取出下一個元素,傳回值為Object類
			String str = (String)it.next();
			System.out.println(str);
            //删除滿足條件元素
			if("#".equals(str)){
                //不能通過集合方法删除
				//c.remove(str);
                //通過疊代器對象删除,删除next擷取的元素
				it.remove();
			}
		}
		System.out.println("c:"+c);
	}
}

           

4、增強for循環(for each)

JDK5之後推出了一個特性:增強for循環

也稱為新循環,采用了疊代器的原理,它不取代傳統for循環的工作

僅用來周遊集合或數組使用,周遊其中所有元素,而不需要使用下标值

文法格式:

for(類型 接收變量名 : 數組或集合){}

**注意:**新循環是編譯器認可的,而不是虛拟機認可的,

編譯器在編譯源代碼時會将新循環改成相應的格式,

周遊數組改為傳統的for循環周遊,周遊集合改為疊代器

是以使用新循環周遊集合時,也不能操作集合增删集合元素

public class NewForDemo {
	public static void main(String[] args) {
		//使用新循環周遊數組
		String[] array = {"one","two","three","four","five"};
		for(String s : array){
			System.out.println(s);
		}
		//使用新循環周遊集合
		Collection c = new ArrayList();
		c.add("one");
		c.add("two");
		c.add("three");
		c.add("four");
		c.add("five");
		for(Object o : c){
			String s = (String)o;
			System.out.println(s);
		}
	}
}

           

泛型

泛型是JDK5之後推出的一個特性,又稱為參數化類型

允許我們在使用一個類時指定他的屬性、方法的參數和傳回值類型,使得我們用起來更靈活

泛型的原型是Object,不指定時就使用它

泛型在集合中廣泛使用,用于規定集合中的元素類型(集合中的泛型用來限制元素類型)

設計出來的初衷是,把變量類型的定義交給使用者,解決java作為強類型語言,變量命名時必須聲明類型的問題

但其實泛型隻是編譯器支援,在運作時都是當做Object來看,底層實作用的是Object,我們可以通過反射破壞泛型(反射篇會提到)

public class CollectionDemo4 {
	public static void main(String[] args) {
		//<>裡傳入指定的類型,指定後,該集合隻能存放該類型元素
		Collection<String> c = new ArrayList<String>();
        //JDK7以後,後面的<>指定泛型部分可以省略類型,編譯器會自動和前面保持一緻
		//List<String> list = new ArrayList<>();
		c.add("one");
		c.add("two");
		c.add("three");
		c.add("four");
		c.add("five");
		
		//疊代器也需要傳參為集合指定的類型
		Iterator<String> it = c.iterator();
		while(it.hasNext()){
			String s = it.next();
			System.out.println(s);
		}
		
		//可以使用指定的類型來接收疊代器對象
		for(String s:c){
			System.out.println(s);
		}
	}
}
           

線性表(List集合)

1、java.util.List 線性表

List集合是一個可以重複的集合,并且有序

特點是提供了一組通過下标操作元素的方法

2、常用實作類(ArrayList與LinkedList)

  1. java.util.ArrayList 數組

    一段連續的存儲空間,内部使用數組實作,查詢性能更好,但是增删元素性能差

  2. java.util.LinkedList 連結清單

    各元素獨立的存儲,通過節點來互相連接配接

    内部使用連結清單實作,增删元素性能好,尤其首尾增删元素性能最佳,但是查詢性能差

如果對性能沒有特别苛刻的要求時,通常使用ArrayList即可

3、List常用方法(List獨有)

  1. E get(int index) 擷取給定下标處對應的元素(E指相應泛型)
  2. E set(int index,E e) 将給定元素設定到指定位置上(替換指定位置元素)

​ 有傳回值,傳回值為原位置對應的元素,是以set是替換元素操作

  1. void add(int index,E e) 将給定元素插入到指定位置(集合重載的方法)
  2. E remove(int index) 删除并傳回給定位置元素(集合重載的方法)
  3. List subList(int start,int end) 擷取目前集合中指定範圍的子集(注意含頭不含尾)

    **注意:**截取子集不會對原集合造成影響,但是截取出來的子集還是屬于原集合,

    對截取出來的子集修改,會影響原集合,修改子集元素就是修改原集合對應元素

public class ListDemo1 {
	public static void main(String[] args) {
		//JDK7以後,後面的<>指定泛型部分可以省略類型,編譯器會自動和前面保持一緻
		List<String> list = new ArrayList<>();
		list.add("one");
		list.add("two");
		list.add("three");
		list.add("four");
		list.add("five");
		System.out.println(list);
		//1、
        //通過get方法擷取第一個元素,用相應泛型接收
		String s1 = list.get(0);
		System.out.println("list中第一個元素"+s1);
		/*
		 * 普通for循環周遊List集合(不用疊代器)
		 * 因為List集合可以通過下标擷取
		 */
		for(int i=0;i<list.size();i++){
			String str = list.get(i);
			System.out.println(str);
		}
        //2、
		//将第二個元素替換為2
		//list.set(1, "2");
		//可以用相應泛型接收替換下來的值
		String s2 = list.set(1, "2");
		System.out.println(list);
		System.out.println("替換下的元素為:"+s2);
		/*
		 * 在不建立新集合的情況下
		 * 将list集合元素颠倒位置
		 */
		//擷取前後的值互換,隻用走一半的循環
		for(int i=0;i<list.size()/2;i++){
			//将前面的數儲存下來
			String start = list.get(i);
			//将前面的數替換掉後面對應位置的數,再将替換下來的儲存
			String end = list.set(list.size()-1-i, start);
			//将替換下來的儲存到開始的位置
			list.set(i, end);
			//縮寫成一句話為:
			//list.set(i, list.set(list.size()-1-i, list.get(i)));
		}
		System.out.println(list);
		//3、
        //在下标為3的位置添加元素"two"
		list.add(3,"two");
		System.out.println(list);
		//4、
        //删除下标為4的元素
		//list.remove(4);
		//用相應泛型接收删除的值
		String s4 = list.remove(4);
		System.out.println(s4);
		System.out.println(list);
        //5、
        //擷取下标3-5的集合元素子集(含下标元素3、4,因為含頭不含尾)
		List<String> sublist = list.subList(3, 5);
		System.out.println(sublist);
	}
}
           

練習:

public class ListDemo2 {
	public static void main(String[] args) {
		List<Integer> list = new ArrayList<>();
		//對清單指派
		for(int i=0;i<10;i++){
			list.add(i,i);
		}
		System.out.println(list);//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
		
		//截取清單子集内容為3-7(要截取3-8,含頭不含尾)
		List<Integer> sublist = list.subList(3, 8);
		System.out.println(sublist);//[3, 4, 5, 6, 7]
		//不會對原集合造成影響,但是對該截取出來的子集修改,會影響原集合
		System.out.println(list);//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
		
		//将子集元素擴大10倍
		for(int i=0;i<sublist.size();i++){
			sublist.set(i, sublist.get(i)*10);
		}
		System.out.println(sublist);//[30, 40, 50, 60, 70]
		//注意這時會對原集合造成影響,原集合同樣發生改變
		//修改子集元素就是修改原集合對應元素
		System.out.println(list);//[0, 1, 2, 30, 40, 50, 60, 70, 8, 9]
		
		//利用子集清空原集合中2-8之間的元素
		list.subList(3, 9).clear();
		System.out.println(list);//[0, 1, 2, 9]
	}
}
           

集合和數組的互轉

1、集合轉數組的方法(toArray)

T[] toArray(T[] a)

Collection 中定義了一個方法toArray,可以将目前集合轉換為數組

用自己指定的泛型數組,接收轉換後的數組,如果不指定,預設為Object,需要用Object數組接收

**注意:**toArray(new String[c.size]) 小闊号裡面傳入我們自己指定的類型數組對象,

這個數組對象隻作為模範,能用(大小夠用)就用,如果不能用,編譯器會自己按照這個模範建立一個位元組能用的數組對象

代碼示例:

package collection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
public class CollectionToArrayDemo {
	public static void main(String[] args) {
		Collection<String> c = new ArrayList<>();
		c.add("one");
		c.add("two");
		c.add("three");
		c.add("four");
		c.add("five");
		System.out.println(c);
		//toArray預設轉換為Object類數組
		Object[] array = c.toArray();
		//自己傳入一個數組對象的模範,按照這個模範生成數組
		String[] strArray = c.toArray(new String[c.size()]);
		System.out.println(Arrays.toString(strArray));
        //注意:這個數組模範能用則用,不能用會按照這個模範自己建立一個
		String[] strArray2 = c.toArray(new String[0]);
		System.out.println(Arrays.toString(strArray2));
	}
}
           

2、數組轉集合(asList)

List<T> Arrays.asList(數組) 數組轉為List集合

數組的工具類提供了一個靜态方法asList,可以将給定的數組轉換為一個List集合

**注意:**隻能轉為List集合,因為數組不能重複

這個數組轉換的集合其實是數組的映射,對這個集合元素的操作就是對相應數組元素的操作

是以,由于數組定長,是以改變由數組轉換集合的元素個數的操作(增、删)都是不支援的

會抛出異常 java.lang.UnsupportedOperationException

為了解決這個問題,我們可以建立一個真正的新集合,将數組轉換集合的元素,全部添加到新集合中

所有集合都支援一個參數為Collection類型的構造方法

作用是建立該集合的同時包含給定集合中的所有元素

public class ArrayToListDemo {
	public static void main(String[] args) {
		String[] array = {"one","two","three","four","five"};
		//轉換為相應的集合
		List<String> list = Arrays.asList(array);
		System.out.println(list);
		//注意:對這個集合的元素操作,就是對原數組相應元素的操作
		list.set(1, "2");
		System.out.println(list);
		//原數組也發生改變
		System.out.println(Arrays.toString(array));
		//由于這個原因,數組定長
		//不能對這個數組轉換的集合進行增删操作
		//list.add("six");
		//為了解決這個問題,我們可以建立新集合接收數組轉換集合的元素
		List<String> list1 = new ArrayList<>();
		list1.addAll(list);
		System.out.println(list1);
		//使用集合的重載構造方法,傳入一個Collection類型的參數
		//建立該集合的同時包含給定集合中的所有元素
		List<String> list2 = new ArrayList<>(list);
		System.out.println(list2);
	}
}

           

實作一步操作(将數組轉換為真正的集合)

public class ArrayToListDemo {
	public static void main(String[] args) {
		String[] array = {"one","two","three","four","five"};
		List<String> list = new ArrayList<>(Arrays.asList(array));
		System.out.println(list);
	}
}