Home | History | Annotate | Download | only in collect
      1 /*
      2  * Copyright (C) 2009 The Guava Authors
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
      5  * in compliance with the License. You may obtain a copy of the License at
      6  *
      7  * http://www.apache.org/licenses/LICENSE-2.0
      8  *
      9  * Unless required by applicable law or agreed to in writing, software distributed under the License
     10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
     11  * or implied. See the License for the specific language governing permissions and limitations under
     12  * the License.
     13  */
     14 
     15 package com.google.common.collect;
     16 
     17 import static com.google.common.base.Preconditions.checkNotNull;
     18 
     19 import com.google.common.annotations.GwtCompatible;
     20 
     21 import java.util.Collections;
     22 import java.util.Comparator;
     23 import java.util.List;
     24 
     25 import javax.annotation.Nullable;
     26 
     27 /**
     28  * An implementation of {@link ImmutableTable} holding an arbitrary number of
     29  * cells.
     30  *
     31  * @author Gregory Kick
     32  */
     33 @GwtCompatible
     34 abstract class RegularImmutableTable<R, C, V> extends ImmutableTable<R, C, V> {
     35   RegularImmutableTable() {}
     36 
     37   abstract Cell<R, C, V> getCell(int iterationIndex);
     38 
     39   @Override
     40   final ImmutableSet<Cell<R, C, V>> createCellSet() {
     41     return isEmpty() ? ImmutableSet.<Cell<R, C, V>>of() : new CellSet();
     42   }
     43 
     44   private final class CellSet extends ImmutableSet<Cell<R, C, V>> {
     45     @Override
     46     public int size() {
     47       return RegularImmutableTable.this.size();
     48     }
     49 
     50     @Override
     51     public UnmodifiableIterator<Cell<R, C, V>> iterator() {
     52       return asList().iterator();
     53     }
     54 
     55     @Override
     56     ImmutableList<Cell<R, C, V>> createAsList() {
     57       return new ImmutableAsList<Cell<R, C, V>>() {
     58         @Override
     59         public Cell<R, C, V> get(int index) {
     60           return getCell(index);
     61         }
     62 
     63         @Override
     64         ImmutableCollection<Cell<R, C, V>> delegateCollection() {
     65           return CellSet.this;
     66         }
     67       };
     68     }
     69 
     70     @Override
     71     public boolean contains(@Nullable Object object) {
     72       if (object instanceof Cell) {
     73         Cell<?, ?, ?> cell = (Cell<?, ?, ?>) object;
     74         Object value = get(cell.getRowKey(), cell.getColumnKey());
     75         return value != null && value.equals(cell.getValue());
     76       }
     77       return false;
     78     }
     79 
     80     @Override
     81     boolean isPartialView() {
     82       return false;
     83     }
     84   }
     85 
     86   abstract V getValue(int iterationIndex);
     87 
     88   @Override
     89   final ImmutableCollection<V> createValues() {
     90     return isEmpty() ? ImmutableList.<V>of() : new Values();
     91   }
     92 
     93   private final class Values extends ImmutableList<V> {
     94     @Override
     95     public int size() {
     96       return RegularImmutableTable.this.size();
     97     }
     98 
     99     @Override
    100     public V get(int index) {
    101       return getValue(index);
    102     }
    103 
    104     @Override
    105     boolean isPartialView() {
    106       return true;
    107     }
    108   }
    109 
    110   static <R, C, V> RegularImmutableTable<R, C, V> forCells(
    111       List<Cell<R, C, V>> cells,
    112       @Nullable final Comparator<? super R> rowComparator,
    113       @Nullable final Comparator<? super C> columnComparator) {
    114     checkNotNull(cells);
    115     if (rowComparator != null || columnComparator != null) {
    116       /*
    117        * This sorting logic leads to a cellSet() ordering that may not be expected and that isn't
    118        * documented in the Javadoc. If a row Comparator is provided, cellSet() iterates across the
    119        * columns in the first row, the columns in the second row, etc. If a column Comparator is
    120        * provided but a row Comparator isn't, cellSet() iterates across the rows in the first
    121        * column, the rows in the second column, etc.
    122        */
    123       Comparator<Cell<R, C, V>> comparator = new Comparator<Cell<R, C, V>>() {
    124         @Override public int compare(Cell<R, C, V> cell1, Cell<R, C, V> cell2) {
    125           int rowCompare = (rowComparator == null) ? 0
    126             : rowComparator.compare(cell1.getRowKey(), cell2.getRowKey());
    127           if (rowCompare != 0) {
    128             return rowCompare;
    129           }
    130           return (columnComparator == null) ? 0
    131               : columnComparator.compare(cell1.getColumnKey(), cell2.getColumnKey());
    132         }
    133       };
    134       Collections.sort(cells, comparator);
    135     }
    136     return forCellsInternal(cells, rowComparator, columnComparator);
    137   }
    138 
    139   static <R, C, V> RegularImmutableTable<R, C, V> forCells(
    140       Iterable<Cell<R, C, V>> cells) {
    141     return forCellsInternal(cells, null, null);
    142   }
    143 
    144   /**
    145    * A factory that chooses the most space-efficient representation of the
    146    * table.
    147    */
    148   private static final <R, C, V> RegularImmutableTable<R, C, V>
    149       forCellsInternal(Iterable<Cell<R, C, V>> cells,
    150           @Nullable Comparator<? super R> rowComparator,
    151           @Nullable Comparator<? super C> columnComparator) {
    152     ImmutableSet.Builder<R> rowSpaceBuilder = ImmutableSet.builder();
    153     ImmutableSet.Builder<C> columnSpaceBuilder = ImmutableSet.builder();
    154     ImmutableList<Cell<R, C, V>> cellList = ImmutableList.copyOf(cells);
    155     for (Cell<R, C, V> cell : cellList) {
    156       rowSpaceBuilder.add(cell.getRowKey());
    157       columnSpaceBuilder.add(cell.getColumnKey());
    158     }
    159 
    160     ImmutableSet<R> rowSpace = rowSpaceBuilder.build();
    161     if (rowComparator != null) {
    162       List<R> rowList = Lists.newArrayList(rowSpace);
    163       Collections.sort(rowList, rowComparator);
    164       rowSpace = ImmutableSet.copyOf(rowList);
    165     }
    166     ImmutableSet<C> columnSpace = columnSpaceBuilder.build();
    167     if (columnComparator != null) {
    168       List<C> columnList = Lists.newArrayList(columnSpace);
    169       Collections.sort(columnList, columnComparator);
    170       columnSpace = ImmutableSet.copyOf(columnList);
    171     }
    172 
    173     // use a dense table if more than half of the cells have values
    174     // TODO(gak): tune this condition based on empirical evidence
    175     return (cellList.size() > (((long) rowSpace.size() * columnSpace.size()) / 2)) ?
    176         new DenseImmutableTable<R, C, V>(cellList, rowSpace, columnSpace) :
    177         new SparseImmutableTable<R, C, V>(cellList, rowSpace, columnSpace);
    178   }
    179 }
    180