一 容器介紹
-
容器,是用來容納物體、管理物體。生活中,我們會用到各種各樣的容器。如鍋碗瓢盆、箱子和包等
-
程序中的“容器”也有類似的功能,用來容納和管理數據。比如,如下新聞網站的新聞列表、教育網站的課程列表就是用“容器”來管理
-
視頻課程信息也是使用“容器”來管理
-
計算機當中能夠存儲數據的位置有兩個——>一個是磁盤,一個是內存
- 磁盤是通過文件的形式存儲數據的——>(永久存儲,不會受到計算機的關閉而影響數據的)
- 內存是臨時存儲——>關閉計算機后內存中是沒有數據的
- 容器存儲的數據在內存當中
開發和學習中需要時刻和數據打交道,如何組織這些數據是我們編程中重要的內容。 我們一般通過“容器”來容納和管理數據。事實上,我們前面所學的數組就是一種容器,可以在其中放置對象或基本類型數據。
數組的優勢:是一種簡單的線性序列,可以快速地訪問數組元素,效率高。如果從查詢效率和類型檢查的角度講,數組是最好的。
數組的劣勢:不靈活。容量需要事先定義好,不能隨著需求的變化而擴容。比如:我們在一個用戶管理系統中,要把今天注冊的所有用戶取出來,那么這樣的用戶有多少個?我們在寫程序時是無法確定的。因此,在這里就不能使用數組。
基于數組并不能滿足我們對于“管理和組織數據的需求”,所以我們需要一種更強大、更靈活、容量隨時可擴的容器來裝載我們的對象。 這就是我們今天要學習的容器,也叫集合(Collection)。
二 容器結構
(三大接口)
1 結構圖
? List和Set都是單例接口,Map是雙例接口(k,v)
2 單例集合
? List——>相當于動態數組,Set——>相當于數學里的集合
3 雙例集合
? Map——>相當于數學里的函數,(mapping——>映射)
(單例集合)
三 (*)Collection接口
(12個方法)
(定義了單例集合的基本行為)
(接口中的方法很重要,注意返回值,和返回值類型)
Collection 表示一組對象,它是集中、收集的意思。Collection接口的兩個子接口是List、Set接口。
Collection接口中定義的方法
方法 | 說明 |
---|---|
boolean add(Object element) | 增加元素到容器中 |
boolean remove(Object element) | 從容器中移除元素 |
boolean contains(Object element) | 容器中是否包含該元素 |
int size() | 容器中元素的數量 |
boolean isEmpty() | 容器是否為空 |
void clear() | 清空容器中所有元素 |
Iterator iterator() | 獲得迭代器,用于遍歷所有元素 |
boolean containsAll(Collection c) | 本容器是否包含c容器中的所有元素 |
boolean addAll(Collection c) | 將容器c中所有元素增加到本容器 |
boolean removeAll(Collection c) | 移除本容器和容器c中都包含的元素 |
boolean retainAll(Collection c) | 取本容器和容器c中都包含的元素,移除非交集元素 |
Object[] toArray() | 轉化成Object數組 |
由于List、Set是Collection的子接口,意味著所有List、Set的實現類都有上面的方法。
JDK8之后,Collection接口新增的方法(將在JDK新特性和函數式編程中介紹):
新增方法 | 說明 |
---|---|
removeIf | 作用是刪除容器中所有滿足filter指定條件的元素 |
stream parallelStream | stream和parallelStream 分別返回該容器的Stream視圖表示,不同之處在于parallelStream()返回并行的Stream,Stream是Java函數式編程的核心類。 |
spliterator | 可分割的迭代器,不同以往的iterator需要順序迭代,Spliterator可以分割為若干個小的迭代器進行并行操作,可以實現多線程操作提高效率 |
四 (*)List接口
(6個方法)
(進一步細化了單例集合的存儲特征)
(有序,可重復)
List接口特點
List是有序、可重復的容器。
有序:有序(元素存入集合的順序和取出的順序一致)。List中每個元素都有索引標記。可以根據元素的索引標記(在List中的位置)訪問元素,從而精確控制這些元素。
可重復:List允許加入重復的元素。更確切地講,List通常允許滿足 e1.equals(e2) 的元素重復加入容器。
List接口中的常用方法
除了Collection接口中的方法,List多了一些跟順序(索引)有關的方法,參見下表:
方法 | 說明 |
---|---|
void add (int index, Object element) | 在指定位置插入元素,以前元素全部后移一位 |
Object set (int index,Object element) | 修改指定位置的元素 |
Object get (int index) | 返回指定位置的元素 |
Object remove (int index) | 刪除指定位置的元素,后面元素全部前移一位 |
int indexOf (Object o) | 返回第一個匹配元素的索引,如果沒有該元素,返回-1. |
int lastIndexOf (Object o) | 返回最后一個匹配元素的索引,如果沒有該元素,返回-1 |
五(ArrayList)
1 ArrayList容器的基本操作
(Collection里面的方法——>添加,刪除,轉數組,長度,判空,清除)
(是List接口的實現類。是List存儲特征的具體實現)
(底層是用數組實現的——>查詢效率高,增刪效率低,線程不安全)
(數組是有索引的查詢快,但是插入和刪除都要移動后面所有的數據慢)
(建議如果沒有用到Arraylist里面的方法,最好用List接口類型定義引用)
實例化
? 三種定義方式都可以
- 但是ArrayList繼承了List接口和Collection接口,可以用包括自己的所有的方法(缺點就是以后換容器時候代碼會作廢)
- 用List雖然方法少,但是以后換容器的時候不用擔心
- 用Collection定義方法太少,一般不會用
建議如果沒有用到Arraylist里面的方法,最好用List接口類型定義引用
示例
public class ArrayListTest {public static void main(String[] args) {//實例化ArrayList容器List<String> list = new ArrayList<>();//添加元素boolean flag1 = list.add("xiaojia");boolean flag2 = list.add("xiaoliu");boolean flag3 = list.add("jia");boolean flag4 = list.add("jia");System.out.println(flag1+"\t"+flag2+"\t"+flag3+"\t"+flag4);//將ArrayList轉換為數組——>Collextion里面的方法,和數組里面的方法Object[] objects = list.toArray();System.out.println(Arrays.toString(objects));//刪除元素——>可以選擇刪除數據,或者位置boolean flag4 = list.remove("oldlu");System.out.println(flag4);//獲取容器中元素的個數int size = list.size();System.out.println(size);//判斷容器是否為空boolean empty = list.isEmpty();System.out.println(empty);//容器中是否包含指定的元素boolean value = list.contains("itbz");System.out.println(value);//清空容器list.clear();Object[] objects1 = list.toArray();System.out.println(Arrays.toString(objects1));}
}
2 ArrayList容器的索引操作
(6個操作)
(指定位置添加元素,獲取元素,元素替換,刪除指定位置,查找元素在容器(第一次)(最后一次)出現位置)
public class ArrayListTest2 {public static void main(String[] args) {//實例化容器List<String> list = new ArrayList<>();//添加元素list.add("jia");list.add("jiajia");//向指定位置添加元素list.add(0,"xiao");System.out.println("獲取元素");String value1 = list.get(0);System.out.println(value1);System.out.println("獲取所有元素方式一");//使用普通for循環for(int i=0;i<list.size();i++){System.out.println(list.get(i));}System.out.println("獲取所有元素方式二");//使用Foreach循環for(String str:list){System.out.println(str);}System.out.println("元素替換");list.set(1,"kevin");for(String str:list){System.out.println(str);}System.out.println("根據索引位置刪除元素);String value2 = list.remove(1);System.out.println(value2);System.out.println("----------------");for(String str:list){System.out.println(str);}System.out.println("查找元素第一次出現的位置");int value3 = list.indexOf("jjj");System.out.println(value3);System.out.println("查找元素最后一次出現的位置");list.add("jjj");for(String str:list){System.out.println(str);}int value4 = list.lastIndexOf("jjj");System.out.println(value4);}
}
3 ArrayList并集、交集、差集
(Collection里面的3個方法)
(addAll,retainAll,removeAll)
并集
//并集操作:將另一個容器中的元素添加到當前容器中List<String> a = new ArrayList<>();a.add("a");a.add("b");a.add("c");List<String> b = new ArrayList<>();b.add("a");b.add("b");b.add("c");//a并集ba.addAll(b);for(String str :a){System.out.println(str);}
交集
//交集操作:保留相同的,刪除不同的List<String> a1 = new ArrayList<>();a1.add("a");a1.add("b");a1.add("c");List<String> b1 = new ArrayList<>();b1.add("a");b1.add("d");b1.add("e");//交集操作a1.retainAll(b1);for(String str :a1){System.out.println(str);}
差集
//差集操作:保留不同的,刪除相同的List<String> a2 = new ArrayList<>();a2.add("a");a2.add("b");a2.add("c");List<String> b2= new ArrayList<>();b2.add("b");b2.add("c");b2.add("d");a2.removeAll(b2);for(String str :a2){System.out.println(str);}
4 ArrayList源碼分析
數組的初始化和擴容——>最開始數組長度為10,然后以1.5倍擴容,jdk1.8之后在操作數組時才初始化,節省內存
1
2
3
4
六 (Vector)
1 Vector容器的使用
(方法都加了同步檢查——>“線程安全,效率低”)
(方法就增加了synchronized同步標記——>為了多線程的時候線程安全)
(單線程——>串型,多線程——>并型)
(使用容器的時候如果對線程安全有要求,大部分都使用ArrayList)
Vector(向量)的使用
Vector的使用與ArrayList是相同的,因為他們都實現了List接口,對List接口中的抽象方法做了具體實現。
public class VectorTest {public static void main(String[] args) {//實例化VectorList<String> v = new Vector<>();v.add("a");v.add("b");v.add("a");for(int i=0;i<v.size();i++){System.out.println(v.get(i));}System.out.println("----------------------");for(String str:v){System.out.println(str);}}
}
2 Vector源碼分析
(和ArrayList底層都是數組,區別就是線程安全和效率)
(數組初始化方式,ArrayList1.8之后是延遲初始化(調用時初始化),在Vector中是立即初始化,長度也是10)
(ArrayList數組擴容1.5倍,Vector2倍擴容)
成員變量
/*** The array buffer into which the components of the vector are* stored. The capacity of the vector is the length of this array buffer,* and is at least large enough to contain all the vector's elements.** <p>Any array elements following the last element in the Vector are null.** @serial*/
protected Object[] elementData;/*** The number of valid components in this {@code Vector} object.* Components {@code elementData[0]} through* {@code elementData[elementCount-1]} are the actual items.** @serial*/protected int elementCount;/*** The amount by which the capacity of the vector is automatically* incremented when its size becomes greater than its capacity. If* the capacity increment is less than or equal to zero, the capacity* of the vector is doubled each time it needs to grow.** @serial*/protected int capacityIncrement;
構造方法
public Vector() {this(10);
}
添加元素
/*** Appends the specified element to the end of this Vector.** @param e element to be appended to this Vector* @return {@code true} (as specified by {@link Collection#add})* @since 1.2*/
public synchronized boolean add(E e) {modCount++;ensureCapacityHelper(elementCount + 1);elementData[elementCount++] = e;return true;
}
數組擴容
/*** This implements the unsynchronized semantics of ensureCapacity.* Synchronized methods in this class can internally call this* method for ensuring capacity without incurring the cost of an* extra synchronization.** @see #ensureCapacity(int)*/
private void ensureCapacityHelper(int minCapacity) {// overflow-conscious code
//判斷是否需要擴容,數組中的元素個數-數組長度,如果大于0表明需要擴容if (minCapacity - elementData.length > 0)grow(minCapacity);
}
private void grow(int minCapacity) {// overflow-conscious codeint oldCapacity = elementData.length;
//擴容2倍int newCapacity = oldCapacity + ((capacityIncrement > 0) ?capacityIncrement : oldCapacity);if (newCapacity - minCapacity < 0)newCapacity = minCapacity;if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);elementData = Arrays.copyOf(elementData, newCapacity);
}
七 (LinkedList)
1 LinkedList容器介紹
(底層用雙向鏈表實現的存儲——>查詢效率低,增刪效率高,線程不安全)
雙向鏈表也叫雙鏈表,是鏈表的一種,它的每個數據節點中都有兩個指針,分別指向前一個節點和后一個節點。 所以,從雙向鏈表中的任意一個節點開始,都可以很方便地找到所有節點。
每個節點都應該有3部分內容:
class Node<E> {Node<E> previous; //前一個節點E element; //本節點保存的數據Node<E> next; //后一個節點
}
List實現類的選用規則
如何選用ArrayList、LinkedList、Vector?
- 需要線程安全時,用Vector。
- 不存在線程安全問題時,并且查找較多用ArrayList(一般使用它)
- 不存在線程安全問題時,增加或刪除元素較多用LinkedList
2 LinkedList容器的使用
(LinkedList實現了List接口,所以LinkedList是具備List的存儲特征的(有序,元素有重復))
(自己獨有的8個方法)
list標準
public class LinkedListTest {public static void main(String[] args) {//實例化LinkedList容器List<String> list = new LinkedList<>();//添加元素boolean a = list.add("a");boolean b = list.add("b");boolean c = list.add("c");list.add(3,"a");System.out.println(a+"\t"+b+"\t"+c);for(int i=0;i<list.size();i++){System.out.println(list.get(i));}}
}
非list標準——>自己獨有的方法
方法 | 說明 |
---|---|
void addFirst(E e) | 將指定元素插入到開頭 |
void addLast(E e) | 將指定元素插入到結尾 |
getFirst() | 返回此鏈表的第一個元素 |
getLast() | 返回此鏈表的最后一個元素 |
removeFirst() | 移除此鏈表中的第一個元素,并返回這個元素 |
removeLast() | 移除此鏈表中的最后一個元素,并返回這個元素 |
E pop() | 從此鏈表所表示的堆棧處彈出一個元素,等效于removeFirst |
void push(E e) | 將元素推入此鏈表所表示的堆棧 這個等效于addFisrt(E e) |
public class LinkedListTest2 {public static void main(String[] args) {System.out.println("-------LinkedList-------------");//將指定元素插入到鏈表開頭LinkedList<String> linkedList1 = new LinkedList<>();linkedList1.addFirst("a");linkedList1.addFirst("b");linkedList1.addFirst("c");for (String str:linkedList1){System.out.println(str);}System.out.println("----------------------");//將指定元素插入到鏈表結尾LinkedList<String> linkedList = new LinkedList<>();linkedList.addLast("a");linkedList.addLast("b");linkedList.addLast("c");for (String str:linkedList){System.out.println(str);}System.out.println("---------------------------");//返回此鏈表的第一個元素System.out.println(linkedList.getFirst());//返回此鏈表的最后一個元素System.out.println(linkedList.getLast());System.out.println("-----------------------");//移除此鏈表中的第一個元素,并返回這個元素linkedList.removeFirst();//移除此鏈表中的最后一個元素,并返回這個元素linkedList.removeLast();for (String str:linkedList){System.out.println(str);}System.out.println("-----------------------");linkedList.addLast("c");//從此鏈表所表示的堆棧處彈出一個元素,等效于removeFirstlinkedList.pop();for (String str:linkedList){System.out.println(str);}System.out.println("-------------------");//將元素推入此鏈表所表示的堆棧 這個等效于addFisrt(E e)linkedList.push("h");for (String str:linkedList){System.out.println(str);}}
}
3 LinkedList源碼分析
1 添加元素
節點類
private static class Node<E> {E item;Node<E> next;Node<E> prev;Node(Node<E> prev, E element, Node<E> next) {this.item = element;this.next = next;this.prev = prev;}
}
成員變量
transient int size = 0;/*** Pointer to first node.* Invariant: (first == null && last == null) ||* (first.prev == null && first.item != null)*/
transient Node<E> first;/*** Pointer to last node.* Invariant: (first == null && last == null) ||* (last.next == null && last.item != null)*/
transient Node<E> last;
添加元素
/*** Appends the specified element to the end of this list.** <p>This method is equivalent to {@link #addLast}.** @param e element to be appended to this list* @return {@code true} (as specified by {@link Collection#add})*/
public boolean add(E e) {linkLast(e);return true;
}/*** Links e as last element.*/
void linkLast(E e) {final Node<E> l = last;final Node<E> newNode = new Node<>(l, e, null);last = newNode;if (l == null)first = newNode;elsel.next = newNode;size++;modCount++;
}//創建新節點,頭尾指針指向
2 頭尾添加元素
addFirst
/*** Inserts the specified element at the beginning of this list.** @param e the element to add*/
public void addFirst(E e) {linkFirst(e);
}/*** Links e as first element.*/
private void linkFirst(E e) {final Node<E> f = first;final Node<E> newNode = new Node<>(null, e, f);first = newNode;if (f == null)last = newNode;elsef.prev = newNode;size++;modCount++;
}
addLast
/*** Appends the specified element to the end of this list.** <p>This method is equivalent to {@link #add}.** @param e the element to add*/
public void addLast(E e) {linkLast(e);
}/*** Links e as last element.*/
void linkLast(E e) {final Node<E> l = last;final Node<E> newNode = new Node<>(l, e, null);last = newNode;if (l == null)first = newNode;elsel.next = newNode;size++;modCount++;
}
3 獲取元素——>返回的不是對象是元素
/*** Returns the element at the specified position in this list.** @param index index of the element to return* @return the element at the specified position in this list* @throws IndexOutOfBoundsException {@inheritDoc}*/
public E get(int index) {checkElementIndex(index);return node(index).item;
}
private void checkElementIndex(int index) {if (!isElementIndex(index))throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
/*** Tells if the argument is the index of an existing element.*/
private boolean isElementIndex(int index) {return index >= 0 && index < size;
}
/*** Returns the (non-null) Node at the specified element index.*/
Node<E> node(int index) {// assert isElementIndex(index);if (index < (size >> 1)) { //增加查詢效率Node<E> x = first;for (int i = 0; i < index; i++)x = x.next;return x;} else {Node<E> x = last;for (int i = size - 1; i > index; i--)x = x.prev;return x;}
}