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

org.joda.collect.grid.DenseGrid Maven / Gradle / Ivy

The newest version!
/*
 *  Copyright 2014-present Stephen Colebourne
 *
 *  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 org.joda.collect.grid;

import java.io.Serializable;
import java.util.AbstractList;
import java.util.AbstractSet;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;

import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;

/**
 * Mutable implementation of the {@code Grid} data structure based on an array.
 * 

* This uses one item of memory for each possible combination of row and column. * * @param the type of the value * @author Stephen Colebourne */ public final class DenseGrid extends AbstractGrid implements Serializable { /** Serialization version. */ private static final long serialVersionUID = 1L; /** * The number of rows. */ private final int rowCount; /** * The number of columns. */ private final int columnCount; /** * The size. */ private int size; /** * The values. */ private final V[] values; //----------------------------------------------------------------------- /** * Creates an empty {@code DenseGrid} of the specified size. * * @param the type of the value * @param rowCount the number of rows, zero or greater * @param columnCount the number of rows, zero or greater * @return the mutable grid, not null */ public static DenseGrid create(int rowCount, int columnCount) { return new DenseGrid(rowCount, columnCount); } /** * Creates a {@code DenseGrid} copying from another grid. * * @param the type of the value * @param grid the grid to copy, not null * @return the mutable grid, not null */ @SuppressWarnings("unchecked") public static DenseGrid create(Grid grid) { if (grid == null) { throw new IllegalArgumentException("Grid must not be null"); } if (grid instanceof DenseImmutableGrid) { return new DenseGrid((DenseImmutableGrid) grid); } DenseGrid created = DenseGrid.create(grid.rowCount(), grid.columnCount()); created.putAll(grid); return created; } /** * Creates a {@code DenseGrid} copying from an array. *

* The row count and column count are derived from the maximum size of the array. * The grid is initialized from the non-null values. * * @param the type of the value * @param array the array, first by row, then by column * @return the mutable grid, not null */ public static DenseGrid create(V[][] array) { if (array == null) { throw new IllegalArgumentException("Array must not be null"); } int rowCount = array.length; if (rowCount == 0) { return new DenseGrid(0, 0); } int columnCount = array[0].length; for (int i = 1; i < rowCount; i++) { columnCount = Math.max(columnCount, array[i].length); } DenseGrid created = DenseGrid.create(rowCount, columnCount); for (int row = 0; row < array.length; row++) { V[] rowValues = array[row]; for (int column = 0; column < rowValues.length; column++) { V cellValue = rowValues[column]; if (cellValue != null) { created.values[row * columnCount + column] = cellValue; created.size++; } } } return created; } //----------------------------------------------------------------------- /** * Restricted constructor. */ @SuppressWarnings("unchecked") private DenseGrid(int rowCount, int columnCount) { validateCounts(rowCount, columnCount); this.rowCount = rowCount; this.columnCount = columnCount; this.values = (V[]) new Object[rowCount * columnCount]; } /** * Restricted constructor. */ private DenseGrid(DenseImmutableGrid grid) { this.rowCount = grid.rowCount(); this.columnCount = grid.columnCount(); this.size = grid.size(); this.values = grid.valuesArray(); } //----------------------------------------------------------------------- @Override public int rowCount() { return rowCount; } @Override public int columnCount() { return columnCount; } @Override public int size() { return size; } @Override public boolean contains(int row, int column) { if (exists(row, column)) { return values[row * columnCount + column] != null; } return false; } @Override public boolean containsValue(Object valueToFind) { if (valueToFind != null) { for (Object value : values) { if (valueToFind.equals(value)) { return true; } } } return false; } @Override public V get(int row, int column) { if (exists(row, column)) { return values[row * columnCount + column]; } return null; } @Override public Cell cell(int row, int column) { V value = get(row, column); return (value != null ? ImmutableCell.of(row, column, value) : null); } //----------------------------------------------------------------------- @Override public Set> cells() { return new Cells(this); } /** * View onto the grid. */ static class Cells extends AbstractSet> { private final DenseGrid grid; Cells(DenseGrid grid) { this.grid = grid; } @Override public int size() { return grid.size; } @Override public boolean contains(Object obj) { Cell cell = (Cell) obj; return Objects.equal(cell.getValue(), grid.get(cell.getRow(), cell.getColumn())); } @Override public Iterator> iterator() { return new Iterator>() { private MutableCell cell = new MutableCell(); private int count; private int current = -1; @Override public boolean hasNext() { return (count < grid.size); } @Override public Cell next() { if (!hasNext()) { throw new NoSuchElementException("No more elements"); } current++; for ( ; current < grid.values.length; current++) { if (grid.values[current] != null) { break; } } V value = grid.values[current]; count++; cell.set(current / grid.columnCount, current % grid.columnCount, value); return cell; } @Override public void remove() { if (current < 0) { throw new IllegalStateException("Unable to remove, next() not called yet"); } if (grid.values[current] == null) { throw new IllegalStateException("Unable to remove, element has been removed"); } grid.values[current] = null; grid.size--; count--; } }; } @Override public boolean add(Cell cell) { Preconditions.checkArgument(cell != null, "Cell must not be null"); int oldSize = grid.size; grid.put(cell.getRow(), cell.getColumn(), cell.getValue()); return grid.size > oldSize; } @Override public boolean remove(Object obj) { Cell cell = (Cell) obj; return grid.remove(cell.getRow(), cell.getColumn()); } @Override public void clear() { grid.clear(); } } //----------------------------------------------------------------------- @Override @SuppressWarnings("unchecked") public ImmutableCollection values() { Object[] array = new Object[size]; int index = 0; for (Object object : values) { if (object != null) { array[index++] = object; } } return ImmutableList.copyOf((V[]) array); } //----------------------------------------------------------------------- @Override public List row(int row) { Preconditions.checkElementIndex(row, rowCount(), "Row index"); int base = row * rowCount; return new Inner(this, base, columnCount, 1); } @Override public List> rows() { return new Outer(this, rowCount, columnCount, columnCount, 1); } @Override public List column(int column) { Preconditions.checkElementIndex(column, columnCount(), "Column index"); return new Inner(this, column, rowCount, columnCount); } @Override public List> columns() { return new Outer(this, columnCount, 1, rowCount, columnCount); } static class Outer extends AbstractList> { private final DenseGrid grid; private final int size; private final int gap; private final int innerSize; private final int innerGap; Outer(DenseGrid grid, int size, int gap, int innerSize, int innerGap) { this.grid = grid; this.size = size; this.gap = gap; this.innerSize = innerSize; this.innerGap = innerGap; } @Override public int size() { return size; } @Override public List get(int index) { Preconditions.checkElementIndex(index, size); int base = index * gap; return new Inner(grid, base, innerSize, innerGap); } } static class Inner extends AbstractList { private final DenseGrid grid; private final int size; private final int offset; private final int gap; Inner(DenseGrid grid, int offset, int size, int gap) { this.grid = grid; this.size = size; this.offset = offset; this.gap = gap; } @Override public int size() { return size; } @Override public V get(int index) { Preconditions.checkElementIndex(index, size); int arrayIndex = index * gap + offset; return grid.values[arrayIndex]; } @Override public V set(int index, V newValue) { Preconditions.checkElementIndex(index, size); int arrayIndex = index * gap + offset; V old = grid.values[arrayIndex]; grid.values[arrayIndex] = newValue; if (old == null && newValue != null) { grid.size++; } else if (old != null && newValue == null) { grid.size--; } return old; } } //----------------------------------------------------------------------- @Override public void clear() { Arrays.fill(values, null); size = 0; } @Override public void put(int row, int column, V value) { if (!exists(row, column)) { throw new IndexOutOfBoundsException("Invalid row-column: " + row + "," + column); } if (value == null) { throw new IllegalArgumentException("Value must not be null"); } Object current = values[row * columnCount + column]; values[row * columnCount + column] = value; if (current == null) { size++; } } @Override public void putAll(Grid grid) { if (grid == null) { throw new IllegalArgumentException("Grid must nor be null"); } for (Cell cell : grid.cells()) { put(cell.getRow(), cell.getColumn(), cell.getValue()); } } @Override public boolean remove(int row, int column) { V current = get(row, column); if (current != null) { values[row * columnCount + column] = null; size--; return true; } return false; } //----------------------------------------------------------------------- /** * Returns a clone of the internal array. * * @return the array, not null */ V[] valuesArray() { return values.clone(); } //----------------------------------------------------------------------- @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof DenseGrid) { DenseGrid other = (DenseGrid) obj; return Arrays.equals(values, other.values); } return super.equals(obj); } @Override public int hashCode() { int hash = 0; for (int i = 0; i < values.length; i++) { Object value = values[i]; if (value != null) { int row = i / columnCount; int column = i % columnCount; hash += (row ^ Integer.rotateLeft(column, 16) ^ value.hashCode()); } } return rowCount ^ Integer.rotateLeft(columnCount, 16) ^ hash; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy