001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.collections4.bidimap; 018 019import java.util.Collection; 020import java.util.Iterator; 021import java.util.Map; 022import java.util.Objects; 023import java.util.Set; 024import java.util.function.Predicate; 025 026import org.apache.commons.collections4.BidiMap; 027import org.apache.commons.collections4.MapIterator; 028import org.apache.commons.collections4.ResettableIterator; 029import org.apache.commons.collections4.collection.AbstractCollectionDecorator; 030import org.apache.commons.collections4.iterators.AbstractIteratorDecorator; 031import org.apache.commons.collections4.keyvalue.AbstractMapEntryDecorator; 032 033/** 034 * Abstract {@link BidiMap} implemented using two maps. 035 * <p> 036 * An implementation can be written simply by implementing the 037 * {@link #createBidiMap(Map, Map, BidiMap)} method. 038 * </p> 039 * 040 * @param <K> the type of the keys in the map 041 * @param <V> the type of the values in the map 042 * @see DualHashBidiMap 043 * @see DualTreeBidiMap 044 * @since 3.0 045 */ 046public abstract class AbstractDualBidiMap<K, V> implements BidiMap<K, V> { 047 048 /** 049 * Inner class MapIterator. 050 * 051 * @param <K> the type of the keys. 052 * @param <V> the type of the values. 053 */ 054 protected static class BidiMapIterator<K, V> implements MapIterator<K, V>, ResettableIterator<K> { 055 056 /** The parent map */ 057 protected final AbstractDualBidiMap<K, V> parent; 058 059 /** The iterator being wrapped */ 060 protected Iterator<Map.Entry<K, V>> iterator; 061 062 /** The last returned entry */ 063 protected Map.Entry<K, V> last; 064 065 /** Whether remove is allowed at present */ 066 protected boolean canRemove; 067 068 /** 069 * Constructs a new instance. 070 * @param parent the parent map 071 */ 072 protected BidiMapIterator(final AbstractDualBidiMap<K, V> parent) { 073 this.parent = parent; 074 this.iterator = parent.normalMap.entrySet().iterator(); 075 } 076 077 @Override 078 public K getKey() { 079 if (last == null) { 080 throw new IllegalStateException( 081 "Iterator getKey() can only be called after next() and before remove()"); 082 } 083 return last.getKey(); 084 } 085 086 @Override 087 public V getValue() { 088 if (last == null) { 089 throw new IllegalStateException( 090 "Iterator getValue() can only be called after next() and before remove()"); 091 } 092 return last.getValue(); 093 } 094 095 @Override 096 public boolean hasNext() { 097 return iterator.hasNext(); 098 } 099 100 @Override 101 public K next() { 102 last = iterator.next(); 103 canRemove = true; 104 return last.getKey(); 105 } 106 107 @Override 108 public void remove() { 109 if (!canRemove) { 110 throw new IllegalStateException("Iterator remove() can only be called once after next()"); 111 } 112 // store value as remove may change the entry in the decorator (e.g. TreeMap) 113 final V value = last.getValue(); 114 iterator.remove(); 115 parent.reverseMap.remove(value); 116 last = null; 117 canRemove = false; 118 } 119 120 @Override 121 public void reset() { 122 iterator = parent.normalMap.entrySet().iterator(); 123 last = null; 124 canRemove = false; 125 } 126 127 @Override 128 public V setValue(final V value) { 129 if (last == null) { 130 throw new IllegalStateException( 131 "Iterator setValue() can only be called after next() and before remove()"); 132 } 133 if (parent.reverseMap.containsKey(value) && 134 parent.reverseMap.get(value) != last.getKey()) { 135 throw new IllegalArgumentException( 136 "Cannot use setValue() when the object being set is already in the map"); 137 } 138 return parent.put(last.getKey(), value); 139 } 140 141 @Override 142 public String toString() { 143 if (last != null) { 144 return "MapIterator[" + getKey() + "=" + getValue() + "]"; 145 } 146 return "MapIterator[]"; 147 } 148 } 149 150 /** 151 * Inner class EntrySet. 152 * 153 * @param <K> the type of the keys. 154 * @param <V> the type of the values. 155 */ 156 protected static class EntrySet<K, V> extends View<K, V, Map.Entry<K, V>> implements Set<Map.Entry<K, V>> { 157 158 /** Serialization version */ 159 private static final long serialVersionUID = 4040410962603292348L; 160 161 /** 162 * Constructs a new instance. 163 * 164 * @param parent the parent BidiMap 165 */ 166 protected EntrySet(final AbstractDualBidiMap<K, V> parent) { 167 super(parent.normalMap.entrySet(), parent); 168 } 169 170 @Override 171 public Iterator<Map.Entry<K, V>> iterator() { 172 return parent.createEntrySetIterator(super.iterator()); 173 } 174 175 @Override 176 public boolean remove(final Object obj) { 177 if (!(obj instanceof Map.Entry)) { 178 return false; 179 } 180 final Map.Entry<?, ?> entry = (Map.Entry<?, ?>) obj; 181 final Object key = entry.getKey(); 182 if (parent.containsKey(key)) { 183 final V value = parent.normalMap.get(key); 184 if (Objects.equals(value, entry.getValue())) { 185 parent.normalMap.remove(key); 186 parent.reverseMap.remove(value); 187 return true; 188 } 189 } 190 return false; 191 } 192 } 193 194 /** 195 * Inner class EntrySetIterator. 196 * 197 * @param <K> the type of the keys. 198 * @param <V> the type of the values. 199 */ 200 protected static class EntrySetIterator<K, V> extends AbstractIteratorDecorator<Map.Entry<K, V>> { 201 202 /** The parent map */ 203 protected final AbstractDualBidiMap<K, V> parent; 204 205 /** The last returned entry */ 206 protected Map.Entry<K, V> last; 207 208 /** Whether remove is allowed at present */ 209 protected boolean canRemove; 210 211 /** 212 * Constructs a new instance. 213 * @param iterator the iterator to decorate 214 * @param parent the parent map 215 */ 216 protected EntrySetIterator(final Iterator<Map.Entry<K, V>> iterator, final AbstractDualBidiMap<K, V> parent) { 217 super(iterator); 218 this.parent = parent; 219 } 220 221 @Override 222 public Map.Entry<K, V> next() { 223 last = new MapEntry<>(super.next(), parent); 224 canRemove = true; 225 return last; 226 } 227 228 @Override 229 public void remove() { 230 if (!canRemove) { 231 throw new IllegalStateException("Iterator remove() can only be called once after next()"); 232 } 233 // store value as remove may change the entry in the decorator (e.g. TreeMap) 234 final Object value = last.getValue(); 235 super.remove(); 236 parent.reverseMap.remove(value); 237 last = null; 238 canRemove = false; 239 } 240 } 241 242 /** 243 * Inner class KeySet. 244 * 245 * @param <K> the type of elements maintained by this set 246 */ 247 protected static class KeySet<K> extends View<K, Object, K> implements Set<K> { 248 249 /** Serialization version */ 250 private static final long serialVersionUID = -7107935777385040694L; 251 252 /** 253 * Constructs a new instance. 254 * 255 * @param parent the parent BidiMap 256 */ 257 @SuppressWarnings("unchecked") 258 protected KeySet(final AbstractDualBidiMap<K, ?> parent) { 259 super(parent.normalMap.keySet(), (AbstractDualBidiMap<K, Object>) parent); 260 } 261 262 @Override 263 public boolean contains(final Object key) { 264 return parent.normalMap.containsKey(key); 265 } 266 267 @Override 268 public Iterator<K> iterator() { 269 return parent.createKeySetIterator(super.iterator()); 270 } 271 272 @Override 273 public boolean remove(final Object key) { 274 if (parent.normalMap.containsKey(key)) { 275 final Object value = parent.normalMap.remove(key); 276 parent.reverseMap.remove(value); 277 return true; 278 } 279 return false; 280 } 281 } 282 283 /** 284 * Inner class KeySetIterator. 285 * 286 * @param <K> the key type. 287 */ 288 protected static class KeySetIterator<K> extends AbstractIteratorDecorator<K> { 289 290 /** The parent map */ 291 protected final AbstractDualBidiMap<K, ?> parent; 292 293 /** The last returned key */ 294 protected K lastKey; 295 296 /** Whether remove is allowed at present */ 297 protected boolean canRemove; 298 299 /** 300 * Constructs a new instance. 301 * @param iterator the iterator to decorate 302 * @param parent the parent map 303 */ 304 protected KeySetIterator(final Iterator<K> iterator, final AbstractDualBidiMap<K, ?> parent) { 305 super(iterator); 306 this.parent = parent; 307 } 308 309 @Override 310 public K next() { 311 lastKey = super.next(); 312 canRemove = true; 313 return lastKey; 314 } 315 316 @Override 317 public void remove() { 318 if (!canRemove) { 319 throw new IllegalStateException("Iterator remove() can only be called once after next()"); 320 } 321 final Object value = parent.normalMap.get(lastKey); 322 super.remove(); 323 parent.reverseMap.remove(value); 324 lastKey = null; 325 canRemove = false; 326 } 327 } 328 329 /** 330 * Inner class MapEntry. 331 * 332 * @param <K> the type of the keys. 333 * @param <V> the type of the values. 334 */ 335 protected static class MapEntry<K, V> extends AbstractMapEntryDecorator<K, V> { 336 337 /** The parent map */ 338 protected final AbstractDualBidiMap<K, V> parent; 339 340 /** 341 * Constructs a new instance. 342 * @param entry the entry to decorate 343 * @param parent the parent map 344 */ 345 protected MapEntry(final Map.Entry<K, V> entry, final AbstractDualBidiMap<K, V> parent) { 346 super(entry); 347 this.parent = parent; 348 } 349 350 @Override 351 public V setValue(final V value) { 352 final K key = getKey(); 353 if (parent.reverseMap.containsKey(value) && 354 parent.reverseMap.get(value) != key) { 355 throw new IllegalArgumentException( 356 "Cannot use setValue() when the object being set is already in the map"); 357 } 358 parent.put(key, value); 359 return super.setValue(value); 360 } 361 } 362 363 /** 364 * Inner class Values. 365 * 366 * @param <V> the type of the values. 367 */ 368 protected static class Values<V> extends View<Object, V, V> implements Set<V> { 369 370 /** Serialization version */ 371 private static final long serialVersionUID = 4023777119829639864L; 372 373 /** 374 * Constructs a new instance. 375 * 376 * @param parent the parent BidiMap 377 */ 378 @SuppressWarnings("unchecked") 379 protected Values(final AbstractDualBidiMap<?, V> parent) { 380 super(parent.normalMap.values(), (AbstractDualBidiMap<Object, V>) parent); 381 } 382 383 @Override 384 public boolean contains(final Object value) { 385 return parent.reverseMap.containsKey(value); 386 } 387 388 @Override 389 public Iterator<V> iterator() { 390 return parent.createValuesIterator(super.iterator()); 391 } 392 393 @Override 394 public boolean remove(final Object value) { 395 if (parent.reverseMap.containsKey(value)) { 396 final Object key = parent.reverseMap.remove(value); 397 parent.normalMap.remove(key); 398 return true; 399 } 400 return false; 401 } 402 } 403 404 /** 405 * Inner class ValuesIterator. 406 * 407 * @param <V> the value type. 408 */ 409 protected static class ValuesIterator<V> extends AbstractIteratorDecorator<V> { 410 411 /** The parent map */ 412 protected final AbstractDualBidiMap<Object, V> parent; 413 414 /** The last returned value */ 415 protected V lastValue; 416 417 /** Whether remove is allowed at present */ 418 protected boolean canRemove; 419 420 /** 421 * Constructs a new instance. 422 * @param iterator the iterator to decorate 423 * @param parent the parent map 424 */ 425 @SuppressWarnings("unchecked") 426 protected ValuesIterator(final Iterator<V> iterator, final AbstractDualBidiMap<?, V> parent) { 427 super(iterator); 428 this.parent = (AbstractDualBidiMap<Object, V>) parent; 429 } 430 431 @Override 432 public V next() { 433 lastValue = super.next(); 434 canRemove = true; 435 return lastValue; 436 } 437 438 @Override 439 public void remove() { 440 if (!canRemove) { 441 throw new IllegalStateException("Iterator remove() can only be called once after next()"); 442 } 443 super.remove(); // removes from maps[0] 444 parent.reverseMap.remove(lastValue); 445 lastValue = null; 446 canRemove = false; 447 } 448 } 449 450 /** 451 * Inner class View. 452 * 453 * @param <K> the type of the keys in the map. 454 * @param <V> the type of the values in the map. 455 * @param <E> the type of the elements in the collection. 456 */ 457 protected abstract static class View<K, V, E> extends AbstractCollectionDecorator<E> { 458 459 /** Generated serial version ID. */ 460 private static final long serialVersionUID = 4621510560119690639L; 461 462 /** The parent map */ 463 protected final AbstractDualBidiMap<K, V> parent; 464 465 /** 466 * Constructs a new instance. 467 * 468 * @param coll the collection view being decorated 469 * @param parent the parent BidiMap 470 */ 471 protected View(final Collection<E> coll, final AbstractDualBidiMap<K, V> parent) { 472 super(coll); 473 this.parent = parent; 474 } 475 476 @Override 477 public void clear() { 478 parent.clear(); 479 } 480 481 @Override 482 public boolean equals(final Object object) { 483 return object == this || decorated().equals(object); 484 } 485 486 @Override 487 public int hashCode() { 488 return decorated().hashCode(); 489 } 490 491 @Override 492 public boolean removeAll(final Collection<?> coll) { 493 if (parent.isEmpty() || coll.isEmpty()) { 494 return false; 495 } 496 boolean modified = false; 497 for (final Object current : coll) { 498 modified |= remove(current); 499 } 500 return modified; 501 } 502 503 /** 504 * @since 4.4 505 */ 506 @Override 507 public boolean removeIf(final Predicate<? super E> filter) { 508 if (parent.isEmpty() || Objects.isNull(filter)) { 509 return false; 510 } 511 boolean modified = false; 512 final Iterator<?> it = iterator(); 513 while (it.hasNext()) { 514 @SuppressWarnings("unchecked") 515 final E e = (E) it.next(); 516 if (filter.test(e)) { 517 it.remove(); 518 modified = true; 519 } 520 } 521 return modified; 522 } 523 524 /** 525 * {@inheritDoc} 526 * <p> 527 * This implementation iterates over the elements of this bidi map, checking each element in 528 * turn to see if it's contained in {@code coll}. If it's not contained, it's removed 529 * from this bidi map. As a consequence, it is advised to use a collection type for 530 * {@code coll} that provides a fast (e.g. O(1)) implementation of 531 * {@link Collection#contains(Object)}. 532 */ 533 @Override 534 public boolean retainAll(final Collection<?> coll) { 535 if (parent.isEmpty()) { 536 return false; 537 } 538 if (coll.isEmpty()) { 539 parent.clear(); 540 return true; 541 } 542 boolean modified = false; 543 final Iterator<E> it = iterator(); 544 while (it.hasNext()) { 545 if (!coll.contains(it.next())) { 546 it.remove(); 547 modified = true; 548 } 549 } 550 return modified; 551 } 552 } 553 554 /** 555 * Normal delegate map. 556 */ 557 transient Map<K, V> normalMap; 558 559 // Map delegation 560 561 /** 562 * Reverse delegate map. 563 */ 564 transient Map<V, K> reverseMap; 565 566 /** 567 * Inverse view of this map. 568 */ 569 transient BidiMap<V, K> inverseBidiMap; 570 571 /** 572 * View of the keys. 573 */ 574 transient Set<K> keySet; 575 576 /** 577 * View of the values. 578 */ 579 transient Set<V> values; 580 581 /** 582 * View of the entries. 583 */ 584 transient Set<Map.Entry<K, V>> entrySet; 585 586 /** 587 * Creates an empty map, initialized by {@code createMap}. 588 * <p> 589 * This constructor remains in place for deserialization. 590 * All other usage is deprecated in favor of 591 * {@link #AbstractDualBidiMap(Map, Map)}. 592 */ 593 protected AbstractDualBidiMap() { 594 } 595 596 /** 597 * Creates an empty map using the two maps specified as storage. 598 * <p> 599 * The two maps must be a matching pair, normal and reverse. 600 * They will typically both be empty. 601 * <p> 602 * Neither map is validated, so nulls may be passed in. 603 * If you choose to do this then the subclass constructor must populate 604 * the {@code maps[]} instance variable itself. 605 * 606 * @param normalMap the normal direction map 607 * @param reverseMap the reverse direction map 608 * @since 3.1 609 */ 610 protected AbstractDualBidiMap(final Map<K, V> normalMap, final Map<V, K> reverseMap) { 611 this.normalMap = normalMap; 612 this.reverseMap = reverseMap; 613 } 614 615 // BidiMap changes 616 617 /** 618 * Constructs a map that decorates the specified maps, 619 * used by the subclass {@code createBidiMap} implementation. 620 * 621 * @param normalMap the normal direction map 622 * @param reverseMap the reverse direction map 623 * @param inverseBidiMap the inverse BidiMap 624 */ 625 protected AbstractDualBidiMap(final Map<K, V> normalMap, final Map<V, K> reverseMap, 626 final BidiMap<V, K> inverseBidiMap) { 627 this.normalMap = normalMap; 628 this.reverseMap = reverseMap; 629 this.inverseBidiMap = inverseBidiMap; 630 } 631 632 @Override 633 public void clear() { 634 normalMap.clear(); 635 reverseMap.clear(); 636 } 637 638 @Override 639 public boolean containsKey(final Object key) { 640 return normalMap.containsKey(key); 641 } 642 643 @Override 644 public boolean containsValue(final Object value) { 645 return reverseMap.containsKey(value); 646 } 647 648 /** 649 * Creates a new instance of the subclass. 650 * 651 * @param normalMap the normal direction map 652 * @param reverseMap the reverse direction map 653 * @param inverseMap this map, which is the inverse in the new map 654 * @return the bidi map 655 */ 656 protected abstract BidiMap<V, K> createBidiMap(Map<V, K> normalMap, Map<K, V> reverseMap, BidiMap<K, V> inverseMap); 657 658 /** 659 * Creates an entry set iterator. 660 * Subclasses can override this to return iterators with different properties. 661 * 662 * @param iterator the iterator to decorate 663 * @return the entrySet iterator 664 */ 665 protected Iterator<Map.Entry<K, V>> createEntrySetIterator(final Iterator<Map.Entry<K, V>> iterator) { 666 return new EntrySetIterator<>(iterator, this); 667 } 668 669 /** 670 * Creates a key set iterator. 671 * Subclasses can override this to return iterators with different properties. 672 * 673 * @param iterator the iterator to decorate 674 * @return the keySet iterator 675 */ 676 protected Iterator<K> createKeySetIterator(final Iterator<K> iterator) { 677 return new KeySetIterator<>(iterator, this); 678 } 679 680 /** 681 * Creates a values iterator. 682 * Subclasses can override this to return iterators with different properties. 683 * 684 * @param iterator the iterator to decorate 685 * @return the values iterator 686 */ 687 protected Iterator<V> createValuesIterator(final Iterator<V> iterator) { 688 return new ValuesIterator<>(iterator, this); 689 } 690 691 /** 692 * Gets an entrySet view of the map. 693 * Changes made on the set are reflected in the map. 694 * The set supports remove and clear but not add. 695 * <p> 696 * The Map Entry setValue() method only allow a new value to be set. 697 * If the value being set is already in the map, an IllegalArgumentException 698 * is thrown (as setValue cannot change the size of the map). 699 * </p> 700 * 701 * @return the entrySet view 702 */ 703 @Override 704 public Set<Map.Entry<K, V>> entrySet() { 705 if (entrySet == null) { 706 entrySet = new EntrySet<>(this); 707 } 708 return entrySet; 709 } 710 711 @Override 712 public boolean equals(final Object obj) { 713 return normalMap.equals(obj); 714 } 715 716 @Override 717 public V get(final Object key) { 718 return normalMap.get(key); 719 } 720 721 @Override 722 public K getKey(final Object value) { 723 return reverseMap.get(value); 724 } 725 726 @Override 727 public int hashCode() { 728 return normalMap.hashCode(); 729 } 730 731 @Override 732 public BidiMap<V, K> inverseBidiMap() { 733 if (inverseBidiMap == null) { 734 inverseBidiMap = createBidiMap(reverseMap, normalMap, this); 735 } 736 return inverseBidiMap; 737 } 738 739 @Override 740 public boolean isEmpty() { 741 return normalMap.isEmpty(); 742 } 743 744 // Map views 745 /** 746 * Gets a keySet view of the map. 747 * Changes made on the view are reflected in the map. 748 * The set supports remove and clear but not add. 749 * 750 * @return the keySet view 751 */ 752 @Override 753 public Set<K> keySet() { 754 if (keySet == null) { 755 keySet = new KeySet<>(this); 756 } 757 return keySet; 758 } 759 760 // BidiMap 761 /** 762 * Obtains a {@code MapIterator} over the map. 763 * The iterator implements {@link BidiMapIterator}. 764 * This implementation relies on the entrySet iterator. 765 * 766 * @return a map iterator 767 */ 768 @Override 769 public MapIterator<K, V> mapIterator() { 770 return new BidiMapIterator<>(this); 771 } 772 773 @Override 774 public V put(final K key, final V value) { 775 if (normalMap.containsKey(key)) { 776 reverseMap.remove(normalMap.get(key)); 777 } 778 if (reverseMap.containsKey(value)) { 779 normalMap.remove(reverseMap.get(value)); 780 } 781 final V obj = normalMap.put(key, value); 782 reverseMap.put(value, key); 783 return obj; 784 } 785 786 @Override 787 public void putAll(final Map<? extends K, ? extends V> map) { 788 for (final Map.Entry<? extends K, ? extends V> entry : map.entrySet()) { 789 put(entry.getKey(), entry.getValue()); 790 } 791 } 792 793 @Override 794 public V remove(final Object key) { 795 V value = null; 796 if (normalMap.containsKey(key)) { 797 value = normalMap.remove(key); 798 reverseMap.remove(value); 799 } 800 return value; 801 } 802 803 @Override 804 public K removeValue(final Object value) { 805 K key = null; 806 if (reverseMap.containsKey(value)) { 807 key = reverseMap.remove(value); 808 normalMap.remove(key); 809 } 810 return key; 811 } 812 813 @Override 814 public int size() { 815 return normalMap.size(); 816 } 817 818 @Override 819 public String toString() { 820 return normalMap.toString(); 821 } 822 823 /** 824 * Gets a values view of the map. 825 * Changes made on the view are reflected in the map. 826 * The set supports remove and clear but not add. 827 * 828 * @return the values view 829 */ 830 @Override 831 public Set<V> values() { 832 if (values == null) { 833 values = new Values<>(this); 834 } 835 return values; 836 } 837 838}