15、java中的集合(2)
?????? 說一下單列集合,java中的單列集合的頂級接口是Collection,它有兩個子接口:List、Set,本篇介紹一下List接口及其實現(xiàn)類的功能方法和基本實現(xiàn)原理。
?????? List集合是有序集合,這里的有序并不是指存入List集合的元素會被自動排序,而是指數(shù)據(jù)存儲和數(shù)據(jù)讀取的順序是一樣的,就是說按著1,2,3的順序依次添加元素后,遍歷集合時元素也會按著1,2,3的順序進行打印。List集合不會對添加的元素進行排序,并且允許存儲相同的元素。實現(xiàn)List接口的實現(xiàn)類有:ArrayList、LinkedList、Vector、Stack等,接下來詳細的介紹一下一般常用的幾個List集合。
?????? 先說一下ArrayList,ArrayList集合的底層數(shù)據(jù)結(jié)構(gòu)是數(shù)組,其實ArrayList就是依靠數(shù)組進行存儲數(shù)據(jù),但是這個數(shù)組可以進行擴容操作,所以才顯得List集合的容量大小可變,正因為依賴數(shù)組,所以ArrayList集合具有查詢快、增刪慢的特點,另外ArrayList的底層實現(xiàn)并未用到同步機制,所以是線程不安全的集合,效率高。
???????? ArrayList集合中的功能方法在這里就不做介紹了,按著API測試即可,挺簡單的。這里說一下它的底層實現(xiàn),底層代碼及分析如下:
//實現(xiàn)了RandomAccess接口說明可以快速隨機訪問 //實現(xiàn)了Cloneable接口說明可以被克隆 //實現(xiàn)了Serializable接口說明可以被序列化 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { /**********************************************成員變量****************************************************************///capacity 默認容量private static final int DEFAULT_CAPACITY = 10;//這個就是ArrayList底層維護的那個數(shù)組,用于存儲數(shù)據(jù)用//elementData 不參與序列化,因為他本質(zhì)上只是一個地址,而并不是數(shù)據(jù)transient Object[] elementData;//就是聲明一個空數(shù)組,當(dāng)實例化一個容量大小為0的ArrayList時就用EMPTY_ELEMENTDATA實例化elementDataprivate static final Object[] EMPTY_ELEMENTDATA = {};//當(dāng)使用空構(gòu)造方法創(chuàng)建一個集合實例時,elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATAprivate static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//記錄集合的大小private int size;//集合容量的最大值private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;/**********************************************構(gòu)造方法****************************************************************///實例化一個容量為initialCapacity的集合public ArrayList(int initialCapacity) {if (initialCapacity > 0) {this.elementData = new Object[initialCapacity];} else if (initialCapacity == 0) {this.elementData = EMPTY_ELEMENTDATA;} else {throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);}}//實例化一個空的集合public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}//根據(jù)一個集合來實例化一個集合,順序不變public ArrayList(Collection<? extends E> c) {elementData = c.toArray();if ((size = elementData.length) != 0) {if (elementData.getClass() != Object[].class)//類型不是Object[].class則復(fù)制一個elementData = Arrays.copyOf(elementData, size, Object[].class);} else {this.elementData = EMPTY_ELEMENTDATA;}}//類似于字符串的trim操作,就是將當(dāng)前集合的容量縮減為這個集合實際含有有效元素的長度大小//正因為這個方法才使得集合的大小就是元素的個數(shù)public void trimToSize() {//modCount 是AbstractList中的屬性,表示集合在結(jié)構(gòu)上修改的次數(shù)。就是集合大小修改的次數(shù)//主要作用在于迭代集合元素時判斷是否發(fā)生并發(fā)修改異常modCount++;if (size < elementData.length) {elementData = (size == 0)? EMPTY_ELEMENTDATA: Arrays.copyOf(elementData, size);}}/**********************************************添加元素****************************************************************///在集合的尾部添加一個元素public boolean add(E e) {//擴容ensureCapacityInternal(size + 1);elementData[size++] = e;//在尾部添加元素,并將集合的大小加一return true;}//容量檢查private void ensureCapacityInternal(int minCapacity) {//如果是剛進來初始化的話,就將數(shù)組的長度設(shè)置成默認長度10if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);}//如果集合容量已經(jīng)比10大了,則檢查是否需要擴容ensureExplicitCapacity(minCapacity);}//容量檢查private void ensureExplicitCapacity(int minCapacity) {modCount++;// 如果 底層數(shù)組的長度<要填加元素到數(shù)組的下標(biāo) 則進行擴容if (minCapacity - elementData.length > 0)//進行具體的擴容操作grow(minCapacity);}//擴容機制private void grow(int minCapacity) {// overflow-conscious code//先獲取到當(dāng)前數(shù)組的長度int oldCapacity = elementData.length;//新數(shù)組的長度 = 原來數(shù)組的長度 + 原來數(shù)組長度的一半 也就是傳說中的擴容1.5倍int newCapacity = oldCapacity + (oldCapacity >> 1);//如果擴容1.5倍后還是不足以添加新的元素,則將新數(shù)組的容量設(shè)置成將要添加的數(shù)組下標(biāo)if (newCapacity - minCapacity < 0)newCapacity = minCapacity;//判斷一下新的數(shù)組大小是否比集合的最大容量要大 if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);// 創(chuàng)建一個新數(shù)組,長度大小為newCapacity ,然后將原來的數(shù)組中的元素復(fù)制到新數(shù)組中// 在外界看來就像是數(shù)組擴容了,其實就是新創(chuàng)建了一個長度更大的數(shù)組而已elementData = Arrays.copyOf(elementData, newCapacity);}private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflowthrow new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;}/**********************************************刪除元素****************************************************************///刪除下標(biāo)index處的元素public E remove(int index) {rangeCheck(index);//下標(biāo)合法性檢查modCount++;//獲取都指定下標(biāo)處的元素用于返回E oldValue = elementData(index);int numMoved = size - index - 1;if (numMoved > 0)//調(diào)用c或c++的方法將刪除元素后的所有元素往前移一位System.arraycopy(elementData, index+1, elementData, index, numMoved);elementData[--size] = null;return oldValue;}//如果刪除的元素下標(biāo)>集合容量,報錯private void rangeCheck(int index) {if (index >= size)throw new IndexOutOfBoundsException(outOfBoundsMsg(index));}/**********************************獲取指定元素在集合中第一次出現(xiàn)的下標(biāo)*****************************************************/ public int indexOf(Object o) {if (o == null) {for (int i = 0; i < size; i++)if (elementData[i]==null)return i;} else {//就是循環(huán)遍歷底層數(shù)組,只要碰到相同指定元素值的元素直接返回下標(biāo)結(jié)束程序for (int i = 0; i < size; i++)if (o.equals(elementData[i]))return i;}return -1;}/**********************************獲取指定下標(biāo)處的元素*****************************************************/ public E get(int index) {rangeCheck(index);//下標(biāo)合法性校驗//返回元素return elementData(index);}/**********************************序列化和反序列化*****************************************************/ //序列化private void writeObject(ObjectOutputStream s) throws java.io.IOException{int expectedModCount = modCount;s.defaultWriteObject();s.writeInt(size);//將數(shù)組的每一個元素進行寫入for (int i=0; i<size; i++) {s.writeObject(elementData[i]);}}//反序列化private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {elementData = EMPTY_ELEMENTDATA;s.defaultReadObject();s.readInt();if (size > 0) {ensureCapacityInternal(size);Object[] a = elementData;for (int i=0; i<size; i++) {a[i] = s.readObject();}}}/**********************************迭代集合*****************************************************/ public Iterator<E> iterator() {return new Itr();}//使用內(nèi)部類實現(xiàn)Iterator接口的方式實現(xiàn)迭代器private class Itr implements Iterator<E> {// 下一個元素的下標(biāo)int cursor;int lastRet = -1;int expectedModCount = modCount;//集合修改次數(shù)//是否還有下一個元素public boolean hasNext() {return cursor != size;}//獲取下一個節(jié)點元素public E next() {checkForComodification();int i = cursor;if (i >= size)throw new NoSuchElementException();Object[] elementData = ArrayList.this.elementData;if (i >= elementData.length)throw new ConcurrentModificationException();cursor = i + 1;return (E) elementData[lastRet = i];}//判斷是否發(fā)生并發(fā)修改異常,使用迭代器遍歷集合時,不能修改集合,然可以使用迭代器修改集合final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}} }?????? 再說一下另一個常用的集合LinkedList,它的底層數(shù)據(jù)結(jié)構(gòu)是鏈表,依賴一個雙向鏈表存儲數(shù)據(jù),具有增刪快、查詢慢的特點,代碼分析如下:
//雙向鏈表 //實現(xiàn)了Deque接口說明具有雙向隊列的特性 //實現(xiàn)了Cloneable接口說明可以被克隆 //實現(xiàn)了Serializable接口說明可以被序列化 public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable {//用一個靜態(tài)內(nèi)部來聲明節(jié)點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;//頭結(jié)點transient Node<E> first;//尾節(jié)點transient Node<E> last;//構(gòu)造一個集合public LinkedList() {}//根據(jù)已有集合構(gòu)建一個LinkedList集合public LinkedList(Collection<? extends E> c) {this();addAll(c);}/**********************************************添加元素****************************************************************/ //直接用addAll(c)這個方法為例看public boolean addAll(Collection<? extends E> c) {return addAll(size, c);}//相當(dāng)于添加多個結(jié)點public boolean addAll(int index, Collection<? extends E> c) {//index = size//檢查傳入的集合大小是否合理checkPositionIndex(index);Object[] a = c.toArray();//集合轉(zhuǎn)成數(shù)組int numNew = a.length;//獲取數(shù)組長度if (numNew == 0)//如果長度是0,沒有元素,直接返回return false;//聲明兩個結(jié)點,前驅(qū)結(jié)點pred 、 后繼結(jié)點succNode<E> pred, succ;if (index == size) {//這里true,index表示在哪里開始添加,如果添加下標(biāo)和鏈表大小相同,則就是在末尾添加succ = null;//后繼節(jié)點為空pred = last;//前驅(qū)節(jié)點指向鏈表中的最后一個結(jié)點} else {succ = node(index);//獲取到index位置的結(jié)點,succ指向此節(jié)點,也就是新建結(jié)點的后繼結(jié)點指向index位置的結(jié)點pred = succ.prev;//新建結(jié)點的前驅(qū)結(jié)點指向index位置結(jié)點的原來的前一個結(jié)點結(jié)點}for (Object o : a) {@SuppressWarnings("unchecked") E e = (E) o;Node<E> newNode = new Node<>(pred, e, null);//利用上面的前驅(qū)和后繼結(jié)點創(chuàng)建一個結(jié)點,添加到鏈表if (pred == null)//前驅(qū)結(jié)點是null,鏈表中沒結(jié)點first = newNode;//則初始化頭節(jié)點elsepred.next = newNode;//讓pred的后繼結(jié)點指向新添加的節(jié)點,也就是index位置原先的前一個結(jié)點的后繼結(jié)點指向新節(jié)點,大白話就是在index位置和index-1位置直接新添加一個結(jié)點pred = newNode;//然后讓pred節(jié)點指向新添加的節(jié)點}if (succ == null) {//如果后繼結(jié)點是空,初始化尾節(jié)點last = pred;} else {pred.next = succ;//pred的下一個節(jié)點指向succsucc.prev = pred;//succ的上一個節(jié)點指向pred}size += numNew;//鏈表中元素個數(shù)modCount++;//鏈表改變標(biāo)記+1return true;}} /**********************************************獲取元素****************************************************************/ //獲取到index處的結(jié)點 public E get(int index) {checkElementIndex(index);return node(index).item;}//獲取元素時,需要挨個遍歷,所以查詢效率低。 Node<E> node(int index) {if (index < (size >> 1)) {//如果結(jié)點所在的位置在鏈表的前半段,則從前往后遍歷//一直將指針指到index處,獲取到結(jié)點Node<E> x = first;for (int i = 0; i < index; i++)x = x.next;return x;} else {//結(jié)點所在的位置在鏈表的后半段,則從后往前遍歷Node<E> x = last;for (int i = size - 1; i > index; i--)x = x.prev;return x;}}/**********************************************刪除元素****************************************************************/ //確定刪除元素的結(jié)點public E remove(int index) {checkElementIndex(index);return unlink(node(index));}//根據(jù)結(jié)點中的前驅(qū)結(jié)點和后繼結(jié)點刪除(鏈表刪除結(jié)點操作)E unlink(Node<E> x) {// assert x != null;final E element = x.item;final Node<E> next = x.next;final Node<E> prev = x.prev;if (prev == null) {first = next;} else {prev.next = next;x.prev = null;}if (next == null) {last = prev;} else {next.prev = prev;x.next = null;}x.item = null;size--;modCount++;return element;}/**********************************************存在元素****************************************************************/ //就是遍歷集合,然后依次比較返回相同元素所在的下標(biāo)public int indexOf(Object o) {int index = 0;if (o == null) {for (Node<E> x = first; x != null; x = x.next) {if (x.item == null)return index;index++;}} else {for (Node<E> x = first; x != null; x = x.next) {if (o.equals(x.item))return index;index++;}}return -1;}}再說一下這兩個集合的遍歷方式,代碼如下:
1)for循環(huán)ArrayList<String> list = new ArrayList<>();list.add("1111");list.add("2222");list.add("3333");for (int i = 0; i < list.size(); i++) {System.out.println(list.get(i));} 2)迭代器ArrayList<String> list = new ArrayList<>();list.add("1111");list.add("2222");list.add("3333");Iterator<String> iterator = list.iterator();while(iterator.hasNext()) {System.out.println(iterator.next());} 3)增強forArrayList<String> list = new ArrayList<>();list.add("1111");list.add("2222");list.add("3333");for (String string : list) {System.out.println(string);}---------------------------------------------------------------------------1)for循環(huán)LinkedList<String> list = new LinkedList<>();list.add("1111");list.add("2222");list.add("3333");for (int i = 0; i < list.size(); i++) {System.out.println(list.get(i));}2)迭代器LinkedList<String> list = new LinkedList<>();list.add("1111");list.add("2222");list.add("3333");Iterator<String> iterator = list.iterator();while(iterator.hasNext()) {System.out.println(iterator.next());}3)增強forLinkedList<String> list = new LinkedList<>();list.add("1111");list.add("2222");list.add("3333");for (String string : list) {System.out.println(string);}就先說一下這兩個,其實List集合底層封裝的都是數(shù)組或者鏈表,對數(shù)據(jù)結(jié)構(gòu)有點了解的話很容易就理解了。
總結(jié)
以上是生活随笔為你收集整理的15、java中的集合(2)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 高配置电脑清单表五千块钱(高配置电脑清单
- 下一篇: 16、java中的集合(3)