You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

WeakIdentityHashMap.java 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. /*
  2. * Copyright 2004-2011 H2 Group.
  3. * Copyright 2011 James Moger.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package com.iciql.util;
  18. import java.lang.ref.WeakReference;
  19. import java.util.Collection;
  20. import java.util.Map;
  21. import java.util.Set;
  22. import com.iciql.IciqlException;
  23. /**
  24. * This hash map uses weak references, so that elements that are no longer
  25. * referenced elsewhere can be garbage collected. It also uses object identity
  26. * to compare keys. The garbage collection happens when trying to add new data,
  27. * or when resizing.
  28. *
  29. * @param <K>
  30. * the keys
  31. * @param <V>
  32. * the value
  33. */
  34. public class WeakIdentityHashMap<K, V> implements Map<K, V> {
  35. private static final int MAX_LOAD = 90;
  36. private static final WeakReference<Object> DELETED_KEY = new WeakReference<Object>(null);
  37. private int mask, len, size, deletedCount, level;
  38. private int maxSize, minSize, maxDeleted;
  39. private WeakReference<K>[] keys;
  40. private V[] values;
  41. public WeakIdentityHashMap() {
  42. reset(2);
  43. }
  44. public int size() {
  45. return size;
  46. }
  47. private void checkSizePut() {
  48. if (deletedCount > size) {
  49. rehash(level);
  50. }
  51. if (size + deletedCount >= maxSize) {
  52. rehash(level + 1);
  53. }
  54. }
  55. private void checkSizeRemove() {
  56. if (size < minSize && level > 0) {
  57. rehash(level - 1);
  58. } else if (deletedCount > maxDeleted) {
  59. rehash(level);
  60. }
  61. }
  62. private int getIndex(Object key) {
  63. return System.identityHashCode(key) & mask;
  64. }
  65. @SuppressWarnings("unchecked")
  66. private void reset(int newLevel) {
  67. minSize = size * 3 / 4;
  68. size = 0;
  69. level = newLevel;
  70. len = 2 << level;
  71. mask = len - 1;
  72. maxSize = (int) (len * MAX_LOAD / 100L);
  73. deletedCount = 0;
  74. maxDeleted = 20 + len / 2;
  75. keys = new WeakReference[len];
  76. values = (V[]) new Object[len];
  77. }
  78. public V put(K key, V value) {
  79. checkSizePut();
  80. int index = getIndex(key);
  81. int plus = 1;
  82. int deleted = -1;
  83. do {
  84. WeakReference<K> k = keys[index];
  85. if (k == null) {
  86. // found an empty record
  87. if (deleted >= 0) {
  88. index = deleted;
  89. deletedCount--;
  90. }
  91. size++;
  92. keys[index] = new WeakReference<K>(key);
  93. values[index] = value;
  94. return null;
  95. } else if (k == DELETED_KEY) {
  96. if (deleted < 0) {
  97. // found the first deleted record
  98. deleted = index;
  99. }
  100. } else {
  101. Object r = k.get();
  102. if (r == null) {
  103. delete(index);
  104. } else if (r == key) {
  105. // update existing
  106. V old = values[index];
  107. values[index] = value;
  108. return old;
  109. }
  110. }
  111. index = (index + plus++) & mask;
  112. } while (plus <= len);
  113. throw new IciqlException("Hashmap is full");
  114. }
  115. public V remove(Object key) {
  116. checkSizeRemove();
  117. int index = getIndex(key);
  118. int plus = 1;
  119. do {
  120. WeakReference<K> k = keys[index];
  121. if (k == null) {
  122. // found an empty record
  123. return null;
  124. } else if (k == DELETED_KEY) {
  125. // continue
  126. } else {
  127. Object r = k.get();
  128. if (r == null) {
  129. delete(index);
  130. } else if (r == key) {
  131. // found the record
  132. V old = values[index];
  133. delete(index);
  134. return old;
  135. }
  136. }
  137. index = (index + plus++) & mask;
  138. k = keys[index];
  139. } while (plus <= len);
  140. // not found
  141. return null;
  142. }
  143. @SuppressWarnings("unchecked")
  144. private void delete(int index) {
  145. keys[index] = (WeakReference<K>) DELETED_KEY;
  146. values[index] = null;
  147. deletedCount++;
  148. size--;
  149. }
  150. private void rehash(int newLevel) {
  151. WeakReference<K>[] oldKeys = keys;
  152. V[] oldValues = values;
  153. reset(newLevel);
  154. for (int i = 0; i < oldKeys.length; i++) {
  155. WeakReference<K> k = oldKeys[i];
  156. if (k != null && k != DELETED_KEY) {
  157. K key = k.get();
  158. if (key != null) {
  159. put(key, oldValues[i]);
  160. }
  161. }
  162. }
  163. }
  164. public V get(Object key) {
  165. int index = getIndex(key);
  166. int plus = 1;
  167. do {
  168. WeakReference<K> k = keys[index];
  169. if (k == null) {
  170. return null;
  171. } else if (k == DELETED_KEY) {
  172. // continue
  173. } else {
  174. Object r = k.get();
  175. if (r == null) {
  176. delete(index);
  177. } else if (r == key) {
  178. return values[index];
  179. }
  180. }
  181. index = (index + plus++) & mask;
  182. } while (plus <= len);
  183. return null;
  184. }
  185. public void clear() {
  186. reset(2);
  187. }
  188. public boolean containsKey(Object key) {
  189. return get(key) != null;
  190. }
  191. public boolean containsValue(Object value) {
  192. if (value == null) {
  193. return false;
  194. }
  195. for (V item : values) {
  196. if (value.equals(item)) {
  197. return true;
  198. }
  199. }
  200. return false;
  201. }
  202. public Set<java.util.Map.Entry<K, V>> entrySet() {
  203. throw new UnsupportedOperationException();
  204. }
  205. public boolean isEmpty() {
  206. return size == 0;
  207. }
  208. public Set<K> keySet() {
  209. throw new UnsupportedOperationException();
  210. }
  211. public void putAll(Map<? extends K, ? extends V> m) {
  212. throw new UnsupportedOperationException();
  213. }
  214. public Collection<V> values() {
  215. throw new UnsupportedOperationException();
  216. }
  217. }