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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

transient HashMap使用目的分析

發(fā)布時間:2023/11/27 生活经验 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 transient HashMap使用目的分析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

看HashSet源碼有這么一句:

private transient HashMap<E,Object> map;

再看HashSet的Add方法:

實際上HashSet是復用HashMap了。

而我們去看看HashMap也會發(fā)現(xiàn)一樣使用了transient

而不管是HashSet還是HashMapdou都要求是Serializable的:

public class HashMap<K,V> extends AbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable {
public class HashSet<E>extends AbstractSet<E>implements Set<E>, Cloneable, java.io.Serializable

而transient實際上是不序列化的,這就好像有點“矛盾”,再回答這個問題之前先看看transient。

測試代碼如下:

import java.io.Serializable;public class EmployeeTransient implements Serializable {public String getConfidentialInfo() {return confidentialInfo;}public void setConfidentialInfo(String confidentialInfo) {this.confidentialInfo = confidentialInfo;}public String getFirstName() {return firstName;}public void setFirstName(String firstName) {this.firstName = firstName;}public String getLastName() {return lastName;}public void setLastName(String lastName) {this.lastName = lastName;}private transient String confidentialInfo;private String firstName;private String lastName;
}

?現(xiàn)在讓我們先序列化再反序列化回java對象,并驗證是否保存了“?confidentialInfo?”?

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;public class TransientTest {public static void main(String args[]) {try {ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("empInfo.ser"));EmployeeTransient emp = new EmployeeTransient();emp.setFirstName("Lokesh");emp.setLastName("Gupta");emp.setConfidentialInfo("password");//Serialize the objectoos.writeObject(emp);oos.close();} catch (Exception e) {System.out.println(e);}try {ObjectInputStream ooi = new ObjectInputStream(new FileInputStream("empInfo.ser"));//Read the object backEmployeeTransient readEmpInfo = (EmployeeTransient) ooi.readObject();System.out.println(readEmpInfo.getFirstName());System.out.println(readEmpInfo.getLastName());System.out.println(readEmpInfo.getConfidentialInfo());ooi.close();} catch (Exception e) {System.out.println(e);}}
}

很明顯,“?confidentialInfo?”在序列化時沒有保存到持久狀態(tài),這正是我們在java中使用“?transient?”關鍵字的原因。?

什么時候應該在java中使用transient關鍵字?

現(xiàn)在我們對“?transient??”關鍵字非常了解。讓我們通過確定您需要使用transient關鍵字的情況來擴展理解。

  1. 第一個也是非常合乎邏輯的情況是,您可能擁有類實例中的其他字段派生/計算的字段。應該每次都以編程方式計算它們,而不是通過序列化來保持狀態(tài)。一個例子可以是基于時間戳的價值;?例如人的年齡或時間戳和當前時間戳之間的持續(xù)時間。在這兩種情況下,您將根據當前系統(tǒng)時間而不是序列化實例來計算變量的值。
  2. 第二個邏輯示例可以是任何不應以任何形式泄漏到JVM外部的安全信息(在數據庫或字節(jié)流中)。
  3. 另一個例子可能是在JDK或應用程序代碼中未標記為“Serializable”的字段。未實現(xiàn)Serializable接口并在任何可序列化類中引用的類無法序列化;?并將拋出“java.io.NotSerializableException”異常。在序列化主類之前,應將這些不可序列化的引用標記為“transient?”。
  4. 最后,有時候序列化某些字段根本沒有意義。期。例如,在任何類中,如果添加了記錄器引用,那么序列化該記錄器實例的用途是什么。絕對沒用。您可以邏輯地序列化表示實例狀態(tài)的信息。Loggers永遠不要分享實例的狀態(tài)。它們只是用于編程/調試目的的實用程序。類似的例子可以是一個Thread類的引用。線程表示任何給定時間點的進程狀態(tài),并且沒有用于將線程狀態(tài)存儲到您的實例中;?僅僅因為它們不構成你班級實例的狀態(tài)。

?

//final field 1
public final transient String confidentialInfo = "password";

?現(xiàn)在當我再次運行序列化(寫/讀)時,會輸出出password。

原因是,只要任何最終字段/引用被評估為“?常量表達式?”,它就會被JVM序列化,忽略transient關鍵字的存在。

如果要保持非可序列化字段的狀態(tài),請使用readObject()和writeObject()方法。writeObject()/ readObject()通常在內部鏈接到序列化/反序列化機制,因此自動調用。?:java中的SerialVersionUID及相關

?

HashMap如何使用transient關鍵字?

HashMap用于存儲鍵值對,我們都知道。并且我們還知道內部密鑰的位置HashMap是基于例如密鑰獲得的哈希碼來計算的?,F(xiàn)在當我們序列化一個HashMap意味著內部的所有鍵HashMap和鍵的各個值也將被序列化。序列化后,當我們反序列化HashMap實例時,所有鍵實例也將被反序列化。我們知道在這個序列化/反序列化過程中,可能會丟失信息(用于計算哈希碼),最重要的是它本身就是一個新的實例。

在java中,任何兩個實例(即使是同一個類)都不能具有相同的哈希碼。這是一個很大的問題,因為根據新的哈希碼應該放置鍵的位置不在正確的位置。檢索鍵的值時,您將在此新HashMap中引用錯誤的索引。

:在java中使用hashCode和equals方法

因此,當序列化哈希映射時,這意味著哈希索引,因此表的順序不再有效,不應保留。這是問題陳述。

HashMap類使用writeObject()readObject()方法,如下所示

/*** Save the state of the <tt>HashMap</tt> instance to a stream (i.e.,* serialize it).** @serialData The <i>capacity</i> of the HashMap (the length of the*             bucket array) is emitted (int), followed by the*             <i>size</i> (an int, the number of key-value*             mappings), followed by the key (Object) and value (Object)*             for each key-value mapping.  The key-value mappings are*             emitted in no particular order.*/private void writeObject(java.io.ObjectOutputStream s)throws IOException {int buckets = capacity();// Write out the threshold, loadfactor, and any hidden stuffs.defaultWriteObject();s.writeInt(buckets);s.writeInt(size);internalWriteEntries(s);}
// Callbacks to allow LinkedHashMap post-actionsvoid afterNodeAccess(Node<K,V> p) { }void afterNodeInsertion(boolean evict) { }void afterNodeRemoval(Node<K,V> p) { }// Called only from writeObject, to ensure compatible ordering.void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException {Node<K,V>[] tab;if (size > 0 && (tab = table) != null) {for (int i = 0; i < tab.length; ++i) {for (Node<K,V> e = tab[i]; e != null; e = e.next) {s.writeObject(e.key);s.writeObject(e.value);}}}}

下面是HashSet中的實現(xiàn),思路都是一樣的。都是把key,value都寫到序列化里去,

/*** Save the state of this <tt>HashSet</tt> instance to a stream (that is,* serialize it).** @serialData The capacity of the backing <tt>HashMap</tt> instance*             (int), and its load factor (float) are emitted, followed by*             the size of the set (the number of elements it contains)*             (int), followed by all of its elements (each an Object) in*             no particular order.*/private void writeObject(java.io.ObjectOutputStream s)throws java.io.IOException {// Write out any hidden serialization magics.defaultWriteObject();// Write out HashMap capacity and load factors.writeInt(map.capacity());s.writeFloat(map.loadFactor());// Write out sizes.writeInt(map.size());// Write out all elements in the proper order.for (E e : map.keySet())s.writeObject(e);}

然后反序列化的時候就是重新hash一次的過程

下面是HashMap的readObject:

下面是HashSet的readObject:

使用上面的代碼,HashMap仍然可以像通常那樣處理非transient字段,但是它們一個接一個地在字節(jié)數組的末尾寫入存儲的鍵值對。在反序列化時,它會通過默認的反序列化過程處理非transient變量,然后逐個讀取鍵值對。對于每個鍵,哈希和索引再次計算并插入到表中的正確位置,以便可以再次檢索它而不會出現(xiàn)任何錯誤。?

參考:Java transient關鍵字示例?

擴展閱讀:

為什么HashMap的哈希表標記為transient,盡管該類是可序列化的

圖解集合4:HashMap?

總結

以上是生活随笔為你收集整理的transient HashMap使用目的分析的全部內容,希望文章能夠幫你解決所遇到的問題。

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