@@ -32,6 +32,9 @@ package mutable
32
32
* @tparam A type of the elements contained in this hash table.
33
33
*/
34
34
trait HashTable [A , Entry >: Null <: HashEntry [A , Entry ]] extends HashTable .HashUtils [A ] {
35
+ // Replacing Entry type parameter by abstract type member here allows to not expose to public
36
+ // implementation-specific entry classes such as `DefaultEntry` or `LinkedEntry`.
37
+ // However, I'm afraid it's too late now for such breaking change.
35
38
import HashTable ._
36
39
37
40
@ transient protected var _loadFactor = defaultLoadFactor
@@ -52,7 +55,7 @@ trait HashTable[A, Entry >: Null <: HashEntry[A, Entry]] extends HashTable.HashU
52
55
*/
53
56
@ transient protected var sizemap : Array [Int ] = null
54
57
55
- @ transient var seedvalue : Int = tableSizeSeed
58
+ @ transient protected var seedvalue : Int = tableSizeSeed
56
59
57
60
protected def tableSizeSeed = Integer .bitCount(table.length - 1 )
58
61
@@ -75,11 +78,10 @@ trait HashTable[A, Entry >: Null <: HashEntry[A, Entry]] extends HashTable.HashU
75
78
}
76
79
77
80
/**
78
- * Initializes the collection from the input stream. `f` will be called for each key/value pair
79
- * read from the input stream in the order determined by the stream. This is useful for
80
- * structures where iteration order is important (e.g. LinkedHashMap).
81
+ * Initializes the collection from the input stream. `readEntry` will be called for each
82
+ * entry to be read from the input stream.
81
83
*/
82
- private [collection] def init [ B ] (in : java.io.ObjectInputStream , f : ( A , B ) => Entry ) {
84
+ private [collection] def init (in : java.io.ObjectInputStream , readEntry : => Entry ) {
83
85
in.defaultReadObject
84
86
85
87
_loadFactor = in.readInt()
@@ -100,35 +102,34 @@ trait HashTable[A, Entry >: Null <: HashEntry[A, Entry]] extends HashTable.HashU
100
102
101
103
var index = 0
102
104
while (index < size) {
103
- addEntry(f(in.readObject(). asInstanceOf [ A ], in.readObject(). asInstanceOf [ B ]) )
105
+ addEntry(readEntry )
104
106
index += 1
105
107
}
106
108
}
107
109
108
110
/**
109
111
* Serializes the collection to the output stream by saving the load factor, collection
110
- * size, collection keys and collection values. `value` is responsible for providing a value
111
- * from an entry.
112
+ * size and collection entries. `writeEntry` is responsible for writing an entry to the stream.
112
113
*
113
- * `foreach ` determines the order in which the key/value pairs are saved to the stream. To
114
+ * `foreachEntry ` determines the order in which the key/value pairs are saved to the stream. To
114
115
* deserialize, `init` should be used.
115
116
*/
116
- private [collection] def serializeTo [ B ] (out : java.io.ObjectOutputStream , value : Entry => B ) {
117
+ private [collection] def serializeTo (out : java.io.ObjectOutputStream , writeEntry : Entry => Unit ) {
117
118
out.defaultWriteObject
118
119
out.writeInt(_loadFactor)
119
120
out.writeInt(tableSize)
120
121
out.writeInt(seedvalue)
121
122
out.writeBoolean(isSizeMapDefined)
122
- foreachEntry { entry =>
123
- out.writeObject(entry.key)
124
- out.writeObject(value(entry))
125
- }
123
+
124
+ foreachEntry(writeEntry)
126
125
}
127
126
128
127
/** Find entry with given key in table, null if not found.
129
128
*/
130
- protected def findEntry (key : A ): Entry = {
131
- val h = index(elemHashCode(key))
129
+ protected def findEntry (key : A ): Entry =
130
+ findEntry0(key, index(elemHashCode(key)))
131
+
132
+ private [this ] def findEntry0 (key : A , h : Int ): Entry = {
132
133
var e = table(h).asInstanceOf [Entry ]
133
134
while (e != null && ! elemEquals(e.key, key)) e = e.next
134
135
e
@@ -138,7 +139,10 @@ trait HashTable[A, Entry >: Null <: HashEntry[A, Entry]] extends HashTable.HashU
138
139
* pre: no entry with same key exists
139
140
*/
140
141
protected def addEntry (e : Entry ) {
141
- val h = index(elemHashCode(e.key))
142
+ addEntry0(e, index(elemHashCode(e.key)))
143
+ }
144
+
145
+ private [this ] def addEntry0 (e : Entry , h : Int ) {
142
146
e.next = table(h).asInstanceOf [Entry ]
143
147
table(h) = e
144
148
tableSize = tableSize + 1
@@ -147,6 +151,24 @@ trait HashTable[A, Entry >: Null <: HashEntry[A, Entry]] extends HashTable.HashU
147
151
resize(2 * table.length)
148
152
}
149
153
154
+ /** Find entry with given key in table, or add new one if not found.
155
+ * May be somewhat faster then `findEntry`/`addEntry` pair as it
156
+ * computes entry's hash index only once.
157
+ * Returns entry found in table or null.
158
+ * New entries are created by calling `createNewEntry` method.
159
+ */
160
+ protected def findOrAddEntry [B ](key : A , value : B ): Entry = {
161
+ val h = index(elemHashCode(key))
162
+ val e = findEntry0(key, h)
163
+ if (e ne null ) e else { addEntry0(createNewEntry(key, value), h); null }
164
+ }
165
+
166
+ /** Creates new entry to be immediately inserted into the hashtable.
167
+ * This method is guaranteed to be called only once and in case that the entry
168
+ * will be added. In other words, an implementation may be side-effecting.
169
+ */
170
+ protected def createNewEntry [B ](key : A , value : B ): Entry
171
+
150
172
/** Remove entry from table if present.
151
173
*/
152
174
protected def removeEntry (key : A ) : Entry = {
@@ -195,7 +217,7 @@ trait HashTable[A, Entry >: Null <: HashEntry[A, Entry]] extends HashTable.HashU
195
217
}
196
218
197
219
/** Avoid iterator for a 2x faster traversal. */
198
- protected def foreachEntry [C ](f : Entry => C ) {
220
+ protected def foreachEntry [U ](f : Entry => U ) {
199
221
val iterTable = table
200
222
var idx = lastPopulatedIndex
201
223
var es = iterTable(idx)
0 commit comments