1 package structures; 2 3 import java.util.ArrayList; 4 import java.util.Arrays; 5 import java.util.HashMap; 6 import java.util.Random; 7 8 import shared.KillSwitch; 9 import shared.Primes; 10 import shared.Shared; 11 import shared.Timer; 12 import shared.Tools; 13 14 /** 15 * @author Brian Bushnell 16 * @date December 12, 2017 17 * 18 */ 19 public final class LongLongHashMap{ 20 main(String[] args)21 public static void main(String[] args){ 22 Random randy2=Shared.threadLocalRandom(); 23 LongLongHashMap map=new LongLongHashMap(20, 0.7f); 24 HashMap<Long, Integer> map2=new HashMap<Long, Integer>(20, 0.7f); 25 ArrayList<Long> list=new ArrayList<Long>(); 26 ArrayList<Long> list2=new ArrayList<Long>(); 27 // ArrayList<Integer> vals=new ArrayList<Integer>(); 28 for(long i=0; i<1000; i++){ 29 assert(!map.contains(i)); 30 assert(!map2.containsKey(i)); 31 list.add(new Long(i)); 32 } 33 for(int i=0; i<1000; i++){ 34 long r=randy2.nextLong(); 35 list2.add(r); 36 } 37 38 for(long x : list){ 39 map.put(x, (int)(2*x)); 40 map2.put(x, (int)(2*x)); 41 assert(map.get(x)==((int)(2*x))); 42 assert(map2.get(x)==((int)(2*x))); 43 } 44 45 for(long x : list){ 46 assert(map.get(x)==((int)(2*x))); 47 assert(map2.get(x)==((int)(2*x))); 48 map.remove(x); 49 map2.remove(x); 50 assert(!map.contains(x)); 51 assert(!map2.containsKey(x)); 52 } 53 assert(map.isEmpty()); 54 assert(map2.isEmpty()); 55 56 for(long x : list2){ 57 map.put(x, (int)(2*x)); 58 map2.put(x, (int)(2*x)); 59 assert(map.get(x)==((int)(2*x))); 60 assert(map2.get(x)==((int)(2*x))); 61 } 62 63 for(long x : list2){ 64 assert(map.get(x)==((int)(2*x))); 65 assert(map2.get(x)==((int)(2*x))); 66 map.remove(x); 67 map2.remove(x); 68 assert(!map.contains(x)); 69 assert(!map2.containsKey(x)); 70 } 71 assert(map.isEmpty()); 72 assert(map2.isEmpty()); 73 74 int count=4000000; 75 int runs=32; 76 LongList ll=new LongList(count); 77 for(int i=0; i<count; i++){ll.add(randy2.nextLong());} 78 79 Shared.printMemory(); 80 Timer t=new Timer(); 81 for(int k=0; k<2; k++){ 82 System.err.println("LongHashMap:"); 83 t.start(); 84 for(int i=0; i<runs; i++){ 85 // for(long x : ll.array){ 86 // map.add(x); 87 // } 88 final long[] y=ll.array; 89 for(int z=0; z<count; z++){ 90 final long key=y[z]; 91 map.add(key); 92 map.contains(key); 93 map.remove(key); 94 map.add(key); 95 } 96 // for(long x : ll.array){ 97 // map.remove(x); 98 // } 99 // map.clear(); 100 // assert(map.isEmpty()); 101 // System.err.println("Finished run "+i); 102 } 103 t.stop(); 104 System.err.println(t); 105 Shared.printMemory(); 106 107 // System.err.println("HashMap:"); 108 // t.start(); 109 // for(int i=0; i<runs; i++){ 110 // for(long x : ll.array){ 111 // map2.add(x); 112 // } 113 // for(long x : ll.array){ 114 // map2.remove(x); 115 // } 116 // assert(map2.isEmpty()); 117 //// System.err.println("Finished run "+i); 118 // } 119 // t.stop(); 120 // System.err.println(t); 121 // Shared.printMemory(); 122 } 123 t.stop(); 124 } 125 126 /*--------------------------------------------------------------*/ 127 /*---------------- Initialization ----------------*/ 128 /*--------------------------------------------------------------*/ 129 LongLongHashMap()130 public LongLongHashMap(){ 131 this(256); 132 } 133 LongLongHashMap(int initialSize)134 public LongLongHashMap(int initialSize){ 135 this(initialSize, 0.7f); 136 } 137 LongLongHashMap(int initialSize, float loadFactor_)138 public LongLongHashMap(int initialSize, float loadFactor_){ 139 invalid=randy.nextLong()|MINMASK; 140 assert(invalid<0); 141 assert(initialSize>0); 142 assert(loadFactor_>0 && loadFactor_<1); 143 loadFactor=Tools.mid(0.25f, loadFactor_, 0.90f); 144 resize(initialSize); 145 } 146 147 /*--------------------------------------------------------------*/ 148 /*---------------- Public Methods ----------------*/ 149 /*--------------------------------------------------------------*/ 150 clear()151 public void clear(){ 152 if(size<1){return;} 153 Arrays.fill(keys, invalid); 154 Arrays.fill(values, 0); 155 size=0; 156 // assert(verify()); //123 157 } 158 contains(long key)159 public boolean contains(long key){ 160 // assert(verify()); //123 161 return key==invalid ? false : findCell(key)>=0; 162 } 163 containsKey(long key)164 public boolean containsKey(long key){ 165 return contains(key); 166 } 167 get(long key)168 public long get(long key){ 169 // assert(verify()); //123 170 long value=-1; 171 if(key!=invalid){ 172 int cell=findCell(key); 173 if(cell>=0){value=values[cell];} 174 } 175 return value; 176 } 177 178 /** 179 * Increment this key's value by 1. 180 * @param key 181 * @return New value 182 */ add(long key)183 public long add(long key){ 184 return increment(key, 1); 185 } 186 187 /** 188 * Increment this key's value by incr. 189 * @param key 190 * @param incr 191 * @return New value 192 */ increment(long key, long incr)193 public long increment(long key, long incr){ 194 // assert(verify()); //123 195 if(key==invalid){resetInvalid();} 196 int cell=findCellOrEmpty(key); 197 if(keys[cell]==invalid){ 198 keys[cell]=key; 199 values[cell]=incr; 200 size++; 201 // assert(verify()); //123 202 if(size>sizeLimit){resize();} 203 // assert(verify()); //123 204 return incr; 205 }else{ 206 values[cell]+=incr; 207 // assert(verify()); //123 208 return values[cell]; 209 } 210 } 211 212 /** 213 * Map this key to value. 214 * @param key 215 * @param value 216 * @return true if the key was added, false if it was already contained. 217 */ put(long key, long value)218 public boolean put(long key, long value){ 219 // assert(verify()); //123 220 if(key==invalid){resetInvalid();} 221 int cell=findCellOrEmpty(key); 222 if(keys[cell]==invalid){ 223 keys[cell]=key; 224 values[cell]=value; 225 size++; 226 if(size>sizeLimit){resize();} 227 // assert(verify()); //123 228 return true; 229 } 230 assert(keys[cell]==key); 231 // assert(verify()); //123 232 return false; 233 } 234 235 /** 236 * Remove this key from the map. 237 * @param key 238 * @return Old value. 239 */ remove(long key)240 public long remove(long key){ 241 // assert(verify()); //123 242 if(key==invalid){return -1;} 243 final int cell=findCell(key); 244 if(cell<0){return -1;} 245 assert(keys[cell]==key); 246 keys[cell]=invalid; 247 final long value=values[cell]; 248 values[cell]=0; 249 size--; 250 251 rehashFrom(cell); 252 // assert(verify()); //123 253 return value; 254 } 255 size()256 public int size(){return size;} 257 isEmpty()258 public boolean isEmpty(){return size==0;} 259 260 /*--------------------------------------------------------------*/ 261 /*---------------- String Methods ----------------*/ 262 /*--------------------------------------------------------------*/ 263 264 @Override toString()265 public String toString(){ 266 return toStringListView(); 267 } 268 toStringSetView()269 public String toStringSetView(){ 270 StringBuilder sb=new StringBuilder(); 271 sb.append('['); 272 String comma=""; 273 for(int i=0; i<keys.length; i++){ 274 if(keys[i]!=invalid){ 275 sb.append(comma+"("+i+", "+keys[i]+", "+values[i]+")"); 276 comma=", "; 277 } 278 } 279 sb.append(']'); 280 return sb.toString(); 281 } 282 toStringListView()283 public String toStringListView(){ 284 StringBuilder sb=new StringBuilder(); 285 sb.append('['); 286 String comma=""; 287 for(int i=0; i<keys.length; i++){ 288 if(keys[i]!=invalid){ 289 sb.append(comma+keys[i]); 290 comma=", "; 291 } 292 } 293 sb.append(']'); 294 return sb.toString(); 295 } 296 toArray()297 public long[] toArray(){ 298 long[] x=KillSwitch.allocLong1D(size); 299 int i=0; 300 for(long key : keys){ 301 if(key!=invalid){ 302 x[i]=key; 303 i++; 304 } 305 } 306 return x; 307 } 308 toArray(long thresh)309 public long[] toArray(long thresh){ 310 int len=0; 311 // assert(verify()); 312 for(int i=0; i<values.length; i++){ 313 assert((values[i]==0)==(keys[i]==invalid)) : i+", "+values[i]+", "+keys[i]+", "+invalid+"\n"+toStringSetView(); 314 assert((keys[i]<0)==((keys[i]==invalid))) : toStringSetView(); 315 if(values[i]>=thresh){ 316 assert(keys[i]>=0) : "\nNegative key ("+keys[i]+", "+values[i]+", "+i+") for thresh "+thresh+":\n"+toStringSetView(); 317 len++; 318 } 319 } 320 long[] x=KillSwitch.allocLong1D(len); 321 for(int i=0, j=0; j<len; i++){ 322 if(values[i]>=thresh){ 323 x[j]=keys[i]; 324 assert(keys[i]>=0) : "\nNegative key ("+keys[i]+", "+values[i]+", "+i+") for thresh "+thresh+":\n"+toStringSetView(); 325 j++; 326 } 327 } 328 return x; 329 } 330 331 /*--------------------------------------------------------------*/ 332 /*---------------- Private Methods ----------------*/ 333 /*--------------------------------------------------------------*/ 334 verify()335 public boolean verify(){ 336 if(keys==null){return true;} 337 int numValues=0; 338 int numFound=0; 339 for(int i=0; i<keys.length; i++){ 340 final long key=keys[i]; 341 final long value=values[i]; 342 343 if(key==invalid){ 344 if(value!=0){ 345 assert(false) : i+", "+key+", "+value; 346 return false; 347 } 348 }else{ 349 numValues++; 350 if(value<1){ 351 assert(false) : i+", "+key+", "+value; 352 return false; 353 } 354 final int cell=findCell(key); 355 if(i==cell){ 356 numFound++; 357 }else{ 358 assert(false) : i+", "+key+", "+value+", "+cell+"\n"+((cell>=0) ? keys[cell]+", "+values[cell]+"\n" : ""); 359 return false; 360 } 361 } 362 } 363 boolean pass=(numValues==numFound && numValues==size); 364 assert(pass) : numValues+", "+numFound+", "+size; 365 return pass; 366 } 367 rehashFrom(int initial)368 private void rehashFrom(int initial){ 369 if(size<1){return;} 370 final int limit=keys.length; 371 for(int cell=initial+1; cell<limit; cell++){ 372 final long x=keys[cell]; 373 if(x==invalid){return;} 374 rehashCell(cell); 375 } 376 for(int cell=0; cell<initial; cell++){ 377 final long x=keys[cell]; 378 if(x==invalid){return;} 379 rehashCell(cell); 380 } 381 } 382 rehashCell(final int cell)383 private boolean rehashCell(final int cell){ 384 final long key=keys[cell]; 385 final long value=values[cell]; 386 assert(key!=invalid); 387 if(key==invalid){resetInvalid();} 388 final int dest=findCellOrEmpty(key); 389 if(cell==dest){return false;} 390 assert(keys[dest]==invalid); 391 keys[cell]=invalid; 392 values[cell]=0; 393 keys[dest]=key; 394 values[dest]=value; 395 return true; 396 } 397 resetInvalid()398 private void resetInvalid(){ 399 final long old=invalid; 400 long x=invalid; 401 while(x==old || contains(x)){x=randy.nextLong()|MINMASK;} 402 assert(x<0); 403 invalid=x; 404 for(int i=0; i<keys.length; i++){ 405 if(keys[i]==old){ 406 assert(values[i]==0); 407 keys[i]=invalid; 408 } 409 } 410 } 411 412 private int findCell(final long key){ 413 if(key==invalid){return -1;} 414 415 final int limit=keys.length, initial=(int)((key&MASK)%modulus); 416 for(int cell=initial; cell<limit; cell++){ 417 final long x=keys[cell]; 418 if(x==key){return cell;} 419 if(x==invalid){return -1;} 420 } 421 for(int cell=0; cell<initial; cell++){ 422 final long x=keys[cell]; 423 if(x==key){return cell;} 424 if(x==invalid){return -1;} 425 } 426 return -1; 427 } 428 429 private int findCellOrEmpty(final long key){ 430 assert(key!=invalid) : "Collision - this should have been intercepted."; 431 432 final int limit=keys.length, initial=(int)((key&MASK)%modulus); 433 for(int cell=initial; cell<limit; cell++){ 434 final long x=keys[cell]; 435 if(x==key || x==invalid){return cell;} 436 } 437 for(int cell=0; cell<initial; cell++){ 438 final long x=keys[cell]; 439 if(x==key || x==invalid){return cell;} 440 } 441 throw new RuntimeException("No empty cells - size="+size+", limit="+limit); 442 } 443 444 private final void resize(){ 445 assert(size>=sizeLimit); 446 resize(keys.length*2L+1); 447 } 448 449 private final void resize(final long size2){ 450 // assert(verify()); //123 451 assert(size2>size) : size+", "+size2; 452 long newPrime=Primes.primeAtLeast(size2); 453 if(newPrime+extra>Integer.MAX_VALUE){ 454 newPrime=Primes.primeAtMost(Integer.MAX_VALUE-extra); 455 } 456 assert(newPrime>modulus) : "Overflow: "+size+", "+size2+", "+modulus+", "+newPrime; 457 modulus=(int)newPrime; 458 459 final int size3=(int)(newPrime+extra); 460 sizeLimit=(int)(modulus*loadFactor); 461 final long[] oldKeys=keys; 462 final long[] oldValues=values; 463 keys=KillSwitch.allocLong1D(size3); 464 values=KillSwitch.allocLong1D(size3); 465 Arrays.fill(keys, invalid); 466 467 // System.err.println("Resizing "+(old==null ? "null" : ""+old.length)+" to "+size3); 468 469 if(size<1){return;} 470 471 size=0; 472 for(int i=0; i<oldKeys.length; i++){ 473 long key=oldKeys[i]; 474 if(key!=invalid){ 475 put(key, oldValues[i]); 476 } 477 } 478 // assert(verify()); //123 479 } 480 481 /*--------------------------------------------------------------*/ 482 /*---------------- Getters ----------------*/ 483 /*--------------------------------------------------------------*/ 484 485 public long[] keys() {return keys;} 486 487 public long[] values() {return values;} 488 489 public long invalid() {return invalid;} 490 491 /*--------------------------------------------------------------*/ 492 /*---------------- Fields ----------------*/ 493 /*--------------------------------------------------------------*/ 494 495 private long[] keys; 496 private long[] values; 497 private int size=0; 498 /** Value for empty cells */ 499 private long invalid; 500 private int modulus; 501 private int sizeLimit; 502 private final float loadFactor; 503 504 private static final Random randy=new Random(1); 505 private static final long MASK=Long.MAX_VALUE; 506 private static final long MINMASK=Long.MIN_VALUE; 507 508 private static final int extra=10; 509 510 } 511