001: /* Copyright (c) 2001-2005, The HSQL Development Group
002: * All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * Redistributions of source code must retain the above copyright notice, this
008: * list of conditions and the following disclaimer.
009: *
010: * Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * Neither the name of the HSQL Development Group nor the names of its
015: * contributors may be used to endorse or promote products derived from this
016: * software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
022: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
026: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030:
031: package org.hsqldb.store;
032:
033: import java.sql.Date;
034:
035: /*
036: * implementation notes:
037: *
038: * NB: As of this version this class cannot be used for mixed object types
039: * It is relativly easy to support this by adding an 'instanceof' test inside
040: * each getOrAddXxxx method before casting the Set values to the target type
041: * for comparison purposes.
042: *
043: * superclass is used as an Object Set
044: * getOrAddXxxx methods are implemented directly for speed
045: * the superclass infrastructure is otherwise used
046: */
047:
048: /**
049: * Subclass of BaseHashMap for maintaining a pool of objects. Supports a
050: * range of java.lang.* objects.
051: *
052: * @author fredt@users
053: * @version 1.8.0
054: * @since 1.7.2
055: *
056: */
057: public class ValuePoolHashMap extends BaseHashMap {
058:
059: public ValuePoolHashMap(int initialCapacity, int maxCapacity,
060: int purgePolicy) throws IllegalArgumentException {
061:
062: super (initialCapacity, 1, BaseHashMap.objectKeyOrValue,
063: BaseHashMap.noKeyOrValue, true);
064:
065: this .maxCapacity = maxCapacity;
066: this .purgePolicy = purgePolicy;
067: }
068:
069: /**
070: * In rare circumstances resetCapacity may not succeed, in which case
071: * capacity remains unchanged but purge policy is set to newPolicy
072: */
073: public void resetCapacity(int newCapacity, int newPolicy)
074: throws IllegalArgumentException {
075:
076: if (newCapacity != 0 && hashIndex.elementCount > newCapacity) {
077: int surplus = hashIndex.elementCount - newCapacity;
078:
079: surplus += (surplus >> 5);
080:
081: if (surplus > hashIndex.elementCount) {
082: surplus = hashIndex.elementCount;
083: }
084:
085: clear(surplus, (surplus >> 6));
086: }
087:
088: if (newCapacity != 0 && newCapacity < threshold) {
089: rehash(newCapacity);
090:
091: if (newCapacity < hashIndex.elementCount) {
092: newCapacity = maxCapacity;
093: }
094: }
095:
096: this .maxCapacity = newCapacity;
097: this .purgePolicy = newPolicy;
098: }
099:
100: protected Integer getOrAddInteger(int intKey) {
101:
102: Integer testValue;
103: int index = hashIndex.getHashIndex(intKey);
104: int lookup = hashIndex.hashTable[index];
105: int lastLookup = -1;
106:
107: for (; lookup >= 0; lastLookup = lookup, lookup = hashIndex
108: .getNextLookup(lookup)) {
109: testValue = (Integer) objectKeyTable[lookup];
110:
111: if (testValue.intValue() == intKey) {
112: if (accessCount == Integer.MAX_VALUE) {
113: resetAccessCount();
114: }
115:
116: accessTable[lookup] = accessCount++;
117:
118: return testValue;
119: }
120: }
121:
122: if (hashIndex.elementCount >= threshold) {
123: reset();
124:
125: return getOrAddInteger(intKey);
126: }
127:
128: lookup = hashIndex.linkNode(index, lastLookup);
129: testValue = new Integer(intKey);
130: objectKeyTable[lookup] = testValue;
131:
132: if (accessCount == Integer.MAX_VALUE) {
133: resetAccessCount();
134: }
135:
136: accessTable[lookup] = accessCount++;
137:
138: return testValue;
139: }
140:
141: protected Long getOrAddLong(long longKey) {
142:
143: Long testValue;
144: int index = hashIndex
145: .getHashIndex((int) (longKey ^ (longKey >>> 32)));
146: int lookup = hashIndex.hashTable[index];
147: int lastLookup = -1;
148:
149: for (; lookup >= 0; lastLookup = lookup, lookup = hashIndex
150: .getNextLookup(lookup)) {
151: testValue = (Long) objectKeyTable[lookup];
152:
153: if (testValue.longValue() == longKey) {
154: if (accessCount == Integer.MAX_VALUE) {
155: resetAccessCount();
156: }
157:
158: accessTable[lookup] = accessCount++;
159:
160: return testValue;
161: }
162: }
163:
164: if (hashIndex.elementCount >= threshold) {
165: reset();
166:
167: return getOrAddLong(longKey);
168: }
169:
170: lookup = hashIndex.linkNode(index, lastLookup);
171: testValue = new Long(longKey);
172: objectKeyTable[lookup] = testValue;
173:
174: if (accessCount == Integer.MAX_VALUE) {
175: resetAccessCount();
176: }
177:
178: accessTable[lookup] = accessCount++;
179:
180: return testValue;
181: }
182:
183: /**
184: * This is dissimilar to normal hash map get() methods. The key Object
185: * should have an equals(String) method which should return true if the
186: * key.toString.equals(String) is true. Also the key.hashCode() method
187: * must return the same value as key.toString.hashCode().<p>
188: *
189: * The above is always true when the key is a String. But it means it is
190: * possible to submit special keys that fulfill the contract. For example
191: * a wrapper around a byte[] can be submitted as key to retrieve either
192: * a new String, which is the toString() method of the wrapper, or return
193: * an existing String which would be equal to the product of toString().
194: *
195: * @param key String or other Object with compatible equals(String)
196: * and hashCode().
197: * @return String from map or a new String
198: */
199: protected String getOrAddString(Object key) {
200:
201: String testValue;
202: int index = hashIndex.getHashIndex(key.hashCode());
203: int lookup = hashIndex.hashTable[index];
204: int lastLookup = -1;
205:
206: for (; lookup >= 0; lastLookup = lookup, lookup = hashIndex
207: .getNextLookup(lookup)) {
208: testValue = (String) objectKeyTable[lookup];
209:
210: if (key.equals(testValue)) {
211: if (accessCount == Integer.MAX_VALUE) {
212: resetAccessCount();
213: }
214:
215: accessTable[lookup] = accessCount++;
216:
217: return testValue;
218: }
219: }
220:
221: if (hashIndex.elementCount >= threshold) {
222: reset();
223:
224: return getOrAddString(key);
225: }
226:
227: testValue = key.toString();
228: lookup = hashIndex.linkNode(index, lastLookup);
229: objectKeyTable[lookup] = testValue;
230:
231: if (accessCount == Integer.MAX_VALUE) {
232: resetAccessCount();
233: }
234:
235: accessTable[lookup] = accessCount++;
236:
237: return testValue;
238: }
239:
240: protected Date getOrAddDate(long longKey) {
241:
242: Date testValue;
243: int hash = (int) longKey ^ (int) (longKey >>> 32);
244: int index = hashIndex.getHashIndex(hash);
245: int lookup = hashIndex.hashTable[index];
246: int lastLookup = -1;
247:
248: for (; lookup >= 0; lastLookup = lookup, lookup = hashIndex
249: .getNextLookup(lookup)) {
250: testValue = (Date) objectKeyTable[lookup];
251:
252: if (testValue.getTime() == longKey) {
253: if (accessCount == Integer.MAX_VALUE) {
254: resetAccessCount();
255: }
256:
257: accessTable[lookup] = accessCount++;
258:
259: return testValue;
260: }
261: }
262:
263: if (hashIndex.elementCount >= threshold) {
264: reset();
265:
266: return getOrAddDate(longKey);
267: }
268:
269: lookup = hashIndex.linkNode(index, lastLookup);
270: testValue = new Date(longKey);
271: objectKeyTable[lookup] = testValue;
272:
273: if (accessCount == Integer.MAX_VALUE) {
274: resetAccessCount();
275: }
276:
277: accessTable[lookup] = accessCount++;
278:
279: return testValue;
280: }
281:
282: protected Double getOrAddDouble(long longKey) {
283:
284: Double testValue;
285: int index = hashIndex
286: .getHashIndex((int) (longKey ^ (longKey >>> 32)));
287: int lookup = hashIndex.hashTable[index];
288: int lastLookup = -1;
289:
290: for (; lookup >= 0; lastLookup = lookup, lookup = hashIndex
291: .getNextLookup(lookup)) {
292: testValue = (Double) objectKeyTable[lookup];
293:
294: if (Double.doubleToLongBits(testValue.doubleValue()) == longKey) {
295: if (accessCount == Integer.MAX_VALUE) {
296: resetAccessCount();
297: }
298:
299: accessTable[lookup] = accessCount++;
300:
301: return testValue;
302: }
303: }
304:
305: if (hashIndex.elementCount >= threshold) {
306: reset();
307:
308: return getOrAddDouble(longKey);
309: }
310:
311: lookup = hashIndex.linkNode(index, lastLookup);
312: testValue = new Double(Double.longBitsToDouble(longKey));
313: objectKeyTable[lookup] = testValue;
314:
315: if (accessCount == Integer.MAX_VALUE) {
316: resetAccessCount();
317: }
318:
319: accessTable[lookup] = accessCount++;
320:
321: return testValue;
322: }
323:
324: protected Object getOrAddObject(Object key) {
325:
326: Object testValue;
327: int index = hashIndex.getHashIndex(key.hashCode());
328: int lookup = hashIndex.hashTable[index];
329: int lastLookup = -1;
330:
331: for (; lookup >= 0; lastLookup = lookup, lookup = hashIndex
332: .getNextLookup(lookup)) {
333: testValue = objectKeyTable[lookup];
334:
335: if (testValue.equals(key)) {
336: if (accessCount == Integer.MAX_VALUE) {
337: resetAccessCount();
338: }
339:
340: accessTable[lookup] = accessCount++;
341:
342: return testValue;
343: }
344: }
345:
346: if (hashIndex.elementCount >= threshold) {
347: reset();
348:
349: return getOrAddObject(key);
350: }
351:
352: lookup = hashIndex.linkNode(index, lastLookup);
353: objectKeyTable[lookup] = key;
354:
355: if (accessCount == Integer.MAX_VALUE) {
356: resetAccessCount();
357: }
358:
359: accessTable[lookup] = accessCount++;
360:
361: return key;
362: }
363: /*
364: public static void main(String[] argv) {
365:
366: int BIGRANGE = 100000;
367: int SMALLRANGE = 50000;
368: int POOLSIZE = 100000;
369: java.util.Random randomgen = new java.util.Random();
370: org.hsqldb.lib.StopWatch sw = new org.hsqldb.lib.StopWatch();
371: ValuePoolHashMap map = new ValuePoolHashMap(POOLSIZE, POOLSIZE,
372: BaseHashMap.PURGE_HALF);
373: int maxCount = 5000000;
374:
375: try {
376: for (int rounds = 0; rounds < 3; rounds++) {
377: sw.zero();
378:
379: // timing for ValuePool retreival
380: for (int i = 0; i < maxCount; i++) {
381: boolean bigrange = (i % 2) == 0;
382: int intValue = randomgen.nextInt(bigrange ? BIGRANGE
383: : SMALLRANGE);
384: Integer intObject = map.getOrAddInteger(intValue);
385:
386: if (intObject.intValue() != intValue) {
387: throw new Exception("Value mismatch");
388: }
389:
390: // System.out.print(intValue);
391: // System.out.print(' ');
392: }
393:
394: System.out.println("Count " + maxCount + " "
395: + sw.elapsedTime());
396: sw.zero();
397:
398: // timing for Integer creation
399: for (int i = 0; i < maxCount; i++) {
400: boolean bigrange = (i % 2) == 0;
401: int intValue = randomgen.nextInt(bigrange ? BIGRANGE
402: : SMALLRANGE);
403: Integer intObject = new Integer(intValue);
404:
405: if (intObject.intValue() != intValue) {
406: throw new Exception("Value mismatch");
407: }
408: }
409:
410: System.out.println("Count new Integer() " + maxCount + " "
411: + sw.elapsedTime());
412: }
413: } catch (Exception e) {
414: e.printStackTrace();
415: }
416: }
417: */
418: }
|