transient关键字的作用_ArrayList Vector (transient关键字)--JAVA成长之路
集合是Java中非常重要而且基礎的內容,因為任何數據必不可少的就是數據的存儲。集合的作用就是以一定的方式組織、存儲數據。下面說說ArrayList,只撿干貨聊。
ArrayList特點
1、ArrayList是基于數組實現的,是一個動態數組,其容量能自動增長,類似于C語言中的動態申請內存,動態增長內存。
2、ArrayList不是線程安全的,只能用在單線程環境下,多線程環境下可以考慮用Collections.synchronizedList(List l),函數返回一個線程安全的ArrayList類,底層方法內使用synchronized同步塊進行控制;也可以使用concurrent并發包下的CopyOnWriteArrayList類,底層數組直接使用關鍵字volatile。Vector則在操作數組的方法上加上了關鍵字synchronized。
3、ArrayList實現了Serializable接口,因此它支持序列化,能夠通過序列化傳輸,實現了RandomAccess接口,支持快速隨機訪問,實際上就是通過下標序號進行快速訪問,實現了Cloneable接口,能被克隆。
4、每個ArrayList實例都有一個容量,該容量是指用來存儲列表元素的數組的大小。它總是至少等于列表的大小。隨著向ArrayList中不斷添加元素, 其容量也自動增長。自動增長會帶來數據向新數組的重新拷貝,因此,如果可預知數據量的多少,可在構造ArrayList時指定其容量。在添加大量元素前, 應用程序也可以使用ensureCapacity操作來增加ArrayList實例的容量,這可以減少遞增式再分配的數量。
注意,此實現不是同步的。如果多個線程同時訪問一個ArrayList實例,而其中至少一個線程從結構上修改了列表,那么它必須保持外部同步。
ArrayList底層
對于ArrayList而言,它實現List接口、底層使用數組保存所有元素。其操作基本上是對數組的操作。下面我們來分析ArrayList的源代碼:
1) 私有屬性:
ArrayList定義只定義類兩個私有屬性:
?????/**???????*?The?array?buffer?into?which?the?elements?of?the?ArrayList?are?stored.???????*?The?capacity?of?the?ArrayList?is?the?length?of?this?array?buffer.???????*/?private?transient?Object[]?elementData;??????????/**???????*?The?size?of?the?ArrayList?(the?number?of?elements?it?contains).???????*???????*?@serial???????*/?private?int?size;很容易理解,elementData存儲ArrayList內的元素(應該是堆內存中元素的引用,而不是實際的元素 ),size表示它包含的元素的數量。
有個關鍵字需要解釋:transient。
Java的serialization提供了一種持久化對象實例的機制。當持久化對象時,可能有一個特殊的對象數據成員,我們不想用serialization機制來保存它。為了在一個特定對象的一個域上關閉serialization,可以在這個域前加上關鍵字transient。
有點抽象,看個例子應該能明白。
public?class?UserInfo?implements?Serializable?{???????private?static?final?long?serialVersionUID?=?996890129747019948L;???????private?String?name;???????private?transient?String?psw;??????????public?UserInfo(String?name,?String?psw)?{???????????this.name?=?name;???????????this.psw?=?psw;???????}??????????public?String?toString()?{???????????return?"name="?+?name?+?",?psw="?+?psw;???????}???}??????public?class?TestTransient?{???????public?static?void?main(String[]?args)?{???????????UserInfo?userInfo?=?new?UserInfo("張三",?"123456");???????????System.out.println(userInfo);???????????try?{???????????????//?序列化,被設置為transient的屬性沒有被序列化???????????????ObjectOutputStream?o?=?new?ObjectOutputStream(new?FileOutputStream(???????????????????????"UserInfo.out"));???????????????o.writeObject(userInfo);???????????????o.close();???????????}?catch?(Exception?e)?{???????????????//?TODO:?handle?exception???????????????e.printStackTrace();???????????}???????????try?{???????????????//?重新讀取內容???????????????ObjectInputStream?in?=?new?ObjectInputStream(new?FileInputStream(???????????????????????"UserInfo.out"));???????????????UserInfo?readUserInfo?=?(UserInfo)?in.readObject();???????????????//讀取后psw的內容為null???????????????System.out.println(readUserInfo.toString());???????????}?catch?(Exception?e)?{???????????????//?TODO:?handle?exception???????????????e.printStackTrace();???????????}???????}???}被標記為transient的屬性在對象被序列化的時候不會被保存。ntData數組被序列化。這是為什么?因為序列化ArrayList的時候,ArrayList里面的elementData未必是滿的,比方說elementData有10的大小,但是我只用了其中的3個,那么是否有必要序列化整個elementData呢?顯然沒有這個必要,因此ArrayList中重寫了writeObject方法:
????private?void?writeObject(java.io.ObjectOutputStream?s)????????????throws?java.io.IOException{????????//?Write?out?element?count,?and?any?hidden?stuff????????int?expectedModCount?=?modCount;????????s.defaultWriteObject();????????//?Write?out?array?length????????s.writeInt(elementData.length);????????//?Write?out?all?elements?in?the?proper?order.????????for?(int?i=0;?i每次序列化的時候調用這個方法,先調用defaultWriteObject()方法序列化ArrayList中的非transient元素,elementData不去序列化它,然后遍歷elementData,只序列化那些有的元素,這樣:
加快了序列化的速度
減小了序列化之后的文件大小
這種做法也是值得學習、借鑒的一種思路。接著回到ArrayList的分析中......
2) 構造方法:
ArrayList提供了三種方式的構造器,可以構造一個默認初始容量為10的空列表、構造一個指定初始容量的空列表以及構造一個包含指定collection的元素的列表,這些元素按照該collection的迭代器返回它們的順序排列的。
3) 元素存儲與擴容:
ArrayList 提供了set(int index, E element)、add(E e)、add(int index, E element)、 addAll(Collection extends E> c)、 addAll(int index, Collection extends E> c)這些添加元素的方法。
20?//?用指定的元素替代此列表中指定位置上的元素,并返回以前位于該位置上的元素。21?public?E?set(int?index,?E?element)?{??22????RangeCheck(index);??23?24????E?oldValue?=?(E)?elementData[index];??25????elementData[index]?=?element;??26????return?oldValue;??27?}????28?//?將指定的元素添加到此列表的尾部。29?public?boolean?add(E?e)?{??30????ensureCapacity(size?+?1);???31????elementData[size++]?=?e;??32????return?true;??33?}????34?//?將指定的元素插入此列表中的指定位置。35?//?如果當前位置有元素,則向右移動當前位于該位置的元素以及所有后續元素(將其索引加1)。36?public?void?add(int?index,?E?element)?{??37????if?(index?>?size?||?index??c)?{??50????Object[]?a?=?c.toArray();??51????int?numNew?=?a.length;??52????ensureCapacity(size?+?numNew);?//?Increments?modCount??53????System.arraycopy(a,?0,?elementData,?size,?numNew);??54????size?+=?numNew;??55????return?numNew?!=?0;??56?}????57?//?從指定的位置開始,將指定collection中的所有元素插入到此列表中。58?public?boolean?addAll(int?index,?Collection?extends?E>?c)?{??59????if?(index?>?size?||?index??0)??69??????System.arraycopy(elementData,?index,?elementData,?index?+?numNew,?numMoved);??70?71????System.arraycopy(a,?0,?elementData,?index,?numNew);??72????size?+=?numNew;??73????return?numNew?!=?0;?????}底層數組的大小不夠了怎么辦?答案就是擴容,這也就是為什么一直說ArrayList的底層是基于動態數組實現的原因,動態數組的意思就是指底層的數組大小并不是固定的,而是根據添加的元素大小進行一個判斷,不夠的話就動態擴容,擴容的代碼就在ensureCapacity里面:
?public?void?ensureCapacity(int?minCapacity)?{????modCount++;????int?oldCapacity?=?elementData.length;????if?(minCapacity?>?oldCapacity)?{????????Object?oldData[]?=?elementData;????????int?newCapacity?=?(oldCapacity?*?3)/2?+?1;????????????if?(newCapacity?看到擴容的時候把元素組大小先乘以3,再除以2,最后加1??赡苡行┤艘獑枮槭裁?#xff1f;我們可以想:
如果一次性擴容擴得太大,必然造成內存空間的浪費
如果一次性擴容擴得不夠,那么下一次擴容的操作必然比較快地會到來,這會降低程序運行效率,要知道擴容還是比價耗費性能的一個操作
所以擴容擴多少,是JDK開發人員在時間、空間上做的一個權衡,提供出來的一個比較合理的數值。最后調用到的是Arrays的copyOf方法,將元素組里面的內容復制到新的數組里面去:
????public?static??T[]?copyOf(U[]?original,?int?newLength,?Class?extends?T[]>?newType)?{???????????T[]?copy?=?((Object)newType?==?(Object)Object[].class)?????????????????(T[])?new?Object[newLength]???????????????:?(T[])?Array.newInstance(newType.getComponentType(),?newLength);???????????System.arraycopy(original,?0,?copy,?0,????????????????????????????Math.min(original.length,?newLength));???????????return?copy;????}數組復制拷貝盡量使用System.arrayCopy或Arrays.copyof()方法,效率更高。
ArrayList還給我們提供了將底層數組的容量調整為當前列表保存的實際元素的大小的功能。它可以通過trimToSize方法來實現。代碼如下:
public?void?trimToSize()?{??????modCount++;??????int?oldCapacity?=?elementData.length;??????if?(size?由于elementData的長度會被拓展,size標記的是其中包含的元素的個數。所以會出現size很小但elementData.length很大的情況,將出現空間的浪費。trimToSize將返回一個新的數組給elementData,元素內容保持不變,length和size相同,節省空間。
4) 元素讀取:
//?返回此列表中指定位置上的元素。public?E?get(int?index)?{??????RangeCheck(index);??????return?(E)?elementData[index];??}5) 元素刪除:
ArrayList提供了根據下標或者指定對象兩種方式的刪除功能。對于ArrayList來說,這兩種刪除的方法差不多,都是調用的下面一段代碼:
????int?numMoved?=?size?-?index?-?1;????if?(numMoved?>?0)????????System.arraycopy(elementData,?index+1,?elementData,?index,?????????????????numMoved);????elementData[--size]?=?null;?//?Let?gc?do?its?work把指定元素后面位置的所有元素,利用System.arraycopy方法整體向前移動一個位置,最后一個位置的元素指定為null,這樣讓gc可以去回收它。
ArrayList的優缺點
隨機訪問快。ArrayList底層以數組實現,是一種隨機訪問模式,再加上它實現了RandomAccess接口,因此查找也就是get的時候非???/p>
順序添加快。ArrayList在順序添加一個元素的時候非常方便,只是往數組里面添加了一個元素而已
刪除元素的時候,涉及到一次元素復制,如果要復制的元素很多,那么就會比較耗費性能
插入元素的時候,涉及到一次元素復制,如果要復制的元素很多,那么就會比較耗費性能
ArrayList和Vector區別
Vector屬于線程安全級別的,但是大多數情況下不使用Vector,因為線程安全需要更大的系統開銷。ArrayList需要使用Collections.synchronizedList方法變成一個線程安全的List。
????List?synchronizedList?=?Collections.synchronizedList(list);????synchronizedList.add("aaa");????synchronizedList.add("bbb");????for?(int?i?=?0;?i?Vector是ArrayList的線程安全版本,其實現90%和ArrayList都完全一樣
增長因子不同。ArrayList在內存不夠時默認是擴展50% + 1個,Vector是默認擴展1倍。
Vector提供indexOf(obj, start)接口,ArrayList沒有。
ArrayList轉靜態數組toArray
有兩個轉化為靜態數組的toArray方法。
第一個,調用Arrays.copyOf將返回一個數組,數組內容是size個elementData的元素,即拷貝elementData從0至size-1位置的元素到新數組并返回。
public?Object[]?toArray()?{???????return?Arrays.copyOf(elementData,?size);??}第二個,如果傳入數組的長度小于size,返回一個新的數組,大小為size,類型與傳入數組相同。所傳入數組長度與size相等,則將 elementData復制到傳入數組中并返回傳入的數組。若傳入數組長度大于size,除了復制elementData外,還將把返回數組的第size個元素置為空。
public??T[]?toArray(T[]?a)?{????if?(a.length??size)????????a[size]?=?null;????return?a;}Fail-Fast機制:
ArrayList也采用了快速失敗的機制,通過記錄modCount參數來實現。在面對并發的修改時,迭代器很快就會完全失敗,而不是冒著在將來某個不確定時間發生任意不確定行為的風險。具體介紹請參考這篇文章HashMap中的Fail-Fast機制。
總結
以上是生活随笔為你收集整理的transient关键字的作用_ArrayList Vector (transient关键字)--JAVA成长之路的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python 拓扑排序 dfs bfs_
- 下一篇: 右手螺旋判断磁感应强度方向_高中物理第1