1/* 2 * Copyright (C) 2008 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.google.common.collect; 18 19import static com.google.common.base.Preconditions.checkNotNull; 20import static com.google.common.base.Predicates.alwaysTrue; 21import static com.google.common.base.Predicates.equalTo; 22import static com.google.common.base.Predicates.in; 23import static com.google.common.base.Predicates.not; 24import static com.google.common.collect.Maps.safeContainsKey; 25import static com.google.common.collect.Maps.safeGet; 26 27import com.google.common.annotations.GwtCompatible; 28import com.google.common.base.Function; 29import com.google.common.base.Predicate; 30import com.google.common.base.Supplier; 31import com.google.common.collect.Maps.ImprovedAbstractMap; 32import com.google.common.collect.Sets.ImprovedAbstractSet; 33 34import java.io.Serializable; 35import java.util.Collection; 36import java.util.Iterator; 37import java.util.LinkedHashMap; 38import java.util.Map; 39import java.util.Map.Entry; 40import java.util.Set; 41 42import javax.annotation.Nullable; 43 44/** 45 * {@link Table} implementation backed by a map that associates row keys with 46 * column key / value secondary maps. This class provides rapid access to 47 * records by the row key alone or by both keys, but not by just the column key. 48 * 49 * <p>The views returned by {@link #column}, {@link #columnKeySet()}, and {@link 50 * #columnMap()} have iterators that don't support {@code remove()}. Otherwise, 51 * all optional operations are supported. Null row keys, columns keys, and 52 * values are not supported. 53 * 54 * <p>Lookups by row key are often faster than lookups by column key, because 55 * the data is stored in a {@code Map<R, Map<C, V>>}. A method call like {@code 56 * column(columnKey).get(rowKey)} still runs quickly, since the row key is 57 * provided. However, {@code column(columnKey).size()} takes longer, since an 58 * iteration across all row keys occurs. 59 * 60 * <p>Note that this implementation is not synchronized. If multiple threads 61 * access this table concurrently and one of the threads modifies the table, it 62 * must be synchronized externally. 63 * 64 * @author Jared Levy 65 */ 66@GwtCompatible 67class StandardTable<R, C, V> extends AbstractTable<R, C, V> implements Serializable { 68 @GwtTransient final Map<R, Map<C, V>> backingMap; 69 @GwtTransient final Supplier<? extends Map<C, V>> factory; 70 71 StandardTable(Map<R, Map<C, V>> backingMap, 72 Supplier<? extends Map<C, V>> factory) { 73 this.backingMap = backingMap; 74 this.factory = factory; 75 } 76 77 // Accessors 78 79 @Override public boolean contains( 80 @Nullable Object rowKey, @Nullable Object columnKey) { 81 return rowKey != null && columnKey != null && super.contains(rowKey, columnKey); 82 } 83 84 @Override public boolean containsColumn(@Nullable Object columnKey) { 85 if (columnKey == null) { 86 return false; 87 } 88 for (Map<C, V> map : backingMap.values()) { 89 if (safeContainsKey(map, columnKey)) { 90 return true; 91 } 92 } 93 return false; 94 } 95 96 @Override public boolean containsRow(@Nullable Object rowKey) { 97 return rowKey != null && safeContainsKey(backingMap, rowKey); 98 } 99 100 @Override public boolean containsValue(@Nullable Object value) { 101 return value != null && super.containsValue(value); 102 } 103 104 @Override public V get(@Nullable Object rowKey, @Nullable Object columnKey) { 105 return (rowKey == null || columnKey == null) 106 ? null 107 : super.get(rowKey, columnKey); 108 } 109 110 @Override public boolean isEmpty() { 111 return backingMap.isEmpty(); 112 } 113 114 @Override public int size() { 115 int size = 0; 116 for (Map<C, V> map : backingMap.values()) { 117 size += map.size(); 118 } 119 return size; 120 } 121 122 // Mutators 123 124 @Override public void clear() { 125 backingMap.clear(); 126 } 127 128 private Map<C, V> getOrCreate(R rowKey) { 129 Map<C, V> map = backingMap.get(rowKey); 130 if (map == null) { 131 map = factory.get(); 132 backingMap.put(rowKey, map); 133 } 134 return map; 135 } 136 137 @Override public V put(R rowKey, C columnKey, V value) { 138 checkNotNull(rowKey); 139 checkNotNull(columnKey); 140 checkNotNull(value); 141 return getOrCreate(rowKey).put(columnKey, value); 142 } 143 144 @Override public V remove( 145 @Nullable Object rowKey, @Nullable Object columnKey) { 146 if ((rowKey == null) || (columnKey == null)) { 147 return null; 148 } 149 Map<C, V> map = safeGet(backingMap, rowKey); 150 if (map == null) { 151 return null; 152 } 153 V value = map.remove(columnKey); 154 if (map.isEmpty()) { 155 backingMap.remove(rowKey); 156 } 157 return value; 158 } 159 160 private Map<R, V> removeColumn(Object column) { 161 Map<R, V> output = new LinkedHashMap<R, V>(); 162 Iterator<Entry<R, Map<C, V>>> iterator 163 = backingMap.entrySet().iterator(); 164 while (iterator.hasNext()) { 165 Entry<R, Map<C, V>> entry = iterator.next(); 166 V value = entry.getValue().remove(column); 167 if (value != null) { 168 output.put(entry.getKey(), value); 169 if (entry.getValue().isEmpty()) { 170 iterator.remove(); 171 } 172 } 173 } 174 return output; 175 } 176 177 private boolean containsMapping( 178 Object rowKey, Object columnKey, Object value) { 179 return value != null && value.equals(get(rowKey, columnKey)); 180 } 181 182 /** Remove a row key / column key / value mapping, if present. */ 183 private boolean removeMapping(Object rowKey, Object columnKey, Object value) { 184 if (containsMapping(rowKey, columnKey, value)) { 185 remove(rowKey, columnKey); 186 return true; 187 } 188 return false; 189 } 190 191 // Views 192 193 /** 194 * Abstract set whose {@code isEmpty()} returns whether the table is empty and 195 * whose {@code clear()} clears all table mappings. 196 */ 197 private abstract class TableSet<T> extends ImprovedAbstractSet<T> { 198 @Override public boolean isEmpty() { 199 return backingMap.isEmpty(); 200 } 201 202 @Override public void clear() { 203 backingMap.clear(); 204 } 205 } 206 207 /** 208 * {@inheritDoc} 209 * 210 * <p>The set's iterator traverses the mappings for the first row, the 211 * mappings for the second row, and so on. 212 * 213 * <p>Each cell is an immutable snapshot of a row key / column key / value 214 * mapping, taken at the time the cell is returned by a method call to the 215 * set or its iterator. 216 */ 217 @Override public Set<Cell<R, C, V>> cellSet() { 218 return super.cellSet(); 219 } 220 221 @Override Iterator<Cell<R, C, V>> cellIterator() { 222 return new CellIterator(); 223 } 224 225 private class CellIterator implements Iterator<Cell<R, C, V>> { 226 final Iterator<Entry<R, Map<C, V>>> rowIterator 227 = backingMap.entrySet().iterator(); 228 Entry<R, Map<C, V>> rowEntry; 229 Iterator<Entry<C, V>> columnIterator 230 = Iterators.emptyModifiableIterator(); 231 232 @Override public boolean hasNext() { 233 return rowIterator.hasNext() || columnIterator.hasNext(); 234 } 235 236 @Override public Cell<R, C, V> next() { 237 if (!columnIterator.hasNext()) { 238 rowEntry = rowIterator.next(); 239 columnIterator = rowEntry.getValue().entrySet().iterator(); 240 } 241 Entry<C, V> columnEntry = columnIterator.next(); 242 return Tables.immutableCell( 243 rowEntry.getKey(), columnEntry.getKey(), columnEntry.getValue()); 244 } 245 246 @Override public void remove() { 247 columnIterator.remove(); 248 if (rowEntry.getValue().isEmpty()) { 249 rowIterator.remove(); 250 } 251 } 252 } 253 254 @Override public Map<C, V> row(R rowKey) { 255 return new Row(rowKey); 256 } 257 258 class Row extends ImprovedAbstractMap<C, V> { 259 final R rowKey; 260 261 Row(R rowKey) { 262 this.rowKey = checkNotNull(rowKey); 263 } 264 265 Map<C, V> backingRowMap; 266 267 Map<C, V> backingRowMap() { 268 return (backingRowMap == null 269 || (backingRowMap.isEmpty() && backingMap.containsKey(rowKey))) 270 ? backingRowMap = computeBackingRowMap() 271 : backingRowMap; 272 } 273 274 Map<C, V> computeBackingRowMap() { 275 return backingMap.get(rowKey); 276 } 277 278 // Call this every time we perform a removal. 279 void maintainEmptyInvariant() { 280 if (backingRowMap() != null && backingRowMap.isEmpty()) { 281 backingMap.remove(rowKey); 282 backingRowMap = null; 283 } 284 } 285 286 @Override 287 public boolean containsKey(Object key) { 288 Map<C, V> backingRowMap = backingRowMap(); 289 return (key != null && backingRowMap != null) 290 && Maps.safeContainsKey(backingRowMap, key); 291 } 292 293 @Override 294 public V get(Object key) { 295 Map<C, V> backingRowMap = backingRowMap(); 296 return (key != null && backingRowMap != null) 297 ? Maps.safeGet(backingRowMap, key) 298 : null; 299 } 300 301 @Override 302 public V put(C key, V value) { 303 checkNotNull(key); 304 checkNotNull(value); 305 if (backingRowMap != null && !backingRowMap.isEmpty()) { 306 return backingRowMap.put(key, value); 307 } 308 return StandardTable.this.put(rowKey, key, value); 309 } 310 311 @Override 312 public V remove(Object key) { 313 Map<C, V> backingRowMap = backingRowMap(); 314 if (backingRowMap == null) { 315 return null; 316 } 317 V result = Maps.safeRemove(backingRowMap, key); 318 maintainEmptyInvariant(); 319 return result; 320 } 321 322 @Override 323 public void clear() { 324 Map<C, V> backingRowMap = backingRowMap(); 325 if (backingRowMap != null) { 326 backingRowMap.clear(); 327 } 328 maintainEmptyInvariant(); 329 } 330 331 @Override 332 protected Set<Entry<C, V>> createEntrySet() { 333 return new RowEntrySet(); 334 } 335 336 private final class RowEntrySet extends Maps.EntrySet<C, V> { 337 @Override 338 Map<C, V> map() { 339 return Row.this; 340 } 341 342 @Override 343 public int size() { 344 Map<C, V> map = backingRowMap(); 345 return (map == null) ? 0 : map.size(); 346 } 347 348 @Override 349 public Iterator<Entry<C, V>> iterator() { 350 final Map<C, V> map = backingRowMap(); 351 if (map == null) { 352 return Iterators.emptyModifiableIterator(); 353 } 354 final Iterator<Entry<C, V>> iterator = map.entrySet().iterator(); 355 return new Iterator<Entry<C, V>>() { 356 @Override public boolean hasNext() { 357 return iterator.hasNext(); 358 } 359 @Override public Entry<C, V> next() { 360 final Entry<C, V> entry = iterator.next(); 361 return new ForwardingMapEntry<C, V>() { 362 @Override protected Entry<C, V> delegate() { 363 return entry; 364 } 365 @Override public V setValue(V value) { 366 return super.setValue(checkNotNull(value)); 367 } 368 @Override 369 public boolean equals(Object object) { 370 // TODO(user): identify why this affects GWT tests 371 return standardEquals(object); 372 } 373 }; 374 } 375 376 @Override 377 public void remove() { 378 iterator.remove(); 379 maintainEmptyInvariant(); 380 } 381 }; 382 } 383 } 384 } 385 386 /** 387 * {@inheritDoc} 388 * 389 * <p>The returned map's views have iterators that don't support 390 * {@code remove()}. 391 */ 392 @Override public Map<R, V> column(C columnKey) { 393 return new Column(columnKey); 394 } 395 396 private class Column extends ImprovedAbstractMap<R, V> { 397 final C columnKey; 398 399 Column(C columnKey) { 400 this.columnKey = checkNotNull(columnKey); 401 } 402 403 @Override public V put(R key, V value) { 404 return StandardTable.this.put(key, columnKey, value); 405 } 406 407 @Override public V get(Object key) { 408 return StandardTable.this.get(key, columnKey); 409 } 410 411 @Override public boolean containsKey(Object key) { 412 return StandardTable.this.contains(key, columnKey); 413 } 414 415 @Override public V remove(Object key) { 416 return StandardTable.this.remove(key, columnKey); 417 } 418 419 /** 420 * Removes all {@code Column} mappings whose row key and value satisfy the 421 * given predicate. 422 */ 423 boolean removeFromColumnIf(Predicate<? super Entry<R, V>> predicate) { 424 boolean changed = false; 425 Iterator<Entry<R, Map<C, V>>> iterator 426 = backingMap.entrySet().iterator(); 427 while (iterator.hasNext()) { 428 Entry<R, Map<C, V>> entry = iterator.next(); 429 Map<C, V> map = entry.getValue(); 430 V value = map.get(columnKey); 431 if (value != null 432 && predicate.apply(Maps.immutableEntry(entry.getKey(), value))) { 433 map.remove(columnKey); 434 changed = true; 435 if (map.isEmpty()) { 436 iterator.remove(); 437 } 438 } 439 } 440 return changed; 441 } 442 443 @Override Set<Entry<R, V>> createEntrySet() { 444 return new EntrySet(); 445 } 446 447 private class EntrySet extends ImprovedAbstractSet<Entry<R, V>> { 448 @Override public Iterator<Entry<R, V>> iterator() { 449 return new EntrySetIterator(); 450 } 451 452 @Override public int size() { 453 int size = 0; 454 for (Map<C, V> map : backingMap.values()) { 455 if (map.containsKey(columnKey)) { 456 size++; 457 } 458 } 459 return size; 460 } 461 462 @Override public boolean isEmpty() { 463 return !containsColumn(columnKey); 464 } 465 466 @Override public void clear() { 467 removeFromColumnIf(alwaysTrue()); 468 } 469 470 @Override public boolean contains(Object o) { 471 if (o instanceof Entry) { 472 Entry<?, ?> entry = (Entry<?, ?>) o; 473 return containsMapping(entry.getKey(), columnKey, entry.getValue()); 474 } 475 return false; 476 } 477 478 @Override public boolean remove(Object obj) { 479 if (obj instanceof Entry) { 480 Entry<?, ?> entry = (Entry<?, ?>) obj; 481 return removeMapping(entry.getKey(), columnKey, entry.getValue()); 482 } 483 return false; 484 } 485 486 @Override public boolean retainAll(Collection<?> c) { 487 return removeFromColumnIf(not(in(c))); 488 } 489 } 490 491 private class EntrySetIterator extends AbstractIterator<Entry<R, V>> { 492 final Iterator<Entry<R, Map<C, V>>> iterator 493 = backingMap.entrySet().iterator(); 494 @Override protected Entry<R, V> computeNext() { 495 while (iterator.hasNext()) { 496 final Entry<R, Map<C, V>> entry = iterator.next(); 497 if (entry.getValue().containsKey(columnKey)) { 498 return new AbstractMapEntry<R, V>() { 499 @Override public R getKey() { 500 return entry.getKey(); 501 } 502 @Override public V getValue() { 503 return entry.getValue().get(columnKey); 504 } 505 @Override public V setValue(V value) { 506 return entry.getValue().put(columnKey, checkNotNull(value)); 507 } 508 }; 509 } 510 } 511 return endOfData(); 512 } 513 } 514 515 @Override Set<R> createKeySet() { 516 return new KeySet(); 517 } 518 519 private class KeySet extends Maps.KeySet<R, V> { 520 KeySet() { 521 super(Column.this); 522 } 523 524 @Override public boolean contains(Object obj) { 525 return StandardTable.this.contains(obj, columnKey); 526 } 527 528 @Override public boolean remove(Object obj) { 529 return StandardTable.this.remove(obj, columnKey) != null; 530 } 531 532 @Override public boolean retainAll(final Collection<?> c) { 533 return removeFromColumnIf(Maps.<R>keyPredicateOnEntries(not(in(c)))); 534 } 535 } 536 537 @Override 538 Collection<V> createValues() { 539 return new Values(); 540 } 541 542 private class Values extends Maps.Values<R, V> { 543 Values() { 544 super(Column.this); 545 } 546 547 @Override public boolean remove(Object obj) { 548 return obj != null && removeFromColumnIf(Maps.<V>valuePredicateOnEntries(equalTo(obj))); 549 } 550 551 @Override public boolean removeAll(final Collection<?> c) { 552 return removeFromColumnIf(Maps.<V>valuePredicateOnEntries(in(c))); 553 } 554 555 @Override public boolean retainAll(final Collection<?> c) { 556 return removeFromColumnIf(Maps.<V>valuePredicateOnEntries(not(in(c)))); 557 } 558 } 559 } 560 561 @Override public Set<R> rowKeySet() { 562 return rowMap().keySet(); 563 } 564 565 private transient Set<C> columnKeySet; 566 567 /** 568 * {@inheritDoc} 569 * 570 * <p>The returned set has an iterator that does not support {@code remove()}. 571 * 572 * <p>The set's iterator traverses the columns of the first row, the 573 * columns of the second row, etc., skipping any columns that have 574 * appeared previously. 575 */ 576 @Override 577 public Set<C> columnKeySet() { 578 Set<C> result = columnKeySet; 579 return (result == null) ? columnKeySet = new ColumnKeySet() : result; 580 } 581 582 private class ColumnKeySet extends TableSet<C> { 583 @Override public Iterator<C> iterator() { 584 return createColumnKeyIterator(); 585 } 586 587 @Override public int size() { 588 return Iterators.size(iterator()); 589 } 590 591 @Override public boolean remove(Object obj) { 592 if (obj == null) { 593 return false; 594 } 595 boolean changed = false; 596 Iterator<Map<C, V>> iterator = backingMap.values().iterator(); 597 while (iterator.hasNext()) { 598 Map<C, V> map = iterator.next(); 599 if (map.keySet().remove(obj)) { 600 changed = true; 601 if (map.isEmpty()) { 602 iterator.remove(); 603 } 604 } 605 } 606 return changed; 607 } 608 609 @Override public boolean removeAll(Collection<?> c) { 610 checkNotNull(c); 611 boolean changed = false; 612 Iterator<Map<C, V>> iterator = backingMap.values().iterator(); 613 while (iterator.hasNext()) { 614 Map<C, V> map = iterator.next(); 615 // map.keySet().removeAll(c) can throw a NPE when map is a TreeMap with 616 // natural ordering and c contains a null. 617 if (Iterators.removeAll(map.keySet().iterator(), c)) { 618 changed = true; 619 if (map.isEmpty()) { 620 iterator.remove(); 621 } 622 } 623 } 624 return changed; 625 } 626 627 @Override public boolean retainAll(Collection<?> c) { 628 checkNotNull(c); 629 boolean changed = false; 630 Iterator<Map<C, V>> iterator = backingMap.values().iterator(); 631 while (iterator.hasNext()) { 632 Map<C, V> map = iterator.next(); 633 if (map.keySet().retainAll(c)) { 634 changed = true; 635 if (map.isEmpty()) { 636 iterator.remove(); 637 } 638 } 639 } 640 return changed; 641 } 642 643 @Override public boolean contains(Object obj) { 644 return containsColumn(obj); 645 } 646 } 647 648 /** 649 * Creates an iterator that returns each column value with duplicates 650 * omitted. 651 */ 652 Iterator<C> createColumnKeyIterator() { 653 return new ColumnKeyIterator(); 654 } 655 656 private class ColumnKeyIterator extends AbstractIterator<C> { 657 // Use the same map type to support TreeMaps with comparators that aren't 658 // consistent with equals(). 659 final Map<C, V> seen = factory.get(); 660 final Iterator<Map<C, V>> mapIterator = backingMap.values().iterator(); 661 Iterator<Entry<C, V>> entryIterator = Iterators.emptyIterator(); 662 663 @Override protected C computeNext() { 664 while (true) { 665 if (entryIterator.hasNext()) { 666 Entry<C, V> entry = entryIterator.next(); 667 if (!seen.containsKey(entry.getKey())) { 668 seen.put(entry.getKey(), entry.getValue()); 669 return entry.getKey(); 670 } 671 } else if (mapIterator.hasNext()) { 672 entryIterator = mapIterator.next().entrySet().iterator(); 673 } else { 674 return endOfData(); 675 } 676 } 677 } 678 } 679 680 /** 681 * {@inheritDoc} 682 * 683 * <p>The collection's iterator traverses the values for the first row, 684 * the values for the second row, and so on. 685 */ 686 @Override public Collection<V> values() { 687 return super.values(); 688 } 689 690 private transient Map<R, Map<C, V>> rowMap; 691 692 @Override public Map<R, Map<C, V>> rowMap() { 693 Map<R, Map<C, V>> result = rowMap; 694 return (result == null) ? rowMap = createRowMap() : result; 695 } 696 697 Map<R, Map<C, V>> createRowMap() { 698 return new RowMap(); 699 } 700 701 class RowMap extends ImprovedAbstractMap<R, Map<C, V>> { 702 @Override public boolean containsKey(Object key) { 703 return containsRow(key); 704 } 705 706 // performing cast only when key is in backing map and has the correct type 707 @SuppressWarnings("unchecked") 708 @Override public Map<C, V> get(Object key) { 709 return containsRow(key) ? row((R) key) : null; 710 } 711 712 @Override public Map<C, V> remove(Object key) { 713 return (key == null) ? null : backingMap.remove(key); 714 } 715 716 @Override protected Set<Entry<R, Map<C, V>>> createEntrySet() { 717 return new EntrySet(); 718 } 719 720 class EntrySet extends TableSet<Entry<R, Map<C, V>>> { 721 @Override public Iterator<Entry<R, Map<C, V>>> iterator() { 722 return Maps.asMapEntryIterator(backingMap.keySet(), new Function<R, Map<C, V>>() { 723 @Override 724 public Map<C, V> apply(R rowKey) { 725 return row(rowKey); 726 } 727 }); 728 } 729 730 @Override public int size() { 731 return backingMap.size(); 732 } 733 734 @Override public boolean contains(Object obj) { 735 if (obj instanceof Entry) { 736 Entry<?, ?> entry = (Entry<?, ?>) obj; 737 return entry.getKey() != null 738 && entry.getValue() instanceof Map 739 && Collections2.safeContains(backingMap.entrySet(), entry); 740 } 741 return false; 742 } 743 744 @Override public boolean remove(Object obj) { 745 if (obj instanceof Entry) { 746 Entry<?, ?> entry = (Entry<?, ?>) obj; 747 return entry.getKey() != null 748 && entry.getValue() instanceof Map 749 && backingMap.entrySet().remove(entry); 750 } 751 return false; 752 } 753 } 754 } 755 756 private transient ColumnMap columnMap; 757 758 @Override public Map<C, Map<R, V>> columnMap() { 759 ColumnMap result = columnMap; 760 return (result == null) ? columnMap = new ColumnMap() : result; 761 } 762 763 private class ColumnMap extends ImprovedAbstractMap<C, Map<R, V>> { 764 // The cast to C occurs only when the key is in the map, implying that it 765 // has the correct type. 766 @SuppressWarnings("unchecked") 767 @Override public Map<R, V> get(Object key) { 768 return containsColumn(key) ? column((C) key) : null; 769 } 770 771 @Override public boolean containsKey(Object key) { 772 return containsColumn(key); 773 } 774 775 @Override public Map<R, V> remove(Object key) { 776 return containsColumn(key) ? removeColumn(key) : null; 777 } 778 779 @Override public Set<Entry<C, Map<R, V>>> createEntrySet() { 780 return new ColumnMapEntrySet(); 781 } 782 783 @Override public Set<C> keySet() { 784 return columnKeySet(); 785 } 786 787 @Override Collection<Map<R, V>> createValues() { 788 return new ColumnMapValues(); 789 } 790 791 class ColumnMapEntrySet extends TableSet<Entry<C, Map<R, V>>> { 792 @Override public Iterator<Entry<C, Map<R, V>>> iterator() { 793 return Maps.asMapEntryIterator(columnKeySet(), new Function<C, Map<R, V>>() { 794 @Override 795 public Map<R, V> apply(C columnKey) { 796 return column(columnKey); 797 } 798 }); 799 } 800 801 @Override public int size() { 802 return columnKeySet().size(); 803 } 804 805 @Override public boolean contains(Object obj) { 806 if (obj instanceof Entry) { 807 Entry<?, ?> entry = (Entry<?, ?>) obj; 808 if (containsColumn(entry.getKey())) { 809 // The cast to C occurs only when the key is in the map, implying 810 // that it has the correct type. 811 @SuppressWarnings("unchecked") 812 C columnKey = (C) entry.getKey(); 813 return get(columnKey).equals(entry.getValue()); 814 } 815 } 816 return false; 817 } 818 819 @Override public boolean remove(Object obj) { 820 if (contains(obj)) { 821 Entry<?, ?> entry = (Entry<?, ?>) obj; 822 removeColumn(entry.getKey()); 823 return true; 824 } 825 return false; 826 } 827 828 @Override public boolean removeAll(Collection<?> c) { 829 /* 830 * We can't inherit the normal implementation (which calls 831 * Sets.removeAllImpl(Set, *Collection*) because, under some 832 * circumstances, it attempts to call columnKeySet().iterator().remove, 833 * which is unsupported. 834 */ 835 checkNotNull(c); 836 return Sets.removeAllImpl(this, c.iterator()); 837 } 838 839 @Override public boolean retainAll(Collection<?> c) { 840 checkNotNull(c); 841 boolean changed = false; 842 for (C columnKey : Lists.newArrayList(columnKeySet().iterator())) { 843 if (!c.contains(Maps.immutableEntry(columnKey, column(columnKey)))) { 844 removeColumn(columnKey); 845 changed = true; 846 } 847 } 848 return changed; 849 } 850 } 851 852 private class ColumnMapValues extends Maps.Values<C, Map<R, V>> { 853 ColumnMapValues() { 854 super(ColumnMap.this); 855 } 856 857 @Override public boolean remove(Object obj) { 858 for (Entry<C, Map<R, V>> entry : ColumnMap.this.entrySet()) { 859 if (entry.getValue().equals(obj)) { 860 removeColumn(entry.getKey()); 861 return true; 862 } 863 } 864 return false; 865 } 866 867 @Override public boolean removeAll(Collection<?> c) { 868 checkNotNull(c); 869 boolean changed = false; 870 for (C columnKey : Lists.newArrayList(columnKeySet().iterator())) { 871 if (c.contains(column(columnKey))) { 872 removeColumn(columnKey); 873 changed = true; 874 } 875 } 876 return changed; 877 } 878 879 @Override public boolean retainAll(Collection<?> c) { 880 checkNotNull(c); 881 boolean changed = false; 882 for (C columnKey : Lists.newArrayList(columnKeySet().iterator())) { 883 if (!c.contains(column(columnKey))) { 884 removeColumn(columnKey); 885 changed = true; 886 } 887 } 888 return changed; 889 } 890 } 891 } 892 893 private static final long serialVersionUID = 0; 894} 895