1120 } 1121 1122 return value; 1123 } 1124 1125 /** 1126 * Save the state of the Hashtable to a stream (i.e., serialize it). 1127 * 1128 * @serialData The <i>capacity</i> of the Hashtable (the length of the 1129 * bucket array) is emitted (int), followed by the 1130 * <i>size</i> of the Hashtable (the number of key-value 1131 * mappings), followed by the key (Object) and value (Object) 1132 * for each key-value mapping represented by the Hashtable 1133 * The key-value mappings are emitted in no particular order. 1134 */ 1135 private void writeObject(java.io.ObjectOutputStream s) 1136 throws IOException { 1137 Entry<Object, Object> entryStack = null; 1138 1139 synchronized (this) { 1140 // Write out the length, threshold, loadfactor 1141 s.defaultWriteObject(); 1142 1143 // Write out length, count of elements 1144 s.writeInt(table.length); 1145 s.writeInt(count); 1146 1147 // Stack copies of the entries in the table 1148 for (Entry<?, ?> entry : table) { 1149 1150 while (entry != null) { 1151 entryStack = 1152 new Entry<>(0, entry.key, entry.value, entryStack); 1153 entry = entry.next; 1154 } 1155 } 1156 } 1157 1158 // Write out the key/value objects from the stacked entries 1159 while (entryStack != null) { 1160 s.writeObject(entryStack.key); 1161 s.writeObject(entryStack.value); 1162 entryStack = entryStack.next; 1163 } 1164 } 1165 1166 /** 1167 * Reconstitute the Hashtable from a stream (i.e., deserialize it). 1168 */ 1169 private void readObject(java.io.ObjectInputStream s) 1170 throws IOException, ClassNotFoundException 1171 { 1172 // Read in the length, threshold, and loadfactor 1173 s.defaultReadObject(); 1174 1175 // Read the original length of the array and number of elements 1176 int origlength = s.readInt(); 1177 int elements = s.readInt(); 1178 1179 // Compute new size with a bit of room 5% to grow but 1180 // no larger than the original size. Make the length 1181 // odd if it's large enough, this helps distribute the entries. 1182 // Guard against the length ending up zero, that's not valid. 1183 int length = (int)(elements * loadFactor) + (elements / 20) + 3; 1184 if (length > elements && (length & 1) == 0) 1185 length--; 1186 if (origlength > 0 && length > origlength) 1187 length = origlength; 1188 table = new Entry<?,?>[length]; 1189 threshold = (int)Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1); 1190 count = 0; 1191 1192 // Read the number of elements and then all the key/value objects 1193 for (; elements > 0; elements--) { 1194 @SuppressWarnings("unchecked") 1195 K key = (K)s.readObject(); 1196 @SuppressWarnings("unchecked") 1197 V value = (V)s.readObject(); 1198 // synch could be eliminated for performance 1199 reconstitutionPut(table, key, value); 1200 } 1201 } 1202 1203 /** 1204 * The put method used by readObject. This is provided because put 1205 * is overridable and should not be called in readObject since the 1206 * subclass will not yet be initialized. 1207 * 1208 * <p>This differs from the regular put method in several ways. No 1209 * checking for rehashing is necessary since the number of elements 1210 * initially in the table is known. The modCount is not incremented 1211 * because we are creating a new instance. Also, no return value 1212 * is needed. 1213 */ 1214 private void reconstitutionPut(Entry<?,?>[] tab, K key, V value) 1215 throws StreamCorruptedException 1216 { 1217 if (value == null) { 1218 throw new java.io.StreamCorruptedException(); 1219 } 1220 // Makes sure the key is not already in the hashtable. 1221 // This should not happen in deserialized version. 1222 int hash = key.hashCode(); 1223 int index = (hash & 0x7FFFFFFF) % tab.length; 1224 for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) { 1225 if ((e.hash == hash) && e.key.equals(key)) { 1226 throw new java.io.StreamCorruptedException(); 1227 } 1228 } 1229 // Creates the new entry. 1230 @SuppressWarnings("unchecked") 1231 Entry<K,V> e = (Entry<K,V>)tab[index]; 1232 tab[index] = new Entry<>(hash, key, value, e); | 1120 } 1121 1122 return value; 1123 } 1124 1125 /** 1126 * Save the state of the Hashtable to a stream (i.e., serialize it). 1127 * 1128 * @serialData The <i>capacity</i> of the Hashtable (the length of the 1129 * bucket array) is emitted (int), followed by the 1130 * <i>size</i> of the Hashtable (the number of key-value 1131 * mappings), followed by the key (Object) and value (Object) 1132 * for each key-value mapping represented by the Hashtable 1133 * The key-value mappings are emitted in no particular order. 1134 */ 1135 private void writeObject(java.io.ObjectOutputStream s) 1136 throws IOException { 1137 Entry<Object, Object> entryStack = null; 1138 1139 synchronized (this) { 1140 // Write out the threshold and loadFactor 1141 s.defaultWriteObject(); 1142 1143 // Write out the length and count of elements 1144 s.writeInt(table.length); 1145 s.writeInt(count); 1146 1147 // Stack copies of the entries in the table 1148 for (Entry<?, ?> entry : table) { 1149 1150 while (entry != null) { 1151 entryStack = 1152 new Entry<>(0, entry.key, entry.value, entryStack); 1153 entry = entry.next; 1154 } 1155 } 1156 } 1157 1158 // Write out the key/value objects from the stacked entries 1159 while (entryStack != null) { 1160 s.writeObject(entryStack.key); 1161 s.writeObject(entryStack.value); 1162 entryStack = entryStack.next; 1163 } 1164 } 1165 1166 /** 1167 * Reconstitute the Hashtable from a stream (i.e., deserialize it). 1168 */ 1169 private void readObject(java.io.ObjectInputStream s) 1170 throws IOException, ClassNotFoundException 1171 { 1172 // Read in the threshold and loadFactor 1173 s.defaultReadObject(); 1174 1175 // Validate loadFactor (ignore threshold - it will be re-computed) 1176 if (loadFactor <= 0 || Float.isNaN(loadFactor)) 1177 throw new StreamCorruptedException("Illegal Load: " + loadFactor); 1178 1179 // Read the original length of the array and number of elements 1180 int origlength = s.readInt(); 1181 int elements = s.readInt(); 1182 1183 // Validate # of elements 1184 if (elements < 0) 1185 throw new StreamCorruptedException("Illegal # of Elements: " + elements); 1186 1187 // Clamp original length to be more than elements / loadFactor 1188 // (this is the invariant enforced with auto-growth) 1189 origlength = Math.max(origlength, (int)(elements / loadFactor) + 1); 1190 1191 // Compute new length with a bit of room 5% + 3 to grow but 1192 // no larger than the clamped original length. Make the length 1193 // odd if it's large enough, this helps distribute the entries. 1194 // Guard against the length ending up zero, that's not valid. 1195 int length = (int)((elements + elements / 20) / loadFactor) + 3; 1196 if (length > elements && (length & 1) == 0) 1197 length--; 1198 length = Math.min(length, origlength); 1199 table = new Entry<?,?>[length]; 1200 threshold = (int)Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1); 1201 count = 0; 1202 1203 // Read the number of elements and then all the key/value objects 1204 for (; elements > 0; elements--) { 1205 @SuppressWarnings("unchecked") 1206 K key = (K)s.readObject(); 1207 @SuppressWarnings("unchecked") 1208 V value = (V)s.readObject(); 1209 // sync is eliminated for performance 1210 reconstitutionPut(table, key, value); 1211 } 1212 } 1213 1214 /** 1215 * The put method used by readObject. This is provided because put 1216 * is overridable and should not be called in readObject since the 1217 * subclass will not yet be initialized. 1218 * 1219 * <p>This differs from the regular put method in several ways. No 1220 * checking for rehashing is necessary since the number of elements 1221 * initially in the table is known. The modCount is not incremented and 1222 * there's no synchronization because we are creating a new instance. 1223 * Also, no return value is needed. 1224 */ 1225 private void reconstitutionPut(Entry<?,?>[] tab, K key, V value) 1226 throws StreamCorruptedException 1227 { 1228 if (value == null) { 1229 throw new java.io.StreamCorruptedException(); 1230 } 1231 // Makes sure the key is not already in the hashtable. 1232 // This should not happen in deserialized version. 1233 int hash = key.hashCode(); 1234 int index = (hash & 0x7FFFFFFF) % tab.length; 1235 for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) { 1236 if ((e.hash == hash) && e.key.equals(key)) { 1237 throw new java.io.StreamCorruptedException(); 1238 } 1239 } 1240 // Creates the new entry. 1241 @SuppressWarnings("unchecked") 1242 Entry<K,V> e = (Entry<K,V>)tab[index]; 1243 tab[index] = new Entry<>(hash, key, value, e); |