集合
集合架構
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)]
}
}
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TPB1ENNRUTykEROBDOsJGcohVYsR2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLwcjM0MzMyEjMwIDOwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
面試考題
問輸出結果(考點,記憶體的存儲,方法區、棧與堆)
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、實作步驟:
-
先建立疊代器對象(Iterator it),接收集合調用iterator方法擷取的一個用于周遊目前集合的疊代器
Iterator it = c.iterator();
該步可以利用向上造型,無需關心疊代器的類名,使用Iterator接口接收即可
- 問:it.hasNext(),疊代器通過hasNext方法檢視有沒有下一個元素,若有傳回true
- 取: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)
-
java.util.ArrayList 數組
一段連續的存儲空間,内部使用數組實作,查詢性能更好,但是增删元素性能差
-
java.util.LinkedList 連結清單
各元素獨立的存儲,通過節點來互相連接配接
内部使用連結清單實作,增删元素性能好,尤其首尾增删元素性能最佳,但是查詢性能差
如果對性能沒有特别苛刻的要求時,通常使用ArrayList即可
3、List常用方法(List獨有)
- E get(int index) 擷取給定下标處對應的元素(E指相應泛型)
- E set(int index,E e) 将給定元素設定到指定位置上(替換指定位置元素)
有傳回值,傳回值為原位置對應的元素,是以set是替換元素操作
- void add(int index,E e) 将給定元素插入到指定位置(集合重載的方法)
- E remove(int index) 删除并傳回給定位置元素(集合重載的方法)
-
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);
}
}