一、Collection接口
首先,讓我們來看一下Collection接口的繼承體系
(一)Collection的常用功能
1. 添加功能 boolean add(E e) 添加一個元素 boolean addAll(Collection c) 添加一批元素 2. 刪除功能 boolean remove(Object o) 刪除一個元素 3. 判斷功能 boolean contains(Object o) 判斷集合是否包含指定的元素 boolean isEmpty() 判斷集合是否為空(集合中沒有元素) 4. 獲取功能 int size() 獲取集合的長度 5. 轉換功能 Object[] toArray() 把集合轉換為數組
(二)遍歷集合的方式
1.普通的for循環【必須要有索引,可以修改元素】
import java.util.*; public class test{ public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); list.add("Hello"); list.add("Java"); list.add("World"); for (int i = 0; i < list.size(); i++){ String s = (String) list.get(i); System.out.println(s); } } }
2.迭代器遍歷【任何集合都可以遍歷,只能獲取元素】
import java.util.*; public class test{ public static void main(String[] args) { Collection<String> c = new ArrayList<String>(); c.add("Hello"); c.add("Java"); c.add("World"); //獲取迭代器對象 Iterator<String> it = c.iterator(); //hasNext()判斷是否有下一個元素,如果有就用next()獲取 while(it.hasNext()){ //獲取下一個元素 String s = it.next(); System.out.println(s); } } }
3.高級for循環【就是迭代器的簡化方式】
import java.util.*; public class test{ public static void main(String[] args) { Collection<String> c = new ArrayList<String>(); c.add("Hello"); c.add("Java"); c.add("World"); //高級for遍歷集合 for (String s : c){ System.out.println(s); } int[] arr = {1, 2, 3, 4, 5}; //高級for遍歷數組 for (int a : arr){ System.out.println(a); } } }
(三)常見的數據結構
數據結構指的是數據的組存儲方式,不同的數據結構有不同的特點。
1. 數組結構(ArrayList底層結構)
查詢快,增刪慢
2. 鏈表結構(LinkedList底層結構)
查詢慢,增刪快
3. 棧和隊列
棧:先進后出(子彈夾,杯子)
隊列:先進先出(排隊,管子)
二、List接口
List集合類中元素有序、且可重復,集合中的每個元素都有其對應的順序索引。
List容器中的元素都對應一個整數型的序號記載其在容器中的位置,可以根據序號存取容器中的元素。
JDK API中List接口的實現類常用的有:ArrayList、LinkList和Vector。
List集合里添加了一些根據索引來操作集合元素的方法
1.add(int index, Object ele) 2.boolean addAll(int index, Collection eles) 3.Object get(int index) 4.int indexOf(Object obj) 5.int lastIndexOf(Object obj) 6.Object remove(int index) 7.Object set(int index, Object ele) 8.List subList(int fromIndex, int toIndex)
(一)ArrayList
ArrayList是List接口的典型實現類,本質上,ArrayList是對象引用的一個變長數組。
ArrayList是List接口的可變數組的實現。實現了所有可選列表操作,並允許包括null在內的所有元素。除了實現List接口外,此類還提供了一些方法來操作內部用來存儲列表的數組的大小。
每個ArrayList實例都有一個初始容量,該容量用來儲存列表元素的數組大小。默認初始容量為10。
ArrayList底層采用數組實現。數組都有一個重大的缺陷,這就是從數組的中間位置刪除一個元素要付出很大的代價,其原因是數組中處於被刪除元素之后的所有元素都要向數組的前端移動。在數組中間位置插入一個元素也是如此(數據的copy)。
注:Arrays.asList(…) 方法返回的 List 集合既不是 ArrayList 實例,也不是 Vector 實例。 Arrays.asList(…)返回值是一個固定長度的 List 集合。
1.ArrayList的存取實現
存儲:
ArrayList提供了一下添加元素的方法
set(int index, E element)、
add(E e)、
add(int index, E element)、
addAll(Collection<? extends E> c)、
addAll(inr index, Collection<? extends E> c)
add(E element)方法,將指定的元素添加到列表的尾部
import java.util.*; public class test{ public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); System.out.println(list); list.add("Java"); System.out.println(list); } }
add(int index, E element)方法,將指定的元素插入此列表的指定位置,如果當前位置有元素,則向右移動當前位於該位置的元素以及所有后續元素(將其索引值加1)
import java.util.*; public class test{ public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); list.add("hello"); list.add("world"); list.add("leslie"); System.out.println(list); list.add(1,"Java"); System.out.println(list); } }
set(int index, E element)方法,替換數組中已經存在的元素內容。
import java.util.*; public class test{ public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); list.add("hello"); list.add("world"); list.add("leslie"); System.out.println(list); list.set(1,"Java"); System.out.println(list); } }
addAll(Collection<? extends E> c)方法,按照指定Collection的迭代器所返回的元素順序,將該Collection中的所有元素添加到此列表的尾部。
import java.util.*; public class test{ public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("hello"); list.add("world"); list.add("leslie"); System.out.println(list); List<String> list_ad = new ArrayList<String>(); list_ad.add("are you ok ?"); //將list中的全部元素添加到list_ad中 System.out.println("添加是否成功:" + list_ad.addAll(list)); //通過循環輸出列表中的內容 for (int i = 0; i < list_ad.size(); i++){ System.out.println(i + ":" + list_ad.get(i)); } } }
addAll(int index, Collection<? extends E> c)方法,從指定的位置開始,將指定collection中的所有元素插入到此列表中
import java.util.*; public class test{ public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("hello"); list.add("world"); list.add("leslie"); System.out.println(list); List<String> list_ad = new ArrayList<String>(); list_ad.add("are you ready ?"); list_ad.add("are you ok ?"); //將list中的全部元素添加到list_ad中 System.out.println("添加是否成功:" + list_ad.addAll(1,list)); //通過循環輸出列表中的內容 for (int i = 0; i < list_ad.size(); i++){ System.out.println(i + ":" + list_ad.get(i)); } } }
讀取
get(int index)方法,獲取指定位置上的元素
import java.util.*; public class test{ public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("hello"); list.add("world"); list.add("leslie"); System.out.println(list); System.out.println(list.get(1)); } }
總結:
-
ArrayLlist內部是由數組來實現的。在存放數據的數組長度不夠時,會進行擴容,即增加數組長度。擴展為原來的1.5倍。
-
由於是數組來實現,所以,優點是查找元素很快。可以通過下標查找元素,查找效率高。缺點是每次添加和刪除元素都會進行大量的數組元素移動。長度不夠會擴容。效率底下。
-
ArrayList每次的增、刪、改操作都伴隨着數組的復制和元素的移動。這意味着新的內存空間的開辟。
(二)LinkedList
LinkedList其實也就是我們在數據結構中的鏈表,這種數據結構有這樣的特性:
- 分配內存空間不是必須是連續的;
- 插入、刪除操作很快,只要修改前后指針就OK了,時間復雜度為O(1);
- 訪問比較慢,必須得從第一個元素開始遍歷,時間復雜度為O(n);
在Java中,LinkedList提供了豐富的方法,可以模擬鏈式隊列,鏈式堆棧等數據結構,為用戶帶來了極大的方便,下面看看這些方法的用法:
add
boolean add(E e):在鏈表后添加一個元素,如果成功,返回true,否則返回false; void addFirst(E e):在鏈表頭部插入一個元素; addLast(E e):在鏈表尾部添加一個元素; void add(int index, E element):在指定位置插入一個元素。
示例代碼
import java.util.*; public class test{ public static void main(String[] args) { LinkedList<String> linkList = new LinkedList<>(); linkList.add("first"); linkList.add("second"); linkList.add("third"); System.out.println(linkList); linkList.addFirst("addFirst"); System.out.println(linkList); linkList.addLast("addLast"); System.out.println(linkList); linkList.add(2, "addByIndex"); System.out.println(linkList); } }
輸出:
remove
E remove();移除鏈表中第一個元素; boolean remove(Object o):移除鏈表中指定的元素; E remove(int index):移除鏈表中指定位置的元素; E removeFirst():移除鏈表中第一個元素,與remove類似; E removeLast():移除鏈表中最后一個元素; boolean removeFirstOccurrence(Object o):移除鏈表中第一次出現所在位置的元素; boolean removeLastOccurrence(Object o):移除鏈表中最后一次出現所在位置的元素;
示例代碼:
import java.util.*; public class test{ public static void main(String[] args) { LinkedList<String> linkedList = new LinkedList<>(); linkedList.add("first"); linkedList.add("second"); linkedList.add("second"); linkedList.add("third"); linkedList.add("four"); linkedList.add("five"); System.out.println(linkedList); linkedList.remove(); System.out.println("remove: " + linkedList); linkedList.remove("second"); System.out.println("remove(Object): " + linkedList); linkedList.remove("six"); System.out.println("remove(Object) not exist: " + linkedList); linkedList.remove(2); System.out.println("remove(index): " + linkedList); linkedList.removeFirst(); System.out.println("removeFirst: " + linkedList); linkedList.removeLast(); System.out.println("removeLast:" + linkedList); System.out.println("----------------------------------"); linkedList.clear(); linkedList.add("first"); linkedList.add("second"); linkedList.add("first"); linkedList.add("third"); linkedList.add("first"); linkedList.add("five"); System.out.println(linkedList); linkedList.removeFirstOccurrence("first"); System.out.println("removeFirstOccurrence: " + linkedList); linkedList.removeLastOccurrence("first"); System.out.println("removeLastOccurrence: " + linkedList); } }
輸出:
get
E get(int index):按照下邊獲取元素; E getFirst():獲取第一個元素; E getLast():獲取最后一個元素;
示例代碼
import java.util.*; public class test{ public static void main(String[] args) { LinkedList<String> linkedList = new LinkedList<>(); linkedList.add("first"); linkedList.add("second"); linkedList.add("second"); linkedList.add("third"); linkedList.add("four"); linkedList.add("five"); System.out.println(linkedList); System.out.println("get(index): " + linkedList.get(3)); System.out.println("getFirst: " + linkedList.getFirst()); System.out.println("getLast: " + linkedList.getLast()); } }
輸出:
注意到了,鏈表前后沒有產生變化。
push、pop、poll
void push(E e):與addFirst一樣,實際上它就是addFirst; E pop():與removeFirst一樣,實際上它就是removeFirst; E poll():查詢並移除第一個元素;
示例代碼:
import java.util.*; public class test{ public static void main(String[] args) { LinkedList<String> linkedList = new LinkedList<>(); linkedList.push("first"); linkedList.push("second"); linkedList.push("second"); linkedList.push("third"); linkedList.push("four"); linkedList.push("five"); System.out.println(linkedList); System.out.println("pop: " + linkedList.pop()); System.out.println("after pop: " + linkedList); System.out.println("poll: " + linkedList.poll()); System.out.println("after poll: " + linkedList); } }
輸出:
通過代碼示例可以看出:push,pop的操作已經很接近stack的操作了。
如果鏈表為空的時候,看看poll與pop是啥區別:
import java.util.*; public class test{ public static void main(String[] args) { LinkedList<String> linkedList = new LinkedList<>(); System.out.println("pop: " + linkedList.poll()); System.out.println("poll: " + linkedList.pop()); } }
輸出
可以看出poll返回null,而pop則產生異常。
peek
E peek():獲取第一個元素,但是不移除;
E peekFirst():獲取第一個元素,但是不移除;
E peekLast():獲取最后一個元素,但是不移除
示例代碼
import java.util.*; public class test{ public static void main(String[] args) { LinkedList<String> linkedList = new LinkedList<>(); linkedList.push("first"); linkedList.push("second"); linkedList.push("second"); linkedList.push("third"); linkedList.push("four"); linkedList.push("five"); System.out.println("linkedList: " + linkedList); System.out.println("peek: " + linkedList.peek()); System.out.println("peekFirst: " + linkedList.peekFirst()); System.out.println("peekLast: " + linkedList.peekLast()); System.out.println("linkedList: " + linkedList); } }
輸出:
如果沒找到對應的元素,統統輸出null:
import java.util.*; public class test{ public static void main(String[] args) { LinkedList<String> linkedList = new LinkedList<>(); System.out.println("linkedList: " + linkedList); System.out.println("peek: " + linkedList.peek()); System.out.println("peekFirst: " + linkedList.peekFirst()); System.out.println("peekLast: " + linkedList.peekLast()); } }
輸出:
offer
boolean offer(E e):在鏈表尾部插入一個元素; boolean offerFirst(E e):與addFirst一樣,實際上它就是addFirst; boolean offerLast(E e):與addLast一樣,實際上它就是addLast;
示例代碼
import java.util.*; public class test{ public static void main(String[] args) { LinkedList<String> linkedList = new LinkedList<>(); linkedList.push("first"); linkedList.push("second"); linkedList.push("second"); linkedList.push("third"); linkedList.push("four"); linkedList.push("five"); System.out.println("linkedList: " + linkedList); linkedList.offer("six"); System.out.println("linkedList: " + linkedList); linkedList.offerFirst("zero"); System.out.println("linkedList: " + linkedList); linkedList.offerLast("seven"); System.out.println("linkedList: " + linkedList); } }
輸出:
其他
boolean contains(Object o) 如果此列表包含指定的元素方法返回true。 E element() 此方法返回此列表的頭部 E set(int index,E element) 此方法替換在與指定元素在此列表中指定位置的元素。 subList(int index, int index) 方法是在給定的ArrayList集合中獲取給定下標的子集合。注意范圍是[)。
代碼示例
import java.util.*; public class test { public static void main(String[] args) { LinkedList<String> linkedList = new LinkedList<>(); linkedList.push("first"); linkedList.push("second"); linkedList.push("second"); linkedList.push("third"); linkedList.push("four"); linkedList.push("five"); System.out.println("linkedList: " + linkedList); System.out.println("linkedList.contains(\"second\"): " + linkedList.contains("second")); System.out.println("linkedList.contains(\"six\"): " + linkedList.contains("six")); System.out.println("linkedList.element(): " + linkedList.element()); System.out.println("linkedList: " + linkedList); System.out.println("linkedList.set(3, \"set\"): " + linkedList.set(3, "set")); System.out.println("linkedList: " + linkedList); System.out.println("linkedList.subList(2,4): " + linkedList.subList(2,4)); System.out.println("linkedList: " + linkedList); } }
(三)Vector
Vector 可實現自動增長的對象數組。
java.util.vector提供了向量類(Vector)以實現類似動態數組的功能。
創建了一個向量類的對象后,可以往其中隨意插入不同類的對象,即不需顧及類型也不需預先選定向量的容量,並可以方便地進行查找。
對於預先不知或者不願預先定義數組大小,並且需要頻繁地進行查找,插入,刪除工作的情況,可以考慮使用向量類。
向量類提供了三種構造方法:
public vector() public vector(int initialcapacity,int capacityIncrement) public vector(int initialcapacity)
使用第一種方法系統會自動對向量進行管理,若使用后兩種方法,則系統將根據參數,initialcapacity設定向量對象的容量(即向量對象可存儲數據的大小),當真正存放的數據個數超過容量時。系統會擴充向量對象存儲容量。
參數capacityincrement給定了每次擴充的擴充值。當capacityincrement為0的時候,則每次擴充一倍,利用這個功能可以優化存儲。
在Vector類中提供了各種方法方便用戶的使用:
1.插入功能
(1)public final synchronized void adddElement(Object obj)
將obj插入向量的尾部。obj可以是任何類型的對象。對同一個向量對象,亦可以在其中插入不同類的對象。但插入的應是對象而不是數值,所以插入數值時要注意將數組轉換成相應的對象。
例如:要插入有整數1時,不要直接調用addElement(1)。正確方法為
import java.util.*; public class test { public static void main(String[] args) { Vector<Integer> v1 = new Vector<Integer>(); Integer i = new Integer(1); v1.addElement(i); System.out.println(v1); } }
(2)public final synchronized void setElementAt(Object obj,int index)
將index處的對象設置成obj,原來的對象將被覆蓋。
import java.util.*; public class test { public static void main(String[] args) { Vector<String> v1 = new Vector<String>(); v1.addElement("Frist"); v1.addElement("Second"); System.out.println(v1); v1.setElementAt("我插!", 1); System.out.println(v1); } }
輸出:
(3)public final synchronized void insertElementAt(Object obj,int index)
在index指定的位置插入obj,原來對象以及此后的對象依次往后順延。
import java.util.*; public class test { public static void main(String[] args) { Vector<String> v1 = new Vector<String>(); v1.addElement("Frist"); v1.addElement("Second"); System.out.println(v1); v1.insertElementAt("我插!", 1); System.out.println(v1); } }
輸出:
2.刪除功能
(1)public final synchronized void removeElement(Object obj)
從向量中刪除obj,若有多個存在,則從向量頭開始試,刪除找到的第一個與obj相同的向量成員。
import java.util.*; public class test { public static void main(String[] args) { Vector<String> v1 = new Vector<String>(); v1.addElement("First"); v1.addElement("Second"); v1.addElement("Thrid"); v1.addElement("First"); System.out.println(v1); v1.removeElement("First"); System.out.println(v1); } }
輸出:
(2)public final synchronized void removeAllElements();
刪除向量所有的對象
import java.util.*; public class test { public static void main(String[] args) { Vector<String> v1 = new Vector<String>(); v1.addElement("First"); v1.addElement("Second"); v1.addElement("Thrid"); v1.addElement("First"); System.out.println(v1); v1.removeAllElements(); System.out.println(v1); } }
輸出:
(3)public fianl synchronized void removeElementAt(int index)
刪除index所指的地方的對象
import java.util.*; public class test { public static void main(String[] args) { Vector<String> v1 = new Vector<String>(); v1.addElement("First"); v1.addElement("Second"); v1.addElement("Thrid"); v1.addElement("First"); System.out.println(v1); v1.removeElementAt(1); System.out.println(v1); } }
輸出:
3.查詢搜索功能
(1)public final int indexOf(Object obj)
從向量頭開始搜索obj,返回所遇到的第一個obj對應的下標,若不存在此obj,返回-1.
import java.util.*; public class test { public static void main(String[] args) { Vector<String> v1 = new Vector<String>(); System.out.println(v1.indexOf("First")); v1.addElement("First"); v1.addElement("Second"); v1.addElement("Thrid"); v1.addElement("First"); System.out.println(v1); System.out.println(v1.indexOf("First")); } }
輸出:
(2)public final synchronized int indexOf(Object obj,int index)
從index所表示的下標處開始搜索obj.
import java.util.*; public class test { public static void main(String[] args) { Vector<String> v1 = new Vector<String>(); System.out.println(v1.indexOf("First",1)); v1.addElement("First"); v1.addElement("Second"); v1.addElement("Thrid"); v1.addElement("First"); System.out.println(v1); System.out.println(v1.indexOf("First", 1)); } }
輸出:
(3)public final int lastIndexOf(Object obj)
從向量尾部開始逆向搜索obj.
import java.util.*; public class test { public static void main(String[] args) { Vector<String> v1 = new Vector<String>(); System.out.println(v1.lastIndexOf("First",1)); v1.addElement("First"); v1.addElement("Second"); v1.addElement("Thrid"); v1.addElement("First"); System.out.println(v1); System.out.println(v1.lastIndexOf("First", 1)); } }
輸出:
(4)public final synchornized int lastIndex(Object obj,int index)
從index所表示的下標處由尾至頭逆向搜索obj.
(5)public final synchornized firstElement()
獲取向量對象中的首個obj
(6)public final synchornized Object lastElement()
獲取向量對象的最后一個obj
4.其他功能
(1)public final int size();
此方法用於獲取向量元素的個數。它們返回值是向量中實際存在的元素個數,而非向量容量。可以調用方法capacity()來獲取容量值。
(2)public final synchronized void setSize(int newsize);
此方法用來定義向量的大小,若向量對象現有成員個數已經超過了newsize的值,則超過部分的多余元素會丟失。
程序中定義Enumeration類的一個對象Enumeration是java.util中的一個接口類,
(3)public final synchronized Enumeration elements();
此方法將向量對象對應到一個枚舉類型。java.util包中的其他類中也都有這類方法,以便於用戶獲取對應的枚舉類型。
在Enumeration中封裝了有關枚舉數據集合的方法。
方法 hasMoreElement() 來判斷集合中是否還有其他元素。
方法 nextElement() 來獲取下一個元素
以下代碼就是用hasMoreElement()和 nextElement()遍歷Vector
import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.Vector; public class test { public void test01() { Vector<String> hs = new Vector<String>(); hs.add("aa"); hs.add("bb"); hs.add("aa"); hs.add("cc"); hs.add("aa"); hs.add("dd"); printSet2(hs); } public void printSet(List hs) { Iterator iterator = hs.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } } public void printSet2(Vector<String> hs) { Enumeration<String> elements = hs.elements(); while (elements.hasMoreElements()) { System.out.println(elements.nextElement()); } } public static void main(String[] args) { new test().test01(); } }
PS:同時也有一個結論 Vector是有序的,可以重復的。
(四)ArrayList、LinkedList、Vector的底層實現和區別
List 有序, 可重復, 有索引。三者均為可伸縮數組。
ArrayList:底層數據結構是數組結構。 線程不安全的。 所以ArrayList的出現替代了Vector, 但是查詢的速度很快。默認擴充為原來的1.5倍。
LinkedList:底層是鏈表數據結構。 線程不安全的, 同時對元素的增刪操作效率很高。但查詢慢。
Vector:底層數據結構是數組結構。 jdk1.0版本。 線程安全的。 無論增刪還是查詢都非常慢。默認擴充為原來的2倍。
Vector 和 ArrayList都是基於存儲元素的Object[ ] array來實現的。
LinkedList是采用雙向列表來實現的。
1、 線程同步,Vector線程安全,ArrayList線程不安全。
2、 效率問題,Vector效率低,ArrayList效率高。
3、 增長數量,Vector以1.5倍增長,ArrayList以2倍增長。
List集合子類Vector這個類已經不常用了, 我就說里面的一個方法, Elements方法, 這個方法的返回值是枚舉接口, 里面有兩個方法, 判斷和獲取。此接口Enumeration的功能與 Iterator 接口的功能是重復的。Enumeration的名稱和方法的名稱過程, 書寫很麻煩。 所以被Iterator所取代。
ArrayList是實現了基於動態數組的數據結構,LinkedList基於雙線鏈表的數據結構。
ArrayList可以隨機定位對於新增和刪除操作add和remove,LinedList比較占優勢
具有Collection接口必備的iterator()方法外,List還提供一個listIterator()方法ListIterator多了一些add()之類的方法,允許添加,刪除,設定元素,還能向前或向后遍歷。
Vector與ArrayList唯一的區別是,Vector是線程安全的,即它的大部分方法都包含有關鍵字synchronized,因此,若對於單一線程的應用來說,最好使用ArrayList代替Vector,因為這樣效率會快很多(類似的情況有StringBuffer線程安全的與StringBuilder線程不安全的);而在多線程程序中,為了保證數據的同步和一致性,可以使用Vector代替ArrayList實現同樣的功能。