001package Torello.Java.Additional; 002 003import java.util.*; 004 005import java.util.function.BiConsumer; 006 007/** 008 * A great class, heavily used by the Java Doc Upgrader Tool, that extends Java's 009 * <CODE>TreeMap</CODE> class to eliminate "Least Recently Used" elements from the 010 * Map, <I>effectively creating an <CODE>'in-memory cache'</CODE> for the tool.</I> 011 * 012 * <EMBED CLASS='external-html' DATA-FILE-ID=LRUTREEMAP> 013 * 014 * @param <K> This is the type for the map {@code 'key'}. It should be thought of as 015 * identical to the {@code java.util.TreeMap} type-parameter {@code 'K'} 016 * 017 * @param <V> This is the type used by the map for {@code 'value'}. It should be thought of as 018 * identical to the {@code java.util.TreeMap} type-parameter {@code 'V'} 019 */ 020public class LRUTreeMap<K, V> extends TreeMap<K, V> 021{ 022 /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUID> */ 023 protected static final long serialVersionUID = 1; 024 025 // ******************************************************************************************** 026 // ******************************************************************************************** 027 // INHERITED FROM: java.util.TreeMap - THESE WORK JUST-FINE, NO @Override NEEDED 028 // ******************************************************************************************** 029 // ******************************************************************************************** 030 031 032 // public int size() 033 034 // public Comparator<? super K> comparator() 035 036 // public Set<K> keySet(); 037 038 // public NavigableSet<K> navigableKeySet() 039 040 // public NavigableSet<K> descendingKeySet() 041 042 // public Collection<V> values() 043 044 // public Set<Map.Entry<K,V>> entrySet() 045 046 // public NavigableMap<K,V> descendingMap() 047 048 // public NavigableMap<K,V> subMap 049 // (K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) 050 051 // public NavigableMap<K,V> headMap(K toKey, boolean inclusive) 052 053 // public NavigableMap<K,V> tailMap(K fromKey, boolean inclusive) 054 055 // public SortedMap<K,V> subMap(K fromKey, K toKey) 056 057 // public SortedMap<K,V> headMap(K toKey) 058 059 // public SortedMap<K,V> tailMap(K fromKey) 060 061 // public void replaceAll(BiFunction<? super K,? super V,? extends V> function) 062 063 064 // ******************************************************************************************** 065 // ******************************************************************************************** 066 // LRUTreeMaps "Helper Class" - Keeps Track of each Tree-Node's "Place In Line" 067 // ******************************************************************************************** 068 // ******************************************************************************************** 069 070 071 // This just keeps track of which Key-Value Pairs are the Least-Recently-Used (and of course) 072 // The Most-Recently-Used 073 private final class LRUTreeMaps 074 { 075 // Each TreeMap has TWO INTERNAL "Place In Line" TreeMap's 076 // 077 // NOTE-ABOUT-EFFICIENCY: This class was developed for use with the EmbedTag's of the 078 // "Java Doc Upgrader" package. These two additional TreeMap's do not create additional 079 // keys, just references to keys. Furthermore, even if a "Reference to Key" was a real 080 // memory-hog (which it isn't, a 'refernce' is just a 'long'), the Key's themselves are 081 // all 8-character String's 082 // 083 // The values in this map are 1,000 - 2,000 node long html-page vectors. These can be 084 // quite memory-intensive. Holding more than 500 of them might slow Java down to an 085 // "unusable state". In any case, only the "Keys" are saved multiple times (not the 086 // web-pages, and furthermore, only a **REFERENCE** to the keys are saved multiple times) 087 088 // A TreeMap that can be queried using the "Key" (the same "Key") that the real OUTER 089 // "Real" TreeMap is using. This is just a "Mirror" We need this because the OUTER 090 // tree map is pruned when it grows larger than MAX_NUM_ELEMENTS. 091 private final TreeMap<K, Integer> keyToPlaceInLine = new TreeMap<>(); 092 private final TreeMap<Integer, K> placeInLineToKey = new TreeMap<>(); 093 094 // The "Place In Line" is just the value in this counterLRU. This counter is incremented 095 // every single time that it is assigned to a key-value pair. 096 private int counterLRU = 0; 097 098 final void added(K key) 099 { 100 Integer possiblyNullPreviousPlaceInLine = keyToPlaceInLine.put(key, counterLRU); 101 placeInLineToKey.put(counterLRU, key); 102 counterLRU++; 103 104 if (possiblyNullPreviousPlaceInLine != null) 105 placeInLineToKey.remove(possiblyNullPreviousPlaceInLine); 106 107 // NOTE: .size() is an OUTER TREE-MAP METHOD-REFERENCE 108 else if (size() > MAX_NUM_ELEMENTS) 109 { 110 // This is difficult to read. 111 // 112 // 1) 'pollFirstEntry' removes *AND* returns the key/place-in-line combo from the 113 // first of the two Queue-Information TreeMap's 114 // 2) Therefore: 'beingRemoved' has the 'key' (saved as the value), and the 115 // 'place-in-line' saved as the key. This needs to be removed from the second 116 // of the two Queue-Information TreeMap's 117 118 Map.Entry<Integer, K> beingRemoved = placeInLineToKey.pollFirstEntry(); 119 K keyBeingRemoved = beingRemoved.getValue(); 120 121 // 'placeInLineToKey' has had this 'key' removed, *NOW* (here) the 122 // 'keyToPlaceInLine' will be purged of this key. 123 124 keyToPlaceInLine.remove(keyBeingRemoved); 125 126 // Make a call to the **OUTER-CLASS*, We just eliminated the "Place In Line" 127 // Queue-TreeMap's (of which there are TWO). The last thing is to remove the 128 // key-value from the actual (Main) TreeMap - the one the user has. 129 // 130 // THIS MEAS: if we call the usual (outer-class) 'remove' method, it would remove 131 // the key from the main TreeMap - AND THEN try to remove the 132 // 'place-in-line' information AGAIN! (What was just done in the 133 // previous lines) 134 // 135 // INSTEAD: a method "removeINTERNAL" is being built. 136 137 // System.out.println("Removig [" + keyBeingRemoved + "] from LRUTreeMap"); 138 139 removeINTERNAL(keyBeingRemoved); 140 } 141 } 142 143 final void removed(K key) 144 { placeInLineToKey.remove(keyToPlaceInLine.remove(key)); } 145 146 final void changed(K key) 147 { 148 // System.out.println("Changed: " + key); 149 Integer previousPlaceInLine = keyToPlaceInLine.put(key, counterLRU); 150 151 placeInLineToKey.remove(previousPlaceInLine); 152 placeInLineToKey.put(counterLRU, key); 153 154 counterLRU++; 155 } 156 157 final void clear() 158 { 159 placeInLineToKey.clear(); 160 keyToPlaceInLine.clear(); 161 } 162 163 final int trimToFit() 164 { 165 int numExtra = size() - MAX_NUM_ELEMENTS; 166 167 if (numExtra <= 0) return 0; 168 169 // If you read it - 'descendingMap' is sort-of *LINKED* to the map from which it 170 // originated. When you remove something from the descending map, you also remove 171 // from the original map from which it was created. 172 173 NavigableMap<Integer, K> entriesToRemove = placeInLineToKey.descendingMap(); 174 175 while (numExtra-- > 0) 176 keyToPlaceInLine.remove(entriesToRemove.pollFirstEntry().getValue()); 177 178 // This is how many elements were removed 179 return numExtra; 180 } 181 } 182 183 184 // ******************************************************************************************** 185 // ******************************************************************************************** 186 // LRUTreeMap: This class extends "TreeMap" - **AND** These are the methods being added 187 // ******************************************************************************************** 188 // ******************************************************************************************** 189 190 191 // This one is only used above, once. 192 private void removeINTERNAL(Object key) { super.remove(key); } 193 194 /** 195 * Keeps a record of the <I>'Ordering of Use'</I> for the elements in {@code 'this'} map. The 196 * values stored here are set by a counter that increases with each use. 197 */ 198 protected LRUTreeMaps mapLRU = new LRUTreeMaps(); 199 200 /** 201 * This is, literally, the maximum number of Key-Value pairs allowed into this instance of 202 * {@code LRUTreeMap}. When an insert or {@code 'put'} operation is made after the tree has 203 * reached this maximum size, it will remove the oldest element in the tree, first, before 204 * inserting the new addition(s)j. 205 */ 206 protected int MAX_NUM_ELEMENTS; 207 208 /** 209 * This merely retrieves the value stored in {@link #MAX_NUM_ELEMENTS}. 210 * 211 * @return The maximum number of elements allowed in this tree before it begins discarding 212 * Key-Value Pairs. 213 */ 214 public int getMaxNumElements() { return this.MAX_NUM_ELEMENTS; } 215 216 /** 217 * Sets a new value for the maximum number of elements allowed into this tree. If this number 218 * is smaller than the current size of the tree, then the oldest elements will be removed until 219 * the tree has a {@code size()} smaller than the value of {@code 'maxNumElements'} 220 * 221 * @param maxNumElements This is thee new maximum allowable size for this {@code LRUTreeMap} 222 * instance. 223 * 224 * @return The number of elements that had to be removed from this tree in order to adhere to 225 * this new size-specifier. {@code '0'} is returned if no elements were removed. 226 * 227 * @throws IllegalArgumentException The value provided to {@code 'maxNumElements'} must be 228 * at least {@code '3'}, or this exception throws. 229 */ 230 public int setMaxNumElements(int maxNumElements) 231 { 232 CHECK_MAX_NUM_ELEMENTS(maxNumElements); 233 234 this.MAX_NUM_ELEMENTS = maxNumElements; 235 236 // No elements need to be removed from the tree. 237 if (this.size() <= maxNumElements) return 0; 238 239 // Returns how many elements were removed. 240 return mapLRU.trimToFit(); 241 } 242 243 private void CHECK_NEW_MAP_SIZE(int mapSize) 244 { 245 if (mapSize > this.MAX_NUM_ELEMENTS) throw new IllegalArgumentException( 246 "The size of the 'map' provided is [" + mapSize + "], unfortunately this is " + 247 "greater than 'maxNumElements' [" + this.MAX_NUM_ELEMENTS + ']' 248 ); 249 } 250 251 private void CHECK_MAX_NUM_ELEMENTS(int maxNumElements) 252 { 253 if (maxNumElements < 3) throw new IllegalArgumentException( 254 "The value provided to 'maxNumElements' [" + maxNumElements + "] must be greater " + 255 "than 2" 256 ); 257 } 258 259 260 // ******************************************************************************************** 261 // ******************************************************************************************** 262 // The Usual TreeMap Constructors - Copied from JDK 'java.util.TreeMap' 263 // ******************************************************************************************** 264 // ******************************************************************************************** 265 266 267 /** 268 * Constructs a new, empty tree map, using the <I>natural ordering</I> of its keys. All keys 269 * inserted into the map must implement the interface {@code 'Comparable'}. Furthermore, all 270 * such keys must be mutually comparable: {@code k1.compareTo(k2)}, and must not throw a 271 * {@code ClassCastException} for any keys k1 and k2 in the map. If the user attempts to put 272 * a key into the map that violates this constraint (for example, the user attempts to put a 273 * string key into a map whose keys are integers), the {@code put(Object key, Object value)} 274 * call will throw a {@code ClassCastException}. 275 * 276 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 277 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 278 * 279 * @param maxNumElements The maximum allowable number of elements that may be inserted into 280 * {@code 'this'} tree before older elements are discarded. 281 */ 282 public LRUTreeMap(int maxNumElements) 283 { 284 super(); 285 286 CHECK_MAX_NUM_ELEMENTS(maxNumElements); 287 288 this.MAX_NUM_ELEMENTS = maxNumElements; 289 } 290 291 /** 292 * Constructs a new, empty tree map, ordered according to the given comparator. All keys 293 * inserted into the map must be mutually comparable by the given comparator: 294 * {@code comparator.compare(k1, k2)}, must not throw a {@code ClassCastException} for any keys 295 * k1 and k2 in the map. If the user attempts to put a key into the map that violates this 296 * constraint, the {@code put(Object key, Object value)} 297 * call will throw a {@code ClassCastException}. 298 * 299 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 300 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 301 * 302 * @param comparator the {@code java.util.Comparator} that will be used to order {@code 'this'} 303 * map. If null, the <I>natural ordering</I> of the keys will be used. 304 * 305 * @param maxNumElements The maximum allowable number of elements that may be inserted into 306 * {@code 'this'} tree before older elements are discarded. 307 */ 308 public LRUTreeMap(Comparator<? super K> comparator, int maxNumElements) 309 { 310 super(comparator); 311 312 CHECK_MAX_NUM_ELEMENTS(maxNumElements); 313 314 this.MAX_NUM_ELEMENTS = maxNumElements; 315 } 316 317 /** 318 * Constructs a new tree map containing the same mappings as the given map, ordered according 319 * to the <I>natural ordering</I> of its keys. All keys inserted into the new map must 320 * implement the interface {@code 'Comparable'}. Furthermore, all such keys must be mutually 321 * comparable: {@code k1.compareTo(k2)} must not throw a {@code ClassCastException} for any 322 * keys k1 and k2 in the map. This method runs in <B>{@code n * log(n)}</B> time. 323 * 324 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 325 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 326 * 327 * @param m the map whose mappings are to be placed in {@code 'this'} map 328 * 329 * @param maxNumElements The maximum allowable number of elements that may be inserted into 330 * {@code 'this'} tree before older elements are discarded. 331 * 332 * @throws ClassCastException if the keys in {@code m} are not Comparable, or are not mutually 333 * comparable 334 * 335 * @throws NullPointerException if the specified map is null 336 */ 337 public LRUTreeMap(Map<? extends K,? extends V> m, int maxNumElements) 338 { 339 super(m); 340 341 // Brief Check to make sure that 'maxNumElements' is at least 3 (and non-negative) 342 CHECK_MAX_NUM_ELEMENTS(maxNumElements); 343 344 this.MAX_NUM_ELEMENTS = maxNumElements; 345 346 // Throws if map.size() is greater than MAX_NUM_ELEMENTS 347 CHECK_NEW_MAP_SIZE(m.size()); 348 349 for (K key : m.keySet()) mapLRU.added(key); 350 } 351 352 /** 353 * Constructs a new tree map containing the same mappings and using the same ordering as the 354 * specified sorted map. This method runs in linear time. 355 * 356 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 357 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 358 * 359 * @param m the sorted map whose mappings are to be placed in {@code 'this'} map, and whose 360 * {@code Comparator} is to be used to sort {@code 'this'} map 361 * 362 * @param maxNumElements The maximum allowable number of elements that may be inserted into 363 * {@code 'this'} tree before older elements are discarded. 364 * 365 * @throws NullPointerException if the specified map is null 366 */ 367 public LRUTreeMap(SortedMap<K,? extends V> m, int maxNumElements) 368 { 369 super(m); 370 371 // Brief Check to make sure that 'maxNumElements' is at least 3 (and non-negative) 372 CHECK_MAX_NUM_ELEMENTS(maxNumElements); 373 374 this.MAX_NUM_ELEMENTS = maxNumElements; 375 376 // Throws if map.size() is greater than MAX_NUM_ELEMENTS 377 CHECK_NEW_MAP_SIZE(m.size()); 378 379 for (K key : m.keySet()) mapLRU.added(key); 380 } 381 382 383 // ******************************************************************************************** 384 // ******************************************************************************************** 385 // The Usual TreeMap Methods - Copied from JDK 'java.util.TreeMap' 386 // ******************************************************************************************** 387 // ******************************************************************************************** 388 389 390 /** 391 * Returns {@code TRUE} if {@code 'this'} map contains a mapping for the specified 392 * {@code 'key'}. 393 * 394 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 395 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 396 * 397 * <BR /><B STYLE='color:red'>UPDATES:</B> After invoking the {@code 'super'} of this 398 * <I>overridden method</I>, the internal LRU-Counter is updated, if necessary. 399 * 400 * @param key key whose presence in {@code 'this'} map is to be tested 401 * 402 * @return {@code TRUE} if {@code 'this'} map contains a mapping for the specified {@code 'key'} 403 * 404 * @throws ClassCastException if the specified {@code 'key'} cannot be compared with the keys 405 * currently in {@code 'this'} map 406 * 407 * @throws NullPointerException if the specified {@code 'key'} is null and {@code 'this'} map 408 * uses <I>natural ordering</I>, or its {@code Comparator} does not permit null keys 409 */ 410 @SuppressWarnings("unchecked") // If the map contains "key", it must have been of type "K" 411 public boolean containsKey(Object key) 412 { 413 boolean ret = super.containsKey(key); 414 415 // Change the time-stamp-counter for 'key' (if it was present) 416 // It is now the 'Most Recently Used' 417 418 if (ret) this.mapLRU.changed((K) key); 419 420 return ret; 421 } 422 423 /** 424 * Returns {@code TRUE} if {@code 'this'} map maps one or more keys to the specified 425 * {@code 'value'}. More formally, returns {@code TRUE} if and only if {@code 'this'} map 426 * contains at least one mapping to a value v such that {@code (value==null ? v==null : 427 * value.equals(v))}. 428 * 429 * <BR /><BR />This operation will probably require time linear in the map size for most 430 * implementations. 431 * 432 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 433 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 434 * 435 * @param value value whose presence in {@code 'this'} map is to be tested 436 * 437 * @return {@code TRUE} if a mapping to value exists; {@code FALSE} otherwise 438 */ 439 public boolean containsValue(Object value) 440 { 441 for (Map.Entry<K, V> entry : this.entrySet()) 442 if (value.equals(entry.getValue())) 443 { 444 // Change the time-stamp-counter for 'entry.key' 445 // These are all now the 'Most Recently Used' keys 446 // 447 // NOTE: The counter-value assigned to each key is in order consistent with the 448 // the order of the entries returned by the iterator. 449 450 this.mapLRU.changed(entry.getKey()); 451 return true; 452 } 453 454 return false; 455 } 456 457 /** 458 * Returns the value to which the specified {@code 'key'} is mapped, or null if {@code 'this'} 459 * map contains no mapping for the {@code 'key'}. More formally, if {@code 'this'} map 460 * contains a mapping from a key {@code k} to a value {@code v} such that {@code 'key'} 461 * compares equal to {@code k} according to the map's ordering, then this method returns 462 * {@code v}; otherwise it returns null. (There can be at most one such mapping.) 463 * 464 * <BR /><BR />A return value of null does not necessarily indicate that the map contains no 465 * mapping for the {@code 'key'}; it's also possible that the map explicitly maps the 466 * {@code 'key'} to null. The {@code 'containsKey'} operation may be used to distinguish these 467 * two cases. 468 * 469 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 470 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 471 * 472 * <BR /><B STYLE='color:red'>UPDATES:</B> After invoking the {@code 'super'} of this 473 * <I>overridden method</I>, the internal LRU-Counter is updated, if necessary. 474 * 475 * @param key the key whose associated value is to be returned 476 * 477 * @return the value to which the specified {@code 'key'} is mapped, or null if 478 * {@code 'this'} map contains no mapping for the {@code 'key'} 479 * 480 * @throws ClassCastException if the specified {@code 'key'} cannot be compared with the keys 481 * currently in {@code 'this'} map 482 * 483 * @throws NullPointerException if the specified {@code 'key'} is null and {@code 'this'} map 484 * uses <I>natural ordering</I>, or its {@code Comparator} does not permit null keys 485 */ 486 @SuppressWarnings("unchecked") // If the map contains "key", it must have been of type "K" 487 public V get(Object key) 488 { 489 V ret = super.get(key); 490 491 // Change the time-stamp-counter for 'key' (if it was present) 492 // It is now the 'Most Recently Used' 493 494 if (ret != null) mapLRU.changed((K) key); 495 496 return ret; 497 } 498 499 /** 500 * Returns the first (lowest) key currently in {@code 'this'} map. 501 * 502 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 503 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 504 * 505 * <BR /><B STYLE='color:red'>UPDATES:</B> After invoking the {@code 'super'} of this 506 * <I>overridden method</I>, the internal LRU-Counter is updated, if necessary. 507 * 508 * @return the first (lowest) key currently in {@code 'this'} map 509 * 510 * @throws NoSuchElementException if {@code 'this'} map is empty 511 */ 512 public K firstKey() 513 { 514 K ret = super.firstKey(); 515 516 // Change the time-stamp-counter for 'first-key' (if present) before returning it. 517 // It is now the 'Most Recently Used' 518 519 if (ret != null) mapLRU.changed(ret); 520 521 return ret; 522 } 523 524 /** 525 * Returns the last (highest) key currently in {@code 'this'} map. 526 * 527 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 528 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 529 * 530 * <BR /><B STYLE='color:red'>UPDATES:</B> After invoking the {@code 'super'} of this 531 * <I>overridden method</I>, the internal LRU-Counter is updated, if necessary. 532 * 533 * @return the last (highest) key currently in {@code 'this'} map 534 * 535 * @throws NoSuchElementException if {@code 'this'} map is empty 536 */ 537 public K lastKey() 538 { 539 K ret = super.lastKey(); 540 541 // Change the time-stamp-counter for 'last-key' (if present) before returning it. 542 // It is now the 'Most Recently Used' 543 544 if (ret != null) mapLRU.changed(ret); 545 546 return ret; 547 } 548 549 /** 550 * Copies all of the mappings from the specified {@code 'map'} to {@code 'this'} map. These 551 * mappings replace any mappings that {@code 'this'} map had for any of the keys currently in 552 * the specified {@code 'map'}. 553 * 554 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 555 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 556 * 557 * <BR /><B STYLE='color:red'>UPDATES:</B> After invoking the {@code 'super'} of this 558 * <I>overridden method</I>, the internal LRU-Counter is updated. 559 * 560 * @param map mappings to be stored in {@code 'this'} map 561 * 562 * @throws ClassCastException if the class of a key or value in the specified map prevents it 563 * from being stored in {@code 'this'} map 564 * 565 * @throws NullPointerException if the specified map is null or the specified map contains a 566 * null key and {@code 'this'} map does not permit null keys 567 */ 568 public void putAll(Map<? extends K,? extends V> map) 569 { 570 // Throws if map.size() is greater than MAX_NUM_ELEMENTS 571 CHECK_NEW_MAP_SIZE(map.size()); 572 573 super.putAll(map); 574 575 // Add all of the entries in 'map' into the (Internal) LRU-Counter class 576 for (K key : map.keySet()) this.mapLRU.added(key); 577 578 if (this.size() > this.MAX_NUM_ELEMENTS) mapLRU.trimToFit(); 579 } 580 581 /** 582 * Associates the specified {@code value} with the specified {@code key} in {@code 'this'} map. 583 * If the map previously contained a mapping for the {@code 'key'}, the old value is replaced. 584 * 585 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 586 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 587 * 588 * <BR /><B STYLE='color:red'>UPDATES:</B> After invoking the {@code 'super'} of this 589 * <I>overridden method</I>, the internal LRU-Counter is updated. 590 * 591 * @param key key with which the specified {@code 'value'} is to be associated 592 * 593 * @param value value to be associated with the specified {@code 'key'} 594 * 595 * @return the previous value associated with {@code 'key'}, or null if there was no mapping 596 * for {@code 'key'}. (A null return can also indicate that the map previously associated 597 * null with key.) 598 * 599 * @throws ClassCastException if the specified {@code 'key'} cannot be compared with the keys 600 * currently in {@code 'this'} map 601 * 602 * @throws NullPointerException if the specified {@code 'key'} is null and {@code 'this'} map 603 * uses <I>natural ordering</I>, or its {@code Comparator} does not permit null keys 604 */ 605 public V put(K key, V value) 606 { 607 V ret = super.put(key, value); 608 609 // Add this 'key' into the (Internal) LRU-Counter class 610 this.mapLRU.added(key); 611 612 return ret; 613 } 614 615 /** 616 * Removes the mapping for this {@code 'key'} from {@code 'this' TreeMap} if present. 617 * 618 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 619 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 620 * 621 * <BR /><B STYLE='color:red'>UPDATES:</B> After invoking the {@code 'super'} of this 622 * <I>overridden method</I>, the internal LRU-Counter is updated, if necessary. 623 * 624 * @param key key for which mapping should be removed 625 * 626 * @return the previous value associated with {@code 'key'}, or null if there was no mapping 627 * for {@code 'key'}. (A null return can also indicate that the map previously associated null 628 * with {@code 'key'}.) 629 * 630 * @throws ClassCastException if the specified {@code 'key'} cannot be compared with the keys 631 * currently in {@code 'this'} map 632 * 633 * @throws NullPointerException if the specified {@code 'key'} is null and {@code 'this'} map 634 * uses <I>natural ordering</I>, or its {@code Comparator} does not permit null keys 635 */ 636 @SuppressWarnings("unchecked") // If the map contained "key", that key must have been type "K" 637 public V remove(Object key) 638 { 639 V ret = super.remove(key); 640 641 // Remove this 'key' from the (Internal) LRU-Counter class, if it was present. 642 if (ret != null) this.mapLRU.removed((K) key); 643 644 return ret; 645 } 646 647 /** 648 * Removes all of the mappings from {@code 'this'} map. The map will be empty after this call 649 * returns. 650 * 651 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 652 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 653 * 654 * <BR /><B STYLE='color:red'>UPDATES:</B> After invoking the {@code 'super'} of this 655 * <I>overridden method</I>, the internal LRU-Counter is updated. 656 */ 657 public void clear() 658 { super.clear(); this.mapLRU.clear(); } 659 660 /** 661 * Returns a shallow copy of this {@code LRUTreeMap} instance. (The keys and values themselves 662 * are not cloned.) 663 * 664 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 665 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 666 * 667 * @return a shallow copy of {@code 'this'} map 668 */ 669 public Object clone() 670 { return new LRUTreeMap<K, V>(this, this.MAX_NUM_ELEMENTS); } 671 672 /** 673 * Returns a key-value mapping associated with the least key in {@code 'this'} map, or null if 674 * the map is empty. 675 * 676 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 677 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 678 * 679 * <BR /><B STYLE='color:red'>UPDATES:</B> After invoking the {@code 'super'} of this 680 * <I>overridden method</I>, the internal LRU-Counter is updated, if necessary. 681 * 682 * @return an entry with the least key, or null if {@code 'this'} map is empty 683 */ 684 public Map.Entry<K,V> firstEntry() 685 { 686 Map.Entry<K, V> ret = super.firstEntry(); 687 688 // Change the time-stamp-counter for 'first-key' before returning it. 689 // It is now the 'Most Recently Used' 690 // 691 // NOTE: The (internal) mapLRU only needs to watch the 'key', not the 'key-value' pair. 692 693 if (ret != null) this.mapLRU.changed(ret.getKey()); 694 695 return ret; 696 } 697 698 /** 699 * Returns a key-value mapping associated with the greatest key in {@code 'this'} map, or 700 * null if the map is empty. 701 * 702 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 703 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 704 * 705 * <BR /><B STYLE='color:red'>UPDATES:</B> After invoking the {@code 'super'} of this 706 * <I>overridden method</I>, the internal LRU-Counter is updated, if necessary. 707 * 708 * @return an entry with the greatest key, or null if {@code 'this'} map is empty 709 */ 710 public Map.Entry<K,V> lastEntry() 711 { 712 Map.Entry<K, V> ret = super.lastEntry(); 713 714 // Change the time-stamp-counter for 'last-key' before returning it. 715 // It is now the 'Most Recently Used' 716 // 717 // NOTE: The (internal) mapLRU only needs to watch the 'key', not the 'key-value' pair. 718 719 if (ret != null) this.mapLRU.changed(ret.getKey()); 720 721 return ret; 722 } 723 724 /** 725 * Removes and returns a key-value mapping associated with the least key in {@code 'this'} map, 726 * or null if the map is empty. 727 * 728 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 729 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 730 * 731 * <BR /><B STYLE='color:red'>UPDATES:</B> After invoking the {@code 'super'} of this 732 * <I>overridden method</I>, the internal LRU-Counter is updated, if necessary. 733 * 734 * @return the removed first entry of {@code 'this'} map, or null if {@code 'this'} map is 735 * empty 736 */ 737 public Map.Entry<K,V> pollFirstEntry() 738 { 739 Map.Entry<K, V> ret = super.pollFirstEntry(); 740 741 // If there was a 'first key', it was just removed from the TreeMap 742 // Make sure to remove it from the (internal) LRU-Counter class 743 744 if (ret != null) this.mapLRU.removed(ret.getKey()); 745 746 return ret; 747 } 748 749 /** 750 * Removes and returns a key-value mapping associated with the greatest key in this 751 * map, or null if the map is empty. 752 * 753 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 754 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 755 * 756 * <BR /><B STYLE='color:red'>UPDATES:</B> After invoking the {@code 'super'} of this 757 * <I>overridden method</I>, the internal LRU-Counter is updated, if necessary. 758 * 759 * @return the removed last entry of {@code 'this'} map, or null if {@code 'this'} map is empty 760 */ 761 public Map.Entry<K,V> pollLastEntry() 762 { 763 Map.Entry<K, V> ret = super.pollLastEntry(); 764 765 // If there was a 'last key', it was just removed from the TreeMap 766 // Make sure to remove it from the (internal) LRU-Counter class 767 768 if (ret != null) this.mapLRU.removed(ret.getKey()); 769 770 return ret; 771 } 772 773 /** 774 * Returns a key-value mapping associated with the greatest key strictly less than the 775 * given {@code 'key'}, or null if there is no such key. 776 * 777 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 778 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 779 * 780 * <BR /><B STYLE='color:red'>UPDATES:</B> After invoking the {@code 'super'} of this 781 * <I>overridden method</I>, the internal LRU-Counter is updated, if necessary. 782 * 783 * @param key the key 784 * 785 * @return an entry with the greatest key less than {@code 'key'}, or null if there is no such 786 * key 787 * 788 * @throws ClassCastException if the specified {@code 'key'} cannot be compared with the keys 789 * currently in the map 790 * 791 * @throws NullPointerException if the specified {@code 'key'} is null and {@code 'this'} map 792 * uses <I>natural ordering</I>, or its {@code Comparator} does not permit null keys 793 */ 794 public Map.Entry<K,V> lowerEntry(K key) 795 { 796 Map.Entry<K, V> ret = super.lowerEntry(key); 797 798 // Change the time-stamp-counter for the lower-entry 'key' (if there is one) 799 // before returning it. It is now the 'Most Recently Used' 800 801 if (ret != null) this.mapLRU.changed(ret.getKey()); 802 803 return ret; 804 } 805 806 /** 807 * Returns the greatest key strictly less than the given {@code 'key'}, or null if there is no 808 * such key. 809 * 810 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 811 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 812 * 813 * <BR /><B STYLE='color:red'>UPDATES:</B> After invoking the {@code 'super'} of this 814 * <I>overridden method</I>, the internal LRU-Counter is updated, if necessary. 815 * 816 * @param key the key 817 * 818 * @return the greatest key less than {@code 'key'}, or null if there is no such key 819 * 820 * @throws ClassCastException if the specified {@code 'key'} cannot be compared with the keys 821 * currently in {@code 'this'} map 822 * 823 * @throws NullPointerException if the specified {@code 'key'} is null and {@code 'this'} map 824 * uses <I>natural ordering</I>, or its {@code Comparator} does not permit null keys 825 */ 826 public K lowerKey(K key) 827 { 828 K ret = super.lowerKey(key); 829 830 // Change the time-stamp-counter for the lower-key (if there is one) 831 // before returning it. It is now the 'Most Recently Used' 832 833 if (ret != null) this.mapLRU.changed(ret); 834 835 return ret; 836 } 837 838 /** 839 * Returns a key-value mapping associated with the greatest key less than or equal to the 840 * given {@code 'key'}, or null if there is no such key. 841 * 842 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 843 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 844 * 845 * <BR /><B STYLE='color:red'>UPDATES:</B> After invoking the {@code 'super'} of this 846 * <I>overridden method</I>, the internal LRU-Counter is updated, if necessary. 847 * 848 * @param key the key 849 * 850 * @return an entry with the greatest key less than or equal to {@code 'key'}, or null if there 851 * is no such key 852 * 853 * @throws ClassCastException if the specified {@code 'key'} cannot be compared with the keys 854 * currently in {@code 'this'} map 855 * 856 * @throws NullPointerException if the specified {@code 'key'} is null and {@code 'this'} map 857 * uses <I>natural ordering</I>, or its {@code Comparator} does not permit null keys 858 */ 859 public Map.Entry<K,V> floorEntry(K key) 860 { 861 Map.Entry<K, V> ret = super.floorEntry(key); 862 863 // Change the time-stamp-counter for the 'key' of the floor-entry (if there is one) 864 // before returning it. It is now the 'Most Recently Used' 865 866 if (ret != null) this.mapLRU.changed(ret.getKey()); 867 868 return ret; 869 } 870 871 /** 872 * Returns the greatest key less than or equal to the given {@code 'key'}, or null if there is 873 * no such key. 874 * 875 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 876 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 877 * 878 * <BR /><B STYLE='color:red'>UPDATES:</B> After invoking the {@code 'super'} of this 879 * <I>overridden method</I>, the internal LRU-Counter is updated, if necessary. 880 * 881 * @param key the key 882 * 883 * @return the greatest key less than or equal to {@code 'key'}, or null if there is no such 884 * key 885 * 886 * @throws ClassCastException if the specified {@code 'key'} cannot be compared with the keys 887 * currently in the map 888 * 889 * @throws NullPointerException if the specified {@code 'key'} is null and {@code 'this'} map 890 * uses <I>natural ordering</I>, or its {@code Comparator} does not permit null keys 891 */ 892 public K floorKey(K key) 893 { 894 K ret = super.floorKey(key); 895 896 // Change the time-stamp-counter for the floor-key (if there is one) 897 // before returning it. It is now the 'Most Recently Used' 898 899 if (ret != null) this.mapLRU.changed(ret); 900 901 return ret; 902 } 903 904 /** 905 * Returns a key-value mapping associated with the least key greater than or equal to 906 * the given {@code 'key'}, or null if there is no such key. 907 * 908 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 909 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 910 * 911 * <BR /><B STYLE='color:red'>UPDATES:</B> After invoking the {@code 'super'} of this 912 * <I>overridden method</I>, the internal LRU-Counter is updated, if necessary. 913 * 914 * @param key the key 915 * 916 * @return an entry with the least key greater than or equal to {@code 'key'}, or null if 917 * there is no such key 918 * 919 * @throws ClassCastException if the specified {@code 'key'} cannot be compared with the keys 920 * currently in the map 921 * 922 * @throws NullPointerException if the specified {@code 'key'} is null and {@code 'this'} map 923 * uses <I>natural ordering</I>, or its {@code Comparator} does not permit null keys 924 */ 925 public Map.Entry<K,V> ceilingEntry(K key) 926 { 927 Map.Entry<K, V> ret = super.ceilingEntry(key); 928 929 // Change the time-stamp-counter for the 'key' of the ceiling-entry (if there is one) 930 // before returning it. It is now the 'Most Recently Used' 931 932 if (ret != null) this.mapLRU.changed(ret.getKey()); 933 934 return ret; 935 } 936 937 /** 938 * Returns the least key greater than or equal to the given {@code 'key'}, or null if there is 939 * no such key. 940 * 941 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 942 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 943 * 944 * <BR /><B STYLE='color:red'>UPDATES:</B> After invoking the {@code 'super'} of this 945 * <I>overridden method</I>, the internal LRU-Counter is updated, if necessary. 946 * 947 * @param key the key 948 * 949 * @return the least key greater than or equal to {@code 'key'}, or null if there is no such 950 * key 951 * 952 * @throws ClassCastException if the specified {@code 'key'} cannot be compared with the keys 953 * currently in the map 954 * 955 * @throws NullPointerException if the specified {@code 'key'} is null and {@code 'this'} map 956 * uses <I>natural ordering</I>, or its {@code Comparator} does not permit null keys 957 */ 958 public K ceilingKey(K key) 959 { 960 K ret = super.ceilingKey(key); 961 962 // Change the time-stamp-counter for the ceiling-key (if there is one) 963 // before returning it. It is now the 'Most Recently Used' 964 965 if (ret != null) this.mapLRU.changed(ret); 966 967 return ret; 968 } 969 970 /** 971 * Returns a key-value mapping associated with the least key strictly greater than the 972 * given {@code 'key'}, or null if there is no such key. 973 * 974 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 975 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 976 * 977 * <BR /><B STYLE='color:red'>UPDATES:</B> After invoking the {@code 'super'} of this 978 * <I>overridden method</I>, the internal LRU-Counter is updated, if necessary. 979 * 980 * @param key the key 981 * 982 * @return an entry with the least key greater than key, or null if there is no such key 983 * 984 * @throws ClassCastException if the specified {@code 'key'} cannot be compared with the keys 985 * currently in the map 986 * 987 * @throws NullPointerException if the specified {@code 'key'} is null and {@code 'this'} map 988 * uses <I>natural ordering</I>, or its {@code Comparator} does not permit null keys 989 */ 990 public Map.Entry<K,V> higherEntry(K key) 991 { 992 Map.Entry<K, V> ret = super.higherEntry(key); 993 994 // Change the time-stamp-counter for the 'key' of the higher-entry (if there is one) 995 // before returning it. It is now the 'Most Recently Used' 996 997 if (ret != null) this.mapLRU.changed(ret.getKey()); 998 999 return ret; 1000 } 1001 1002 /** 1003 * Returns the least key strictly greater than the given {@code 'key'}, or null if 1004 * there is no such key. 1005 * 1006 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 1007 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 1008 * 1009 * <BR /><B STYLE='color:red'>UPDATES:</B> After invoking the {@code 'super'} of this 1010 * <I>overridden method</I>, the internal LRU-Counter is updated, if necessary. 1011 * 1012 * @param key the key 1013 * 1014 * @return the least key greater than {@code 'key'}, or null if there is no such key 1015 * 1016 * @throws ClassCastException if the specified {@code 'key'} cannot be compared with the keys 1017 * currently in {@code 'this'} map 1018 * 1019 * @throws NullPointerException if the specified {@code 'key'} is null and {@code 'this'} map 1020 * uses <I>natural ordering</I>, or its {@code Comparator} does not permit null keys 1021 */ 1022 public K higherKey(K key) 1023 { 1024 K ret = super.higherKey(key); 1025 1026 // Change the time-stamp-counter for the higher-key (if there is one) 1027 // before returning it. It is now the 'Most Recently Used' 1028 1029 if (ret != null) this.mapLRU.changed(ret); 1030 1031 return ret; 1032 } 1033 1034 /** 1035 * Replaces the entry for the specified {@code 'key'} only if currently mapped to the specified 1036 * {@code 'value'}. 1037 * 1038 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 1039 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 1040 * 1041 * <BR /><B STYLE='color:red'>UPDATES:</B> After invoking the {@code 'super'} of this 1042 * <I>overridden method</I>, the internal LRU-Counter is updated, if necessary. 1043 * 1044 * @param key key with which the specified {@code 'value'} is associated 1045 * @param oldValue value expected to be associated with the specified {@code 'key'} 1046 * @param newValue value to be associated with the specified {@code 'key'} 1047 * 1048 * @return {@code TRUE} if the value was replaced 1049 */ 1050 public boolean replace(K key, V oldValue, V newValue) 1051 { 1052 boolean ret = super.replace(key, oldValue, newValue); 1053 1054 // Change the time-stamp-counter for 'key' - if-and-only-if it was present and it mapped 1055 // to 'oldValue' 1056 // If both of these conditions were met, then it is now the 'Most Recently Used' 1057 1058 if (ret) this.mapLRU.changed(key); 1059 1060 return ret; 1061 } 1062 1063 /** 1064 * Replaces the entry for the specified {@code 'key'} only if it is currently mapped to some 1065 * value. 1066 * 1067 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 1068 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 1069 * 1070 * <BR /><B STYLE='color:red'>UPDATES:</B> After invoking the {@code 'super'} of this 1071 * <I>overridden method</I>, the internal LRU-Counter is updated, if necessary. 1072 * 1073 * @param key key with which the specified {@code 'value'} is associated 1074 * @param value value to be associated with the specified {@code 'key'} 1075 * 1076 * @return the previous value associated with the specified {@code 'key'}, or null if there was 1077 * no mapping for the {@code 'key'}. (A null return can also indicate that the map previously 1078 * associated null with the key, if the implementation supports null values.) 1079 */ 1080 public V replace(K key, V value) 1081 { 1082 V ret = super.replace(key, value); 1083 1084 // Change the time-stamp-counter for 'key' - if-and-only-if it was present and mapped 1085 // to any value. 1086 // If it was, then it is now the 'Most Recently Used' 1087 1088 if (ret != null) this.mapLRU.changed(key); 1089 1090 if (this.size() > this.MAX_NUM_ELEMENTS) this.mapLRU.trimToFit(); 1091 1092 return ret; 1093 } 1094 1095 /** 1096 * Performs the given action for each entry in {@code 'this'} map until all entries have been 1097 * processed or the action throws an exception. Unless otherwise specified by the implementing 1098 * class, actions are performed in the order of entry set iteration (if an iteration order is 1099 * specified.) Exceptions thrown by the action are relayed to the caller. 1100 * 1101 * <BR /><BR /><SPAN CLASS=CopiedJDK>Description copied from class: 1102 * {@code java.util.TreeMap<K, V>}, <B>JDK 1.8</B></SPAN> 1103 * 1104 * @param action The action to be performed for each entry 1105 */ 1106 public void forEach(BiConsumer<? super K,? super V> action) 1107 { 1108 super.forEach(action); 1109 1110 // No need to touch the (internal) LRU-Counter class. All keys are being visited 1111 // at the same time. 1112 1113 if (this.size() > this.MAX_NUM_ELEMENTS) this.mapLRU.trimToFit(); 1114 } 1115}