rlm@10
|
1 /**
|
rlm@10
|
2 * Copyright (c) Rich Hickey. All rights reserved.
|
rlm@10
|
3 * The use and distribution terms for this software are covered by the
|
rlm@10
|
4 * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
|
rlm@10
|
5 * which can be found in the file epl-v10.html at the root of this distribution.
|
rlm@10
|
6 * By using this software in any fashion, you are agreeing to be bound by
|
rlm@10
|
7 * the terms of this license.
|
rlm@10
|
8 * You must not remove this notice, or any other, from this software.
|
rlm@10
|
9 **/
|
rlm@10
|
10
|
rlm@10
|
11 /* rich Jul 31, 2008 */
|
rlm@10
|
12
|
rlm@10
|
13 package clojure.lang;
|
rlm@10
|
14
|
rlm@10
|
15 import java.util.concurrent.ConcurrentMap;
|
rlm@10
|
16 import java.util.*;
|
rlm@10
|
17
|
rlm@10
|
18 public class TransactionalHashMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>{
|
rlm@10
|
19 final Ref[] bins;
|
rlm@10
|
20
|
rlm@10
|
21 IPersistentMap mapAt(int bin){
|
rlm@10
|
22 return (IPersistentMap) bins[bin].deref();
|
rlm@10
|
23 }
|
rlm@10
|
24
|
rlm@10
|
25 final int binFor(Object k){
|
rlm@10
|
26 //spread hashes, a la Cliff Click
|
rlm@10
|
27 int h = k.hashCode();
|
rlm@10
|
28 h ^= (h >>> 20) ^ (h >>> 12);
|
rlm@10
|
29 h ^= (h >>> 7) ^ (h >>> 4);
|
rlm@10
|
30 return h % bins.length;
|
rlm@10
|
31 // return k.hashCode() % bins.length;
|
rlm@10
|
32 }
|
rlm@10
|
33
|
rlm@10
|
34 Entry entryAt(Object k){
|
rlm@10
|
35 return mapAt(binFor(k)).entryAt(k);
|
rlm@10
|
36 }
|
rlm@10
|
37
|
rlm@10
|
38 public TransactionalHashMap() throws Exception{
|
rlm@10
|
39 this(421);
|
rlm@10
|
40 }
|
rlm@10
|
41
|
rlm@10
|
42 public TransactionalHashMap(int nBins) throws Exception{
|
rlm@10
|
43 bins = new Ref[nBins];
|
rlm@10
|
44 for(int i = 0; i < nBins; i++)
|
rlm@10
|
45 bins[i] = new Ref(PersistentHashMap.EMPTY);
|
rlm@10
|
46 }
|
rlm@10
|
47
|
rlm@10
|
48 public TransactionalHashMap(Map<? extends K, ? extends V> m) throws Exception{
|
rlm@10
|
49 this(m.size());
|
rlm@10
|
50 putAll(m);
|
rlm@10
|
51 }
|
rlm@10
|
52
|
rlm@10
|
53 public int size(){
|
rlm@10
|
54 int n = 0;
|
rlm@10
|
55 for(int i = 0; i < bins.length; i++)
|
rlm@10
|
56 {
|
rlm@10
|
57 n += mapAt(i).count();
|
rlm@10
|
58 }
|
rlm@10
|
59 return n;
|
rlm@10
|
60 }
|
rlm@10
|
61
|
rlm@10
|
62 public boolean isEmpty(){
|
rlm@10
|
63 return size() == 0;
|
rlm@10
|
64 }
|
rlm@10
|
65
|
rlm@10
|
66 public boolean containsKey(Object k){
|
rlm@10
|
67 return entryAt(k) != null;
|
rlm@10
|
68 }
|
rlm@10
|
69
|
rlm@10
|
70 public V get(Object k){
|
rlm@10
|
71 Entry e = entryAt(k);
|
rlm@10
|
72 if(e != null)
|
rlm@10
|
73 return (V) e.getValue();
|
rlm@10
|
74 return null;
|
rlm@10
|
75 }
|
rlm@10
|
76
|
rlm@10
|
77 public V put(K k, V v){
|
rlm@10
|
78 Ref r = bins[binFor(k)];
|
rlm@10
|
79 IPersistentMap map = (IPersistentMap) r.deref();
|
rlm@10
|
80 Object ret = map.valAt(k);
|
rlm@10
|
81 r.set(map.assoc(k, v));
|
rlm@10
|
82 return (V) ret;
|
rlm@10
|
83 }
|
rlm@10
|
84
|
rlm@10
|
85 public V remove(Object k){
|
rlm@10
|
86 Ref r = bins[binFor(k)];
|
rlm@10
|
87 IPersistentMap map = (IPersistentMap) r.deref();
|
rlm@10
|
88 Object ret = map.valAt(k);
|
rlm@10
|
89 //checked exceptions are a bad idea, especially in an interface
|
rlm@10
|
90 try
|
rlm@10
|
91 {
|
rlm@10
|
92 r.set(map.without(k));
|
rlm@10
|
93 }
|
rlm@10
|
94 catch(Exception e)
|
rlm@10
|
95 {
|
rlm@10
|
96 throw new RuntimeException(e);
|
rlm@10
|
97 }
|
rlm@10
|
98 return (V) ret;
|
rlm@10
|
99 }
|
rlm@10
|
100
|
rlm@10
|
101 public void putAll(Map<? extends K, ? extends V> map){
|
rlm@10
|
102 for(Iterator i = map.entrySet().iterator(); i.hasNext();)
|
rlm@10
|
103 {
|
rlm@10
|
104 Entry<K, V> e = (Entry) i.next();
|
rlm@10
|
105 put(e.getKey(), e.getValue());
|
rlm@10
|
106 }
|
rlm@10
|
107 }
|
rlm@10
|
108
|
rlm@10
|
109 public void clear(){
|
rlm@10
|
110 for(int i = 0; i < bins.length; i++)
|
rlm@10
|
111 {
|
rlm@10
|
112 Ref r = bins[i];
|
rlm@10
|
113 IPersistentMap map = (IPersistentMap) r.deref();
|
rlm@10
|
114 if(map.count() > 0)
|
rlm@10
|
115 {
|
rlm@10
|
116 r.set(PersistentHashMap.EMPTY);
|
rlm@10
|
117 }
|
rlm@10
|
118 }
|
rlm@10
|
119 }
|
rlm@10
|
120
|
rlm@10
|
121 public Set<Entry<K, V>> entrySet(){
|
rlm@10
|
122 final ArrayList<Map.Entry<K, V>> entries = new ArrayList(bins.length);
|
rlm@10
|
123 for(int i = 0; i < bins.length; i++)
|
rlm@10
|
124 {
|
rlm@10
|
125 IPersistentMap map = mapAt(i);
|
rlm@10
|
126 if(map.count() > 0)
|
rlm@10
|
127 entries.addAll((Collection) RT.seq(map));
|
rlm@10
|
128 }
|
rlm@10
|
129 return new AbstractSet<Entry<K, V>>(){
|
rlm@10
|
130 public Iterator iterator(){
|
rlm@10
|
131 return Collections.unmodifiableList(entries).iterator();
|
rlm@10
|
132 }
|
rlm@10
|
133
|
rlm@10
|
134 public int size(){
|
rlm@10
|
135 return entries.size();
|
rlm@10
|
136 }
|
rlm@10
|
137 };
|
rlm@10
|
138 }
|
rlm@10
|
139
|
rlm@10
|
140 public V putIfAbsent(K k, V v){
|
rlm@10
|
141 Ref r = bins[binFor(k)];
|
rlm@10
|
142 IPersistentMap map = (IPersistentMap) r.deref();
|
rlm@10
|
143 Entry e = map.entryAt(k);
|
rlm@10
|
144 if(e == null)
|
rlm@10
|
145 {
|
rlm@10
|
146 r.set(map.assoc(k, v));
|
rlm@10
|
147 return null;
|
rlm@10
|
148 }
|
rlm@10
|
149 else
|
rlm@10
|
150 return (V) e.getValue();
|
rlm@10
|
151 }
|
rlm@10
|
152
|
rlm@10
|
153 public boolean remove(Object k, Object v){
|
rlm@10
|
154 Ref r = bins[binFor(k)];
|
rlm@10
|
155 IPersistentMap map = (IPersistentMap) r.deref();
|
rlm@10
|
156 Entry e = map.entryAt(k);
|
rlm@10
|
157 if(e != null && e.getValue().equals(v))
|
rlm@10
|
158 {
|
rlm@10
|
159 //checked exceptions are a bad idea, especially in an interface
|
rlm@10
|
160 try
|
rlm@10
|
161 {
|
rlm@10
|
162 r.set(map.without(k));
|
rlm@10
|
163 }
|
rlm@10
|
164 catch(Exception ex)
|
rlm@10
|
165 {
|
rlm@10
|
166 throw new RuntimeException(ex);
|
rlm@10
|
167 }
|
rlm@10
|
168 return true;
|
rlm@10
|
169 }
|
rlm@10
|
170 return false;
|
rlm@10
|
171 }
|
rlm@10
|
172
|
rlm@10
|
173 public boolean replace(K k, V oldv, V newv){
|
rlm@10
|
174 Ref r = bins[binFor(k)];
|
rlm@10
|
175 IPersistentMap map = (IPersistentMap) r.deref();
|
rlm@10
|
176 Entry e = map.entryAt(k);
|
rlm@10
|
177 if(e != null && e.getValue().equals(oldv))
|
rlm@10
|
178 {
|
rlm@10
|
179 r.set(map.assoc(k, newv));
|
rlm@10
|
180 return true;
|
rlm@10
|
181 }
|
rlm@10
|
182 return false;
|
rlm@10
|
183 }
|
rlm@10
|
184
|
rlm@10
|
185 public V replace(K k, V v){
|
rlm@10
|
186 Ref r = bins[binFor(k)];
|
rlm@10
|
187 IPersistentMap map = (IPersistentMap) r.deref();
|
rlm@10
|
188 Entry e = map.entryAt(k);
|
rlm@10
|
189 if(e != null)
|
rlm@10
|
190 {
|
rlm@10
|
191 r.set(map.assoc(k, v));
|
rlm@10
|
192 return (V) e.getValue();
|
rlm@10
|
193 }
|
rlm@10
|
194 return null;
|
rlm@10
|
195 }
|
rlm@10
|
196
|
rlm@10
|
197 }
|