diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Result.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Result.java index 74faab2..c6adabe 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Result.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Result.java @@ -58,20 +58,23 @@ import org.apache.hadoop.hbase.util.Bytes; * * To get the latest value for a specific family and qualifier use {@link #getValue(byte[], byte[])}. * - * A Result is backed by an array of {@link KeyValue} objects, each representing + * A Result is backed by an array of {@link Cell} objects, each representing * an HBase cell defined by the row, family, qualifier, timestamp, and value.
* - * The underlying {@link KeyValue} objects can be accessed through the method {@link #listCells()}. - * Each KeyValue can then be accessed through - * {@link KeyValue#getRow()}, {@link KeyValue#getFamily()}, {@link KeyValue#getQualifier()}, - * {@link KeyValue#getTimestamp()}, and {@link KeyValue#getValue()}.
+ * The underlying {@link Cell} objects can be accessed through the method {@link #listCells()}.
+ * This will create a List from the internal Cell []. Better is to exploit the fact that
+ * a new Result instance is a primed {@link CellScanner}; just call {@link #advance()} and
+ * {@link #current()} to iterate over Cells as you would any {@link CellScanner}.
+ * Call {@link #cellScanner()} to reset should you need to iterate the same Result over again
+ * ({@link CellScanner}s are one-shot).
*
- * If you need to overwrite a Result with another Result instance -- as in the old 'mapred' RecordReader next
- * invocations -- then create an empty Result with the null constructor and in then use {@link #copyFrom(Result)}
+ * If you need to overwrite a Result with another Result instance -- as in the old 'mapred'
+ * RecordReader next invocations -- then create an empty Result with the null constructor and
+ * in then use {@link #copyFrom(Result)}
*/
@InterfaceAudience.Public
@InterfaceStability.Stable
-public class Result implements CellScannable {
+public class Result implements CellScannable, CellScanner {
private Cell[] cells;
private Boolean exists; // if the query was just to check existence.
private boolean stale = false;
@@ -86,6 +89,13 @@ public class Result implements CellScannable {
private static final int PAD_WIDTH = 128;
public static final Result EMPTY_RESULT = new Result();
+ private final static int INITIAL_CELLSCANNER_INDEX = -1;
+
+ /**
+ * Index for where we are when Result is acting as a {@link CellScanner}.
+ */
+ private int cellScannerIndex = INITIAL_CELLSCANNER_INDEX;
+
/**
* Creates an empty Result w/ no KeyValue payload; returns null if you call {@link #rawCells()}.
* Use this to represent no results if null won't do or in old 'mapred' as oppposed to 'mapreduce' package
@@ -827,7 +837,21 @@ public class Result implements CellScannable {
@Override
public CellScanner cellScanner() {
- return CellUtil.createCellScanner(this.cells);
+ // Reset
+ this.cellScannerIndex = INITIAL_CELLSCANNER_INDEX;
+ return this;
+ }
+
+ @Override
+ public Cell current() {
+ if (cells == null) return null;
+ return (cellScannerIndex < 0)? null: this.cells[cellScannerIndex];
+ }
+
+ @Override
+ public boolean advance() {
+ if (cells == null) return false;
+ return ++cellScannerIndex < this.cells.length;
}
public Boolean getExists() {
@@ -846,5 +870,4 @@ public class Result implements CellScannable {
public boolean isStale() {
return stale;
}
-
-}
+}
\ No newline at end of file
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestResult.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestResult.java
index 74563f0..a0e1a45 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestResult.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestResult.java
@@ -21,6 +21,7 @@ package org.apache.hadoop.hbase.client;
import static org.apache.hadoop.hbase.HBaseTestCase.assertByteEquals;
+import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.List;
@@ -30,6 +31,7 @@ import junit.framework.TestCase;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hbase.Cell;
+import org.apache.hadoop.hbase.CellScanner;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.SmallTests;
@@ -60,6 +62,31 @@ public class TestResult extends TestCase {
static final byte [] family = Bytes.toBytes("family");
static final byte [] value = Bytes.toBytes("value");
+ /**
+ * Run some tests to ensure Result acts like a proper CellScanner.
+ * @throws IOException
+ */
+ public void testResultAsCellScanner() throws IOException {
+ Cell [] cells = genKVs(row, family, value, 1, 10);
+ Arrays.sort(cells, KeyValue.COMPARATOR);
+ Result r = Result.create(cells);
+ assertSame(r, cells);
+ // Assert I run over same result multiple times.
+ assertSame(r.cellScanner(), cells);
+ assertSame(r.cellScanner(), cells);
+ // Assert we are not creating new object when doing cellscanner
+ assertTrue(r == r.cellScanner());
+ }
+
+ private void assertSame(final CellScanner cellScanner, final Cell [] cells) throws IOException {
+ int count = 0;
+ while (cellScanner.advance()) {
+ assertTrue(cells[count].equals(cellScanner.current()));
+ count++;
+ }
+ assertEquals(cells.length, count);
+ }
+
public void testBasicGetColumn() throws Exception {
KeyValue [] kvs = genKVs(row, family, value, 1, 100);