View Javadoc
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  
17  package com.google.common.collect;
18  
19  import static com.google.common.base.Preconditions.checkNotNull;
20  import static com.google.common.base.Predicates.alwaysTrue;
21  import static com.google.common.base.Predicates.equalTo;
22  import static com.google.common.base.Predicates.in;
23  import static com.google.common.base.Predicates.not;
24  import static com.google.common.collect.Maps.safeContainsKey;
25  import static com.google.common.collect.Maps.safeGet;
26  
27  import com.google.common.annotations.GwtCompatible;
28  import com.google.common.base.Function;
29  import com.google.common.base.Predicate;
30  import com.google.common.base.Supplier;
31  import com.google.common.collect.Maps.ImprovedAbstractMap;
32  import com.google.common.collect.Sets.ImprovedAbstractSet;
33  
34  import java.io.Serializable;
35  import java.util.Collection;
36  import java.util.Iterator;
37  import java.util.LinkedHashMap;
38  import java.util.Map;
39  import java.util.Map.Entry;
40  import java.util.Set;
41  
42  import 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
67  class 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 }