All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.common.collect.StandardTable Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 34.0.0.Final
Show newest version
/*
 * Copyright (C) 2008 The Guava Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.common.collect;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Predicates.alwaysTrue;
import static com.google.common.base.Predicates.equalTo;
import static com.google.common.base.Predicates.in;
import static com.google.common.base.Predicates.not;
import static com.google.common.collect.Maps.safeContainsKey;
import static com.google.common.collect.Maps.safeGet;
import static com.google.common.collect.NullnessCasts.uncheckedCastNullableTToT;
import static java.util.Objects.requireNonNull;

import com.google.common.annotations.GwtCompatible;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.collect.Maps.IteratorBasedAbstractMap;
import com.google.common.collect.Maps.ViewCachingAbstractMap;
import com.google.common.collect.Sets.ImprovedAbstractSet;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.concurrent.LazyInit;
import com.google.j2objc.annotations.WeakOuter;
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterators;
import javax.annotation.CheckForNull;

/**
 * {@link Table} implementation backed by a map that associates row keys with column key / value
 * secondary maps. This class provides rapid access to records by the row key alone or by both keys,
 * but not by just the column key.
 *
 * 

The views returned by {@link #column}, {@link #columnKeySet()}, and {@link #columnMap()} have * iterators that don't support {@code remove()}. Otherwise, all optional operations are supported. * Null row keys, columns keys, and values are not supported. * *

Lookups by row key are often faster than lookups by column key, because the data is stored in * a {@code Map>}. A method call like {@code column(columnKey).get(rowKey)} still runs * quickly, since the row key is provided. However, {@code column(columnKey).size()} takes longer, * since an iteration across all row keys occurs. * *

Note that this implementation is not synchronized. If multiple threads access this table * concurrently and one of the threads modifies the table, it must be synchronized externally. * * @author Jared Levy */ @GwtCompatible @ElementTypesAreNonnullByDefault class StandardTable extends AbstractTable implements Serializable { @GwtTransient final Map> backingMap; @GwtTransient final Supplier> factory; StandardTable(Map> backingMap, Supplier> factory) { this.backingMap = backingMap; this.factory = factory; } // Accessors @Override public boolean contains(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { return rowKey != null && columnKey != null && super.contains(rowKey, columnKey); } @Override public boolean containsColumn(@CheckForNull Object columnKey) { if (columnKey == null) { return false; } for (Map map : backingMap.values()) { if (safeContainsKey(map, columnKey)) { return true; } } return false; } @Override public boolean containsRow(@CheckForNull Object rowKey) { return rowKey != null && safeContainsKey(backingMap, rowKey); } @Override public boolean containsValue(@CheckForNull Object value) { return value != null && super.containsValue(value); } @Override @CheckForNull public V get(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { return (rowKey == null || columnKey == null) ? null : super.get(rowKey, columnKey); } @Override public boolean isEmpty() { return backingMap.isEmpty(); } @Override public int size() { int size = 0; for (Map map : backingMap.values()) { size += map.size(); } return size; } // Mutators @Override public void clear() { backingMap.clear(); } private Map getOrCreate(R rowKey) { Map map = backingMap.get(rowKey); if (map == null) { map = factory.get(); backingMap.put(rowKey, map); } return map; } @CanIgnoreReturnValue @Override @CheckForNull public V put(R rowKey, C columnKey, V value) { checkNotNull(rowKey); checkNotNull(columnKey); checkNotNull(value); return getOrCreate(rowKey).put(columnKey, value); } @CanIgnoreReturnValue @Override @CheckForNull public V remove(@CheckForNull Object rowKey, @CheckForNull Object columnKey) { if ((rowKey == null) || (columnKey == null)) { return null; } Map map = safeGet(backingMap, rowKey); if (map == null) { return null; } V value = map.remove(columnKey); if (map.isEmpty()) { backingMap.remove(rowKey); } return value; } @CanIgnoreReturnValue private Map removeColumn(@CheckForNull Object column) { Map output = new LinkedHashMap<>(); Iterator>> iterator = backingMap.entrySet().iterator(); while (iterator.hasNext()) { Entry> entry = iterator.next(); V value = entry.getValue().remove(column); if (value != null) { output.put(entry.getKey(), value); if (entry.getValue().isEmpty()) { iterator.remove(); } } } return output; } private boolean containsMapping( @CheckForNull Object rowKey, @CheckForNull Object columnKey, @CheckForNull Object value) { return value != null && value.equals(get(rowKey, columnKey)); } /** Remove a row key / column key / value mapping, if present. */ private boolean removeMapping( @CheckForNull Object rowKey, @CheckForNull Object columnKey, @CheckForNull Object value) { if (containsMapping(rowKey, columnKey, value)) { remove(rowKey, columnKey); return true; } return false; } // Views /** * Abstract set whose {@code isEmpty()} returns whether the table is empty and whose {@code * clear()} clears all table mappings. */ @WeakOuter private abstract class TableSet extends ImprovedAbstractSet { @Override public boolean isEmpty() { return backingMap.isEmpty(); } @Override public void clear() { backingMap.clear(); } } /** * {@inheritDoc} * *

The set's iterator traverses the mappings for the first row, the mappings for the second * row, and so on. * *

Each cell is an immutable snapshot of a row key / column key / value mapping, taken at the * time the cell is returned by a method call to the set or its iterator. */ @Override public Set> cellSet() { return super.cellSet(); } @Override Iterator> cellIterator() { return new CellIterator(); } private class CellIterator implements Iterator> { final Iterator>> rowIterator = backingMap.entrySet().iterator(); @CheckForNull Entry> rowEntry; Iterator> columnIterator = Iterators.emptyModifiableIterator(); @Override public boolean hasNext() { return rowIterator.hasNext() || columnIterator.hasNext(); } @Override public Cell next() { if (!columnIterator.hasNext()) { rowEntry = rowIterator.next(); columnIterator = rowEntry.getValue().entrySet().iterator(); } /* * requireNonNull is safe because: * * - columnIterator started off pointing to an empty iterator, so we must have entered the * `if` body above at least once. Thus, if we got this far, that `if` body initialized * rowEntry at least once. * * - The only case in which rowEntry is cleared (during remove() below) happens only if the * caller removed every element from columnIterator. During that process, we would have had * to iterate it to exhaustion. Then we can apply the logic above about an empty * columnIterator. (This assumes no concurrent modification, but behavior under concurrent * modification is undefined, anyway.) */ requireNonNull(rowEntry); Entry columnEntry = columnIterator.next(); return Tables.immutableCell(rowEntry.getKey(), columnEntry.getKey(), columnEntry.getValue()); } @Override public void remove() { columnIterator.remove(); /* * requireNonNull is safe because: * * - columnIterator.remove() succeeded, so it must have returned a value, so it must have been * initialized by next() -- which initializes rowEntry, too. * * - rowEntry isn't cleared except below. If it was cleared below, then either * columnIterator.remove() would have failed above (if the user hasn't called next() since * then) or rowEntry would have been initialized by next() (as discussed above). */ if (requireNonNull(rowEntry).getValue().isEmpty()) { rowIterator.remove(); rowEntry = null; } } } @Override Spliterator> cellSpliterator() { return CollectSpliterators.flatMap( backingMap.entrySet().spliterator(), (Entry> rowEntry) -> CollectSpliterators.map( rowEntry.getValue().entrySet().spliterator(), (Entry columnEntry) -> Tables.immutableCell( rowEntry.getKey(), columnEntry.getKey(), columnEntry.getValue())), Spliterator.DISTINCT | Spliterator.SIZED, size()); } @Override public Map row(R rowKey) { return new Row(rowKey); } class Row extends IteratorBasedAbstractMap { final R rowKey; Row(R rowKey) { this.rowKey = checkNotNull(rowKey); } @CheckForNull Map backingRowMap; final void updateBackingRowMapField() { if (backingRowMap == null || (backingRowMap.isEmpty() && backingMap.containsKey(rowKey))) { backingRowMap = computeBackingRowMap(); } } @CheckForNull Map computeBackingRowMap() { return backingMap.get(rowKey); } // Call this every time we perform a removal. void maintainEmptyInvariant() { updateBackingRowMapField(); if (backingRowMap != null && backingRowMap.isEmpty()) { backingMap.remove(rowKey); backingRowMap = null; } } @Override public boolean containsKey(@CheckForNull Object key) { updateBackingRowMapField(); return (key != null && backingRowMap != null) && Maps.safeContainsKey(backingRowMap, key); } @Override @CheckForNull public V get(@CheckForNull Object key) { updateBackingRowMapField(); return (key != null && backingRowMap != null) ? Maps.safeGet(backingRowMap, key) : null; } @Override @CheckForNull public V put(C key, V value) { checkNotNull(key); checkNotNull(value); if (backingRowMap != null && !backingRowMap.isEmpty()) { return backingRowMap.put(key, value); } return StandardTable.this.put(rowKey, key, value); } @Override @CheckForNull public V remove(@CheckForNull Object key) { updateBackingRowMapField(); if (backingRowMap == null) { return null; } V result = Maps.safeRemove(backingRowMap, key); maintainEmptyInvariant(); return result; } @Override public void clear() { updateBackingRowMapField(); if (backingRowMap != null) { backingRowMap.clear(); } maintainEmptyInvariant(); } @Override public int size() { updateBackingRowMapField(); return (backingRowMap == null) ? 0 : backingRowMap.size(); } @Override Iterator> entryIterator() { updateBackingRowMapField(); if (backingRowMap == null) { return Iterators.emptyModifiableIterator(); } final Iterator> iterator = backingRowMap.entrySet().iterator(); return new Iterator>() { @Override public boolean hasNext() { return iterator.hasNext(); } @Override public Entry next() { return wrapEntry(iterator.next()); } @Override public void remove() { iterator.remove(); maintainEmptyInvariant(); } }; } @Override Spliterator> entrySpliterator() { updateBackingRowMapField(); if (backingRowMap == null) { return Spliterators.emptySpliterator(); } return CollectSpliterators.map(backingRowMap.entrySet().spliterator(), this::wrapEntry); } Entry wrapEntry(final Entry entry) { return new ForwardingMapEntry() { @Override protected Entry delegate() { return entry; } @Override public V setValue(V value) { return super.setValue(checkNotNull(value)); } @Override public boolean equals(@CheckForNull Object object) { // TODO(lowasser): identify why this affects GWT tests return standardEquals(object); } }; } } /** * {@inheritDoc} * *

The returned map's views have iterators that don't support {@code remove()}. */ @Override public Map column(C columnKey) { return new Column(columnKey); } private class Column extends ViewCachingAbstractMap { final C columnKey; Column(C columnKey) { this.columnKey = checkNotNull(columnKey); } @Override @CheckForNull public V put(R key, V value) { return StandardTable.this.put(key, columnKey, value); } @Override @CheckForNull public V get(@CheckForNull Object key) { return StandardTable.this.get(key, columnKey); } @Override public boolean containsKey(@CheckForNull Object key) { return StandardTable.this.contains(key, columnKey); } @Override @CheckForNull public V remove(@CheckForNull Object key) { return StandardTable.this.remove(key, columnKey); } /** Removes all {@code Column} mappings whose row key and value satisfy the given predicate. */ @CanIgnoreReturnValue boolean removeFromColumnIf(Predicate> predicate) { boolean changed = false; Iterator>> iterator = backingMap.entrySet().iterator(); while (iterator.hasNext()) { Entry> entry = iterator.next(); Map map = entry.getValue(); V value = map.get(columnKey); if (value != null && predicate.apply(Maps.immutableEntry(entry.getKey(), value))) { map.remove(columnKey); changed = true; if (map.isEmpty()) { iterator.remove(); } } } return changed; } @Override Set> createEntrySet() { return new EntrySet(); } @WeakOuter private class EntrySet extends ImprovedAbstractSet> { @Override public Iterator> iterator() { return new EntrySetIterator(); } @Override public int size() { int size = 0; for (Map map : backingMap.values()) { if (map.containsKey(columnKey)) { size++; } } return size; } @Override public boolean isEmpty() { return !containsColumn(columnKey); } @Override public void clear() { removeFromColumnIf(alwaysTrue()); } @Override public boolean contains(@CheckForNull Object o) { if (o instanceof Entry) { Entry entry = (Entry) o; return containsMapping(entry.getKey(), columnKey, entry.getValue()); } return false; } @Override public boolean remove(@CheckForNull Object obj) { if (obj instanceof Entry) { Entry entry = (Entry) obj; return removeMapping(entry.getKey(), columnKey, entry.getValue()); } return false; } @Override public boolean retainAll(Collection c) { return removeFromColumnIf(not(in(c))); } } private class EntrySetIterator extends AbstractIterator> { final Iterator>> iterator = backingMap.entrySet().iterator(); @Override @CheckForNull protected Entry computeNext() { while (iterator.hasNext()) { final Entry> entry = iterator.next(); if (entry.getValue().containsKey(columnKey)) { @WeakOuter class EntryImpl extends AbstractMapEntry { @Override public R getKey() { return entry.getKey(); } @Override public V getValue() { return entry.getValue().get(columnKey); } @Override public V setValue(V value) { /* * The cast is safe because of the containsKey check above. (Well, it's possible for * the map to change between that call and this one. But if that happens, the * behavior is undefined because of the concurrent mutation.) * * (Our prototype checker happens to be "smart" enough to understand this for the * *get* call in getValue but not for the *put* call here.) * * (Arguably we should use requireNonNull rather than uncheckedCastNullableTToT: We * know that V is a non-null type because that's the only kind of value type that * StandardTable supports. Thus, requireNonNull is safe as long as the cell is still * present. (And if it's not present, behavior is undefined.) However, that's a * behavior change relative to the old code, so it didn't seem worth risking.) */ return uncheckedCastNullableTToT( entry.getValue().put(columnKey, checkNotNull(value))); } } return new EntryImpl(); } } return endOfData(); } } @Override Set createKeySet() { return new KeySet(); } @WeakOuter private class KeySet extends Maps.KeySet { KeySet() { super(Column.this); } @Override public boolean contains(@CheckForNull Object obj) { return StandardTable.this.contains(obj, columnKey); } @Override public boolean remove(@CheckForNull Object obj) { return StandardTable.this.remove(obj, columnKey) != null; } @Override public boolean retainAll(final Collection c) { return removeFromColumnIf(Maps.keyPredicateOnEntries(not(in(c)))); } } @Override Collection createValues() { return new Values(); } @WeakOuter private class Values extends Maps.Values { Values() { super(Column.this); } @Override public boolean remove(@CheckForNull Object obj) { return obj != null && removeFromColumnIf(Maps.valuePredicateOnEntries(equalTo(obj))); } @Override public boolean removeAll(final Collection c) { return removeFromColumnIf(Maps.valuePredicateOnEntries(in(c))); } @Override public boolean retainAll(final Collection c) { return removeFromColumnIf(Maps.valuePredicateOnEntries(not(in(c)))); } } } @Override public Set rowKeySet() { return rowMap().keySet(); } @LazyInit @CheckForNull private transient Set columnKeySet; /** * {@inheritDoc} * *

The returned set has an iterator that does not support {@code remove()}. * *

The set's iterator traverses the columns of the first row, the columns of the second row, * etc., skipping any columns that have appeared previously. */ @Override public Set columnKeySet() { Set result = columnKeySet; return (result == null) ? columnKeySet = new ColumnKeySet() : result; } @WeakOuter private class ColumnKeySet extends TableSet { @Override public Iterator iterator() { return createColumnKeyIterator(); } @Override public int size() { return Iterators.size(iterator()); } @Override public boolean remove(@CheckForNull Object obj) { if (obj == null) { return false; } boolean changed = false; Iterator> iterator = backingMap.values().iterator(); while (iterator.hasNext()) { Map map = iterator.next(); if (map.keySet().remove(obj)) { changed = true; if (map.isEmpty()) { iterator.remove(); } } } return changed; } @Override public boolean removeAll(Collection c) { checkNotNull(c); boolean changed = false; Iterator> iterator = backingMap.values().iterator(); while (iterator.hasNext()) { Map map = iterator.next(); // map.keySet().removeAll(c) can throw a NPE when map is a TreeMap with // natural ordering and c contains a null. if (Iterators.removeAll(map.keySet().iterator(), c)) { changed = true; if (map.isEmpty()) { iterator.remove(); } } } return changed; } @Override public boolean retainAll(Collection c) { checkNotNull(c); boolean changed = false; Iterator> iterator = backingMap.values().iterator(); while (iterator.hasNext()) { Map map = iterator.next(); if (map.keySet().retainAll(c)) { changed = true; if (map.isEmpty()) { iterator.remove(); } } } return changed; } @Override public boolean contains(@CheckForNull Object obj) { return containsColumn(obj); } } /** Creates an iterator that returns each column value with duplicates omitted. */ Iterator createColumnKeyIterator() { return new ColumnKeyIterator(); } private class ColumnKeyIterator extends AbstractIterator { // Use the same map type to support TreeMaps with comparators that aren't // consistent with equals(). final Map seen = factory.get(); final Iterator> mapIterator = backingMap.values().iterator(); Iterator> entryIterator = Iterators.emptyIterator(); @Override @CheckForNull protected C computeNext() { while (true) { if (entryIterator.hasNext()) { Entry entry = entryIterator.next(); if (!seen.containsKey(entry.getKey())) { seen.put(entry.getKey(), entry.getValue()); return entry.getKey(); } } else if (mapIterator.hasNext()) { entryIterator = mapIterator.next().entrySet().iterator(); } else { return endOfData(); } } } } /** * {@inheritDoc} * *

The collection's iterator traverses the values for the first row, the values for the second * row, and so on. */ @Override public Collection values() { return super.values(); } @LazyInit @CheckForNull private transient Map> rowMap; @Override public Map> rowMap() { Map> result = rowMap; return (result == null) ? rowMap = createRowMap() : result; } Map> createRowMap() { return new RowMap(); } @WeakOuter class RowMap extends ViewCachingAbstractMap> { @Override public boolean containsKey(@CheckForNull Object key) { return containsRow(key); } // performing cast only when key is in backing map and has the correct type @SuppressWarnings("unchecked") @Override @CheckForNull public Map get(@CheckForNull Object key) { // requireNonNull is safe because of the containsRow check. return containsRow(key) ? row((R) requireNonNull(key)) : null; } @Override @CheckForNull public Map remove(@CheckForNull Object key) { return (key == null) ? null : backingMap.remove(key); } @Override protected Set>> createEntrySet() { return new EntrySet(); } @WeakOuter class EntrySet extends TableSet>> { @Override public Iterator>> iterator() { return Maps.asMapEntryIterator( backingMap.keySet(), new Function>() { @Override public Map apply(R rowKey) { return row(rowKey); } }); } @Override public int size() { return backingMap.size(); } @Override public boolean contains(@CheckForNull Object obj) { if (obj instanceof Entry) { Entry entry = (Entry) obj; return entry.getKey() != null && entry.getValue() instanceof Map && Collections2.safeContains(backingMap.entrySet(), entry); } return false; } @Override public boolean remove(@CheckForNull Object obj) { if (obj instanceof Entry) { Entry entry = (Entry) obj; return entry.getKey() != null && entry.getValue() instanceof Map && backingMap.entrySet().remove(entry); } return false; } } } @LazyInit @CheckForNull private transient ColumnMap columnMap; @Override public Map> columnMap() { ColumnMap result = columnMap; return (result == null) ? columnMap = new ColumnMap() : result; } @WeakOuter private class ColumnMap extends ViewCachingAbstractMap> { // The cast to C occurs only when the key is in the map, implying that it // has the correct type. @SuppressWarnings("unchecked") @Override @CheckForNull public Map get(@CheckForNull Object key) { // requireNonNull is safe because of the containsColumn check. return containsColumn(key) ? column((C) requireNonNull(key)) : null; } @Override public boolean containsKey(@CheckForNull Object key) { return containsColumn(key); } @Override @CheckForNull public Map remove(@CheckForNull Object key) { return containsColumn(key) ? removeColumn(key) : null; } @Override public Set>> createEntrySet() { return new ColumnMapEntrySet(); } @Override public Set keySet() { return columnKeySet(); } @Override Collection> createValues() { return new ColumnMapValues(); } @WeakOuter class ColumnMapEntrySet extends TableSet>> { @Override public Iterator>> iterator() { return Maps.asMapEntryIterator( columnKeySet(), new Function>() { @Override public Map apply(C columnKey) { return column(columnKey); } }); } @Override public int size() { return columnKeySet().size(); } @Override public boolean contains(@CheckForNull Object obj) { if (obj instanceof Entry) { Entry entry = (Entry) obj; if (containsColumn(entry.getKey())) { // requireNonNull is safe because of the containsColumn check. return requireNonNull(get(entry.getKey())).equals(entry.getValue()); } } return false; } @Override public boolean remove(@CheckForNull Object obj) { /* * `o instanceof Entry` is guaranteed by `contains`, but we check it here to satisfy our * nullness checker. */ if (contains(obj) && obj instanceof Entry) { Entry entry = (Entry) obj; removeColumn(entry.getKey()); return true; } return false; } @Override public boolean removeAll(Collection c) { /* * We can't inherit the normal implementation (which calls * Sets.removeAllImpl(Set, *Collection*)) because, under some * circumstances, it attempts to call columnKeySet().iterator().remove, * which is unsupported. */ checkNotNull(c); return Sets.removeAllImpl(this, c.iterator()); } @Override public boolean retainAll(Collection c) { checkNotNull(c); boolean changed = false; for (C columnKey : Lists.newArrayList(columnKeySet().iterator())) { if (!c.contains(Maps.immutableEntry(columnKey, column(columnKey)))) { removeColumn(columnKey); changed = true; } } return changed; } } @WeakOuter private class ColumnMapValues extends Maps.Values> { ColumnMapValues() { super(ColumnMap.this); } @Override public boolean remove(@CheckForNull Object obj) { for (Entry> entry : ColumnMap.this.entrySet()) { if (entry.getValue().equals(obj)) { removeColumn(entry.getKey()); return true; } } return false; } @Override public boolean removeAll(Collection c) { checkNotNull(c); boolean changed = false; for (C columnKey : Lists.newArrayList(columnKeySet().iterator())) { if (c.contains(column(columnKey))) { removeColumn(columnKey); changed = true; } } return changed; } @Override public boolean retainAll(Collection c) { checkNotNull(c); boolean changed = false; for (C columnKey : Lists.newArrayList(columnKeySet().iterator())) { if (!c.contains(column(columnKey))) { removeColumn(columnKey); changed = true; } } return changed; } } } private static final long serialVersionUID = 0; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy