日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

15、java中的集合(2)

發布時間:2023/12/3 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 15、java中的集合(2) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?????? 說一下單列集合,java中的單列集合的頂級接口是Collection,它有兩個子接口:List、Set,本篇介紹一下List接口及其實現類的功能方法和基本實現原理。

?????? List集合是有序集合,這里的有序并不是指存入List集合的元素會被自動排序,而是指數據存儲和數據讀取的順序是一樣的,就是說按著1,2,3的順序依次添加元素后,遍歷集合時元素也會按著1,2,3的順序進行打印。List集合不會對添加的元素進行排序,并且允許存儲相同的元素。實現List接口的實現類有:ArrayList、LinkedList、Vector、Stack等,接下來詳細的介紹一下一般常用的幾個List集合。

?????? 先說一下ArrayList,ArrayList集合的底層數據結構是數組,其實ArrayList就是依靠數組進行存儲數據,但是這個數組可以進行擴容操作,所以才顯得List集合的容量大小可變,正因為依賴數組,所以ArrayList集合具有查詢快、增刪慢的特點,另外ArrayList的底層實現并未用到同步機制,所以是線程不安全的集合,效率高。

???????? ArrayList集合中的功能方法在這里就不做介紹了,按著API測試即可,挺簡單的。這里說一下它的底層實現,底層代碼及分析如下:

//實現了RandomAccess接口說明可以快速隨機訪問 //實現了Cloneable接口說明可以被克隆 //實現了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底層維護的那個數組,用于存儲數據用//elementData 不參與序列化,因為他本質上只是一個地址,而并不是數據transient Object[] elementData;//就是聲明一個空數組,當實例化一個容量大小為0的ArrayList時就用EMPTY_ELEMENTDATA實例化elementDataprivate static final Object[] EMPTY_ELEMENTDATA = {};//當使用空構造方法創建一個集合實例時,elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATAprivate static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//記錄集合的大小private int size;//集合容量的最大值private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;/**********************************************構造方法****************************************************************///實例化一個容量為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;}//根據一個集合來實例化一個集合,順序不變public ArrayList(Collection<? extends E> c) {elementData = c.toArray();if ((size = elementData.length) != 0) {if (elementData.getClass() != Object[].class)//類型不是Object[].class則復制一個elementData = Arrays.copyOf(elementData, size, Object[].class);} else {this.elementData = EMPTY_ELEMENTDATA;}}//類似于字符串的trim操作,就是將當前集合的容量縮減為這個集合實際含有有效元素的長度大小//正因為這個方法才使得集合的大小就是元素的個數public void trimToSize() {//modCount 是AbstractList中的屬性,表示集合在結構上修改的次數。就是集合大小修改的次數//主要作用在于迭代集合元素時判斷是否發生并發修改異常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) {//如果是剛進來初始化的話,就將數組的長度設置成默認長度10if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);}//如果集合容量已經比10大了,則檢查是否需要擴容ensureExplicitCapacity(minCapacity);}//容量檢查private void ensureExplicitCapacity(int minCapacity) {modCount++;// 如果 底層數組的長度<要填加元素到數組的下標 則進行擴容if (minCapacity - elementData.length > 0)//進行具體的擴容操作grow(minCapacity);}//擴容機制private void grow(int minCapacity) {// overflow-conscious code//先獲取到當前數組的長度int oldCapacity = elementData.length;//新數組的長度 = 原來數組的長度 + 原來數組長度的一半 也就是傳說中的擴容1.5倍int newCapacity = oldCapacity + (oldCapacity >> 1);//如果擴容1.5倍后還是不足以添加新的元素,則將新數組的容量設置成將要添加的數組下標if (newCapacity - minCapacity < 0)newCapacity = minCapacity;//判斷一下新的數組大小是否比集合的最大容量要大 if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);// 創建一個新數組,長度大小為newCapacity ,然后將原來的數組中的元素復制到新數組中// 在外界看來就像是數組擴容了,其實就是新創建了一個長度更大的數組而已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;}/**********************************************刪除元素****************************************************************///刪除下標index處的元素public E remove(int index) {rangeCheck(index);//下標合法性檢查modCount++;//獲取都指定下標處的元素用于返回E oldValue = elementData(index);int numMoved = size - index - 1;if (numMoved > 0)//調用c或c++的方法將刪除元素后的所有元素往前移一位System.arraycopy(elementData, index+1, elementData, index, numMoved);elementData[--size] = null;return oldValue;}//如果刪除的元素下標>集合容量,報錯private void rangeCheck(int index) {if (index >= size)throw new IndexOutOfBoundsException(outOfBoundsMsg(index));}/**********************************獲取指定元素在集合中第一次出現的下標*****************************************************/ public int indexOf(Object o) {if (o == null) {for (int i = 0; i < size; i++)if (elementData[i]==null)return i;} else {//就是循環遍歷底層數組,只要碰到相同指定元素值的元素直接返回下標結束程序for (int i = 0; i < size; i++)if (o.equals(elementData[i]))return i;}return -1;}/**********************************獲取指定下標處的元素*****************************************************/ public E get(int index) {rangeCheck(index);//下標合法性校驗//返回元素return elementData(index);}/**********************************序列化和反序列化*****************************************************/ //序列化private void writeObject(ObjectOutputStream s) throws java.io.IOException{int expectedModCount = modCount;s.defaultWriteObject();s.writeInt(size);//將數組的每一個元素進行寫入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();}//使用內部類實現Iterator接口的方式實現迭代器private class Itr implements Iterator<E> {// 下一個元素的下標int cursor;int lastRet = -1;int expectedModCount = modCount;//集合修改次數//是否還有下一個元素public boolean hasNext() {return cursor != size;}//獲取下一個節點元素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];}//判斷是否發生并發修改異常,使用迭代器遍歷集合時,不能修改集合,然可以使用迭代器修改集合final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}} }

?????? 再說一下另一個常用的集合LinkedList,它的底層數據結構是鏈表,依賴一個雙向鏈表存儲數據,具有增刪快、查詢慢的特點,代碼分析如下:

//雙向鏈表 //實現了Deque接口說明具有雙向隊列的特性 //實現了Cloneable接口說明可以被克隆 //實現了Serializable接口說明可以被序列化 public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable {//用一個靜態內部來聲明節點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;//頭結點transient Node<E> first;//尾節點transient Node<E> last;//構造一個集合public LinkedList() {}//根據已有集合構建一個LinkedList集合public LinkedList(Collection<? extends E> c) {this();addAll(c);}/**********************************************添加元素****************************************************************/ //直接用addAll(c)這個方法為例看public boolean addAll(Collection<? extends E> c) {return addAll(size, c);}//相當于添加多個結點public boolean addAll(int index, Collection<? extends E> c) {//index = size//檢查傳入的集合大小是否合理checkPositionIndex(index);Object[] a = c.toArray();//集合轉成數組int numNew = a.length;//獲取數組長度if (numNew == 0)//如果長度是0,沒有元素,直接返回return false;//聲明兩個結點,前驅結點pred 、 后繼結點succNode<E> pred, succ;if (index == size) {//這里true,index表示在哪里開始添加,如果添加下標和鏈表大小相同,則就是在末尾添加succ = null;//后繼節點為空pred = last;//前驅節點指向鏈表中的最后一個結點} else {succ = node(index);//獲取到index位置的結點,succ指向此節點,也就是新建結點的后繼結點指向index位置的結點pred = succ.prev;//新建結點的前驅結點指向index位置結點的原來的前一個結點結點}for (Object o : a) {@SuppressWarnings("unchecked") E e = (E) o;Node<E> newNode = new Node<>(pred, e, null);//利用上面的前驅和后繼結點創建一個結點,添加到鏈表if (pred == null)//前驅結點是null,鏈表中沒結點first = newNode;//則初始化頭節點elsepred.next = newNode;//讓pred的后繼結點指向新添加的節點,也就是index位置原先的前一個結點的后繼結點指向新節點,大白話就是在index位置和index-1位置直接新添加一個結點pred = newNode;//然后讓pred節點指向新添加的節點}if (succ == null) {//如果后繼結點是空,初始化尾節點last = pred;} else {pred.next = succ;//pred的下一個節點指向succsucc.prev = pred;//succ的上一個節點指向pred}size += numNew;//鏈表中元素個數modCount++;//鏈表改變標記+1return true;}} /**********************************************獲取元素****************************************************************/ //獲取到index處的結點 public E get(int index) {checkElementIndex(index);return node(index).item;}//獲取元素時,需要挨個遍歷,所以查詢效率低。 Node<E> node(int index) {if (index < (size >> 1)) {//如果結點所在的位置在鏈表的前半段,則從前往后遍歷//一直將指針指到index處,獲取到結點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;}}/**********************************************刪除元素****************************************************************/ //確定刪除元素的結點public E remove(int index) {checkElementIndex(index);return unlink(node(index));}//根據結點中的前驅結點和后繼結點刪除(鏈表刪除結點操作)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;}/**********************************************存在元素****************************************************************/ //就是遍歷集合,然后依次比較返回相同元素所在的下標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循環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循環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集合底層封裝的都是數組或者鏈表,對數據結構有點了解的話很容易就理解了。

總結

以上是生活随笔為你收集整理的15、java中的集合(2)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。