This is an automated email from the ASF dual-hosted git repository.

jbonofre pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-java.git


The following commit(s) were added to refs/heads/main by this push:
     new 71c418c2f GH-948: Use buffer indexing for UUID vector (#949)
71c418c2f is described below

commit 71c418c2fde3c49d4cfa4ec564514d04f66f71bf
Author: Joana Hrotko <[email protected]>
AuthorDate: Mon Jan 19 13:41:06 2026 +0000

    GH-948: Use buffer indexing for UUID vector (#949)
    
    ## What's Changed
    
    The current UUID vector implementation creates new buffer slices when
    reading values through holders, which has several drawbacks:
    - Memory overhead: Each slice creates a new ArrowBuf object
    - Performance impact: Buffer slicing is slower than direct buffer
    indexing
    - Inconsistency: Other fixed-width types (like Decimal) use buffer
    indexing with a `start` offset field
    
    ### Proposed Changes
    
    1. Add `start` field to UUID holders to track buffer offsets:
       - `UuidHolder`: Add `public int start = 0;`
       - `NullableUuidHolder`: Add `public int start = 0;`
    2. Update `UuidVector` to use buffer indexing
    3. Update readers and writers
    
    ### Related Work
    
    - Original UUID extension type implementation: GH-825 (#903)
    
    Closes #948
---
 .../apache/arrow/vector/UuidVectorBenchmarks.java  | 134 +++++++++
 .../java/org/apache/arrow/vector/UuidVector.java   | 115 +++----
 .../complex/impl/NullableUuidHolderReaderImpl.java | 123 ++++++++
 .../arrow/vector/complex/impl/UuidReaderImpl.java  |   8 +-
 .../arrow/vector/complex/impl/UuidWriterImpl.java  |   9 +-
 .../arrow/vector/holders/NullableUuidHolder.java   |   3 +
 .../apache/arrow/vector/holders/UuidHolder.java    |   3 +
 .../apache/arrow/vector/TestLargeListVector.java   |  12 +-
 .../org/apache/arrow/vector/TestListVector.java    |  24 +-
 .../org/apache/arrow/vector/TestMapVector.java     |  20 +-
 .../java/org/apache/arrow/vector/TestUuidType.java |   3 +-
 .../org/apache/arrow/vector/TestUuidVector.java    | 334 ++++++++++++++++++---
 .../vector/complex/writer/TestComplexWriter.java   |   4 +-
 13 files changed, 649 insertions(+), 143 deletions(-)

diff --git 
a/performance/src/main/java/org/apache/arrow/vector/UuidVectorBenchmarks.java 
b/performance/src/main/java/org/apache/arrow/vector/UuidVectorBenchmarks.java
new file mode 100644
index 000000000..b5f87e7a7
--- /dev/null
+++ 
b/performance/src/main/java/org/apache/arrow/vector/UuidVectorBenchmarks.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.arrow.vector;
+
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import org.apache.arrow.memory.BufferAllocator;
+import org.apache.arrow.memory.RootAllocator;
+import org.apache.arrow.vector.complex.impl.UuidWriterImpl;
+import org.apache.arrow.vector.holders.NullableUuidHolder;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
+import org.openjdk.jmh.profile.GCProfiler;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+
+/** Benchmarks for {@link UuidVector}. */
+@State(Scope.Benchmark)
+public class UuidVectorBenchmarks {
+  // checkstyle:off: MissingJavadocMethod
+
+  private static final int VECTOR_LENGTH = 10_000;
+
+  private static final int ALLOCATOR_CAPACITY = 1024 * 1024;
+
+  private BufferAllocator allocator;
+
+  private UuidVector vector;
+
+  private UUID[] testUuids;
+
+  @Setup
+  public void prepare() {
+    allocator = new RootAllocator(ALLOCATOR_CAPACITY);
+    vector = new UuidVector("vector", allocator);
+    vector.allocateNew(VECTOR_LENGTH);
+    vector.setValueCount(VECTOR_LENGTH);
+
+    // Pre-generate UUIDs for consistent benchmarking
+    testUuids = new UUID[VECTOR_LENGTH];
+    for (int i = 0; i < VECTOR_LENGTH; i++) {
+      testUuids[i] = new UUID(i, i * 2L);
+    }
+  }
+
+  @TearDown
+  public void tearDown() {
+    vector.close();
+    allocator.close();
+  }
+
+  @Benchmark
+  @BenchmarkMode(Mode.AverageTime)
+  @OutputTimeUnit(TimeUnit.MICROSECONDS)
+  public void setWithHolder() {
+    NullableUuidHolder holder = new NullableUuidHolder();
+    for (int i = 0; i < VECTOR_LENGTH; i++) {
+      vector.get(i, holder);
+      vector.setSafe(i, holder);
+    }
+  }
+
+  @Benchmark
+  @BenchmarkMode(Mode.AverageTime)
+  @OutputTimeUnit(TimeUnit.MICROSECONDS)
+  public void setUuidDirectly() {
+    for (int i = 0; i < VECTOR_LENGTH; i++) {
+      vector.setSafe(i, testUuids[i]);
+    }
+  }
+
+  @Benchmark
+  @BenchmarkMode(Mode.AverageTime)
+  @OutputTimeUnit(TimeUnit.MICROSECONDS)
+  public void setWithWriter() {
+    UuidWriterImpl writer = new UuidWriterImpl(vector);
+    for (int i = 0; i < VECTOR_LENGTH; i++) {
+      writer.writeExtension(testUuids[i]);
+    }
+  }
+
+  @Benchmark
+  @BenchmarkMode(Mode.AverageTime)
+  @OutputTimeUnit(TimeUnit.MICROSECONDS)
+  public void getWithUuidHolder() {
+    NullableUuidHolder holder = new NullableUuidHolder();
+    for (int i = 0; i < VECTOR_LENGTH; i++) {
+      vector.get(i, holder);
+    }
+  }
+
+  @Benchmark
+  @BenchmarkMode(Mode.AverageTime)
+  @OutputTimeUnit(TimeUnit.MICROSECONDS)
+  public void getUuidDirectly() {
+    for (int i = 0; i < VECTOR_LENGTH; i++) {
+      UUID uuid = vector.getObject(i);
+    }
+  }
+
+  public static void main(String[] args) throws RunnerException {
+    Options opt =
+        new OptionsBuilder()
+            .include(UuidVectorBenchmarks.class.getSimpleName())
+            .forks(1)
+            .addProfiler(GCProfiler.class)
+            .build();
+
+    new Runner(opt).run();
+  }
+  // checkstyle:on: MissingJavadocMethod
+}
diff --git a/vector/src/main/java/org/apache/arrow/vector/UuidVector.java 
b/vector/src/main/java/org/apache/arrow/vector/UuidVector.java
index e0dadd1c6..e1e61a5a2 100644
--- a/vector/src/main/java/org/apache/arrow/vector/UuidVector.java
+++ b/vector/src/main/java/org/apache/arrow/vector/UuidVector.java
@@ -23,7 +23,9 @@ import java.util.UUID;
 import org.apache.arrow.memory.ArrowBuf;
 import org.apache.arrow.memory.BufferAllocator;
 import org.apache.arrow.memory.util.ArrowBufPointer;
+import org.apache.arrow.memory.util.ByteFunctionHelpers;
 import org.apache.arrow.memory.util.hash.ArrowBufHasher;
+import org.apache.arrow.util.Preconditions;
 import org.apache.arrow.vector.complex.impl.UuidReaderImpl;
 import org.apache.arrow.vector.complex.reader.FieldReader;
 import org.apache.arrow.vector.extension.UuidType;
@@ -132,7 +134,8 @@ public class UuidVector extends 
ExtensionTypeVector<FixedSizeBinaryVector>
 
   @Override
   public int hashCode(int index, ArrowBufHasher hasher) {
-    return getUnderlyingVector().hashCode(index, hasher);
+    int start = this.getStartOffset(index);
+    return ByteFunctionHelpers.hash(hasher, this.getDataBuffer(), start, start 
+ UUID_BYTE_WIDTH);
   }
 
   /**
@@ -145,21 +148,6 @@ public class UuidVector extends 
ExtensionTypeVector<FixedSizeBinaryVector>
     return getUnderlyingVector().isSet(index);
   }
 
-  /**
-   * Gets the UUID value at the given index as an ArrowBuf.
-   *
-   * @param index the index to retrieve
-   * @return a buffer slice containing the 16-byte UUID
-   * @throws IllegalStateException if the value at the index is null and null 
checking is enabled
-   */
-  public ArrowBuf get(int index) throws IllegalStateException {
-    if (NullCheckingForGet.NULL_CHECKING_ENABLED && this.isSet(index) == 0) {
-      throw new IllegalStateException("Value at index is null");
-    } else {
-      return getBufferSlicePostNullCheck(index);
-    }
-  }
-
   /**
    * Reads the UUID value at the given index into a NullableUuidHolder.
    *
@@ -167,23 +155,24 @@ public class UuidVector extends 
ExtensionTypeVector<FixedSizeBinaryVector>
    * @param holder the holder to populate with the UUID data
    */
   public void get(int index, NullableUuidHolder holder) {
-    if (NullCheckingForGet.NULL_CHECKING_ENABLED && this.isSet(index) == 0) {
+    Preconditions.checkArgument(index >= 0, "Cannot get negative index in UUID 
vector.");
+    if (isSet(index) == 0) {
       holder.isSet = 0;
-    } else {
-      holder.isSet = 1;
-      holder.buffer = getBufferSlicePostNullCheck(index);
+      return;
     }
+    holder.isSet = 1;
+    holder.buffer = getDataBuffer();
+    holder.start = getStartOffset(index);
   }
 
   /**
-   * Reads the UUID value at the given index into a UuidHolder.
+   * Calculates the byte offset for a given index in the data buffer.
    *
-   * @param index the index to read from
-   * @param holder the holder to populate with the UUID data
+   * @param index the index of the UUID value
+   * @return the byte offset in the data buffer
    */
-  public void get(int index, UuidHolder holder) {
-    holder.isSet = 1;
-    holder.buffer = getBufferSlicePostNullCheck(index);
+  public final int getStartOffset(int index) {
+    return index * UUID_BYTE_WIDTH;
   }
 
   /**
@@ -207,7 +196,7 @@ public class UuidVector extends 
ExtensionTypeVector<FixedSizeBinaryVector>
    * @param holder the holder containing the UUID data
    */
   public void set(int index, UuidHolder holder) {
-    this.set(index, holder.isSet, holder.buffer);
+    this.set(index, holder.buffer, holder.start);
   }
 
   /**
@@ -217,28 +206,11 @@ public class UuidVector extends 
ExtensionTypeVector<FixedSizeBinaryVector>
    * @param holder the holder containing the UUID data
    */
   public void set(int index, NullableUuidHolder holder) {
-    this.set(index, holder.isSet, holder.buffer);
-  }
-
-  /**
-   * Sets the UUID value at the given index with explicit null flag.
-   *
-   * @param index the index to set
-   * @param isSet 1 if the value is set, 0 if null
-   * @param buffer the buffer containing the 16-byte UUID data
-   */
-  public void set(int index, int isSet, ArrowBuf buffer) {
-    getUnderlyingVector().set(index, isSet, buffer);
-  }
-
-  /**
-   * Sets the UUID value at the given index from an ArrowBuf.
-   *
-   * @param index the index to set
-   * @param value the buffer containing the 16-byte UUID data
-   */
-  public void set(int index, ArrowBuf value) {
-    getUnderlyingVector().set(index, value);
+    if (holder.isSet == 0) {
+      getUnderlyingVector().setNull(index);
+    } else {
+      this.set(index, holder.buffer, holder.start);
+    }
   }
 
   /**
@@ -249,10 +221,12 @@ public class UuidVector extends 
ExtensionTypeVector<FixedSizeBinaryVector>
    * @param sourceOffset the offset in the source buffer where the UUID data 
starts
    */
   public void set(int index, ArrowBuf source, int sourceOffset) {
-    // Copy bytes from source buffer to target vector data buffer
-    ArrowBuf dataBuffer = getUnderlyingVector().getDataBuffer();
-    dataBuffer.setBytes((long) index * UUID_BYTE_WIDTH, source, sourceOffset, 
UUID_BYTE_WIDTH);
-    getUnderlyingVector().setIndexDefined(index);
+    Preconditions.checkNotNull(source, "Cannot set UUID vector, the source 
buffer is null.");
+
+    BitVectorHelper.setBit(getUnderlyingVector().getValidityBuffer(), index);
+    getUnderlyingVector()
+        .getDataBuffer()
+        .setBytes((long) index * UUID_BYTE_WIDTH, source, sourceOffset, 
UUID_BYTE_WIDTH);
   }
 
   /**
@@ -286,10 +260,10 @@ public class UuidVector extends 
ExtensionTypeVector<FixedSizeBinaryVector>
    * @param holder the holder containing the UUID data, or null to set a null 
value
    */
   public void setSafe(int index, NullableUuidHolder holder) {
-    if (holder != null) {
-      getUnderlyingVector().setSafe(index, holder.isSet, holder.buffer);
-    } else {
+    if (holder == null || holder.isSet == 0) {
       getUnderlyingVector().setNull(index);
+    } else {
+      this.setSafe(index, holder.buffer, holder.start);
     }
   }
 
@@ -297,14 +271,23 @@ public class UuidVector extends 
ExtensionTypeVector<FixedSizeBinaryVector>
    * Sets the UUID value at the given index from a UuidHolder, expanding 
capacity if needed.
    *
    * @param index the index to set
-   * @param holder the holder containing the UUID data, or null to set a null 
value
+   * @param holder the holder containing the UUID data
    */
   public void setSafe(int index, UuidHolder holder) {
-    if (holder != null) {
-      getUnderlyingVector().setSafe(index, holder.isSet, holder.buffer);
-    } else {
-      getUnderlyingVector().setNull(index);
-    }
+    this.setSafe(index, holder.buffer, holder.start);
+  }
+
+  /**
+   * Sets the UUID value at the given index by copying from a source buffer, 
expanding capacity if
+   * needed.
+   *
+   * @param index the index to set
+   * @param buffer the source buffer to copy from
+   * @param start the offset in the source buffer where the UUID data starts
+   */
+  public void setSafe(int index, ArrowBuf buffer, int start) {
+    getUnderlyingVector().handleSafe(index);
+    this.set(index, buffer, start);
   }
 
   /**
@@ -400,15 +383,9 @@ public class UuidVector extends 
ExtensionTypeVector<FixedSizeBinaryVector>
     return getTransferPair(this.getField().getName(), allocator);
   }
 
-  private ArrowBuf getBufferSlicePostNullCheck(int index) {
-    return getUnderlyingVector()
-        .getDataBuffer()
-        .slice((long) index * UUID_BYTE_WIDTH, UUID_BYTE_WIDTH);
-  }
-
   @Override
   public int getTypeWidth() {
-    return getUnderlyingVector().getTypeWidth();
+    return UUID_BYTE_WIDTH;
   }
 
   /** {@link TransferPair} for {@link UuidVector}. */
diff --git 
a/vector/src/main/java/org/apache/arrow/vector/complex/impl/NullableUuidHolderReaderImpl.java
 
b/vector/src/main/java/org/apache/arrow/vector/complex/impl/NullableUuidHolderReaderImpl.java
new file mode 100644
index 000000000..7a5312f6e
--- /dev/null
+++ 
b/vector/src/main/java/org/apache/arrow/vector/complex/impl/NullableUuidHolderReaderImpl.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.arrow.vector.complex.impl;
+
+import org.apache.arrow.vector.holders.ExtensionHolder;
+import org.apache.arrow.vector.holders.NullableUuidHolder;
+import org.apache.arrow.vector.holders.UuidHolder;
+import org.apache.arrow.vector.types.Types;
+import org.apache.arrow.vector.util.UuidUtility;
+
+/**
+ * Reader implementation for reading UUID values from a {@link 
NullableUuidHolder}.
+ *
+ * <p>This reader wraps a single UUID holder value and provides methods to 
read from it. Unlike
+ * {@link UuidReaderImpl} which reads from a vector, this reader operates on a 
holder instance.
+ *
+ * @see NullableUuidHolder
+ * @see UuidReaderImpl
+ */
+public class NullableUuidHolderReaderImpl extends AbstractFieldReader {
+  private final NullableUuidHolder holder;
+
+  /**
+   * Constructs a reader for the given UUID holder.
+   *
+   * @param holder the UUID holder to read from
+   */
+  public NullableUuidHolderReaderImpl(NullableUuidHolder holder) {
+    this.holder = holder;
+  }
+
+  @Override
+  public int size() {
+    throw new UnsupportedOperationException(
+        "size() is not supported on NullableUuidHolderReaderImpl. "
+            + "This reader wraps a single UUID holder value, not a collection. 
"
+            + "Use UuidReaderImpl for vector-based UUID reading.");
+  }
+
+  @Override
+  public boolean next() {
+    throw new UnsupportedOperationException(
+        "next() is not supported on NullableUuidHolderReaderImpl. "
+            + "This reader wraps a single UUID holder value, not an iterator. "
+            + "Use UuidReaderImpl for vector-based UUID reading.");
+  }
+
+  @Override
+  public void setPosition(int index) {
+    throw new UnsupportedOperationException(
+        "setPosition() is not supported on NullableUuidHolderReaderImpl. "
+            + "This reader wraps a single UUID holder value, not a vector. "
+            + "Use UuidReaderImpl for vector-based UUID reading.");
+  }
+
+  @Override
+  public Types.MinorType getMinorType() {
+    return Types.MinorType.EXTENSIONTYPE;
+  }
+
+  @Override
+  public boolean isSet() {
+    return holder.isSet == 1;
+  }
+
+  @Override
+  public void read(ExtensionHolder h) {
+    if (h instanceof NullableUuidHolder) {
+      NullableUuidHolder nullableHolder = (NullableUuidHolder) h;
+      nullableHolder.buffer = this.holder.buffer;
+      nullableHolder.isSet = this.holder.isSet;
+      nullableHolder.start = this.holder.start;
+    } else if (h instanceof UuidHolder) {
+      UuidHolder uuidHolder = (UuidHolder) h;
+      uuidHolder.buffer = this.holder.buffer;
+      uuidHolder.start = this.holder.start;
+    } else {
+      throw new IllegalArgumentException(
+          "Unsupported holder type: "
+              + h.getClass().getName()
+              + ". "
+              + "Only NullableUuidHolder and UuidHolder are supported for UUID 
values. "
+              + "Provided holder type cannot be used to read UUID data.");
+    }
+  }
+
+  @Override
+  public Object readObject() {
+    if (!isSet()) {
+      return null;
+    }
+    // Convert UUID bytes to Java UUID object
+    try {
+      return UuidUtility.uuidFromArrowBuf(holder.buffer, holder.start);
+    } catch (Exception e) {
+      throw new RuntimeException(
+          String.format(
+              "Failed to read UUID from buffer. Invalid Arrow buffer state: "
+                  + "capacity=%d, readableBytes=%d, readerIndex=%d, 
writerIndex=%d, refCnt=%d. "
+                  + "The buffer must contain exactly 16 bytes of valid UUID 
data.",
+              holder.buffer.capacity(),
+              holder.buffer.readableBytes(),
+              holder.buffer.readerIndex(),
+              holder.buffer.writerIndex(),
+              holder.buffer.refCnt()),
+          e);
+    }
+  }
+}
diff --git 
a/vector/src/main/java/org/apache/arrow/vector/complex/impl/UuidReaderImpl.java 
b/vector/src/main/java/org/apache/arrow/vector/complex/impl/UuidReaderImpl.java
index bb35b960d..bb7ae13e5 100644
--- 
a/vector/src/main/java/org/apache/arrow/vector/complex/impl/UuidReaderImpl.java
+++ 
b/vector/src/main/java/org/apache/arrow/vector/complex/impl/UuidReaderImpl.java
@@ -63,9 +63,7 @@ public class UuidReaderImpl extends AbstractFieldReader {
 
   @Override
   public void read(ExtensionHolder holder) {
-    if (holder instanceof UuidHolder) {
-      vector.get(idx(), (UuidHolder) holder);
-    } else if (holder instanceof NullableUuidHolder) {
+    if (holder instanceof NullableUuidHolder) {
       vector.get(idx(), (NullableUuidHolder) holder);
     } else {
       throw new IllegalArgumentException(
@@ -75,9 +73,7 @@ public class UuidReaderImpl extends AbstractFieldReader {
 
   @Override
   public void read(int arrayIndex, ExtensionHolder holder) {
-    if (holder instanceof UuidHolder) {
-      vector.get(arrayIndex, (UuidHolder) holder);
-    } else if (holder instanceof NullableUuidHolder) {
+    if (holder instanceof NullableUuidHolder) {
       vector.get(arrayIndex, (NullableUuidHolder) holder);
     } else {
       throw new IllegalArgumentException(
diff --git 
a/vector/src/main/java/org/apache/arrow/vector/complex/impl/UuidWriterImpl.java 
b/vector/src/main/java/org/apache/arrow/vector/complex/impl/UuidWriterImpl.java
index ee3c79d5e..944b7e2e6 100644
--- 
a/vector/src/main/java/org/apache/arrow/vector/complex/impl/UuidWriterImpl.java
+++ 
b/vector/src/main/java/org/apache/arrow/vector/complex/impl/UuidWriterImpl.java
@@ -51,8 +51,15 @@ public class UuidWriterImpl extends 
AbstractExtensionTypeWriter<UuidVector> {
       vector.setSafe(getPosition(), (ArrowBuf) value);
     } else if (value instanceof java.util.UUID) {
       vector.setSafe(getPosition(), (java.util.UUID) value);
+    } else if (value instanceof ExtensionHolder) {
+      write((ExtensionHolder) value);
     } else {
-      throw new IllegalArgumentException("Unsupported value type for UUID: " + 
value.getClass());
+      throw new IllegalArgumentException(
+          "Unsupported value type for UUID: "
+              + value.getClass().getName()
+              + ". "
+              + "Supported types are: byte[] (16 bytes), ArrowBuf (16 bytes), 
or java.util.UUID. "
+              + "Convert your value to one of these types before writing.");
     }
     vector.setValueCount(getPosition() + 1);
   }
diff --git 
a/vector/src/main/java/org/apache/arrow/vector/holders/NullableUuidHolder.java 
b/vector/src/main/java/org/apache/arrow/vector/holders/NullableUuidHolder.java
index 7fa50ca76..6a2b4ff60 100644
--- 
a/vector/src/main/java/org/apache/arrow/vector/holders/NullableUuidHolder.java
+++ 
b/vector/src/main/java/org/apache/arrow/vector/holders/NullableUuidHolder.java
@@ -35,6 +35,9 @@ public class NullableUuidHolder extends ExtensionHolder {
   /** Buffer containing 16-byte UUID data. */
   public ArrowBuf buffer;
 
+  /** Offset in the buffer where the UUID data starts. */
+  public int start = 0;
+
   @Override
   public ArrowType type() {
     return UuidType.INSTANCE;
diff --git 
a/vector/src/main/java/org/apache/arrow/vector/holders/UuidHolder.java 
b/vector/src/main/java/org/apache/arrow/vector/holders/UuidHolder.java
index 8a0a66e43..9ec0305f3 100644
--- a/vector/src/main/java/org/apache/arrow/vector/holders/UuidHolder.java
+++ b/vector/src/main/java/org/apache/arrow/vector/holders/UuidHolder.java
@@ -33,6 +33,9 @@ public class UuidHolder extends ExtensionHolder {
   /** Buffer containing 16-byte UUID data. */
   public ArrowBuf buffer;
 
+  /** Offset in the buffer where the UUID data starts. */
+  public int start = 0;
+
   /** Constructs a UuidHolder with isSet = 1. */
   public UuidHolder() {
     this.isSet = 1;
diff --git 
a/vector/src/test/java/org/apache/arrow/vector/TestLargeListVector.java 
b/vector/src/test/java/org/apache/arrow/vector/TestLargeListVector.java
index 759c84651..ccc0d3e17 100644
--- a/vector/src/test/java/org/apache/arrow/vector/TestLargeListVector.java
+++ b/vector/src/test/java/org/apache/arrow/vector/TestLargeListVector.java
@@ -37,7 +37,7 @@ import 
org.apache.arrow.vector.complex.impl.UnionLargeListWriter;
 import org.apache.arrow.vector.complex.reader.FieldReader;
 import org.apache.arrow.vector.complex.writer.BaseWriter.ExtensionWriter;
 import org.apache.arrow.vector.extension.UuidType;
-import org.apache.arrow.vector.holders.UuidHolder;
+import org.apache.arrow.vector.holders.NullableUuidHolder;
 import org.apache.arrow.vector.types.Types.MinorType;
 import org.apache.arrow.vector.types.pojo.ArrowType;
 import org.apache.arrow.vector.types.pojo.Field;
@@ -1071,14 +1071,14 @@ public class TestLargeListVector {
       assertTrue(reader.isSet(), "first list shouldn't be null");
       reader.next();
       FieldReader uuidReader = reader.reader();
-      UuidHolder holder = new UuidHolder();
+      NullableUuidHolder holder = new NullableUuidHolder();
       uuidReader.read(holder);
-      UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
+      UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 
holder.start);
       assertEquals(u1, actualUuid);
       reader.next();
       uuidReader = reader.reader();
       uuidReader.read(holder);
-      actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
+      actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, holder.start);
       assertEquals(u2, actualUuid);
 
       // Verify second list
@@ -1087,12 +1087,12 @@ public class TestLargeListVector {
       reader.next();
       uuidReader = reader.reader();
       uuidReader.read(holder);
-      actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
+      actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, holder.start);
       assertEquals(u3, actualUuid);
       reader.next();
       uuidReader = reader.reader();
       uuidReader.read(holder);
-      actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
+      actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, holder.start);
       assertEquals(u4, actualUuid);
       reader.next();
       uuidReader = reader.reader();
diff --git a/vector/src/test/java/org/apache/arrow/vector/TestListVector.java 
b/vector/src/test/java/org/apache/arrow/vector/TestListVector.java
index e96ac3027..1fe4c59f6 100644
--- a/vector/src/test/java/org/apache/arrow/vector/TestListVector.java
+++ b/vector/src/test/java/org/apache/arrow/vector/TestListVector.java
@@ -40,8 +40,8 @@ import 
org.apache.arrow.vector.complex.writer.BaseWriter.ExtensionWriter;
 import org.apache.arrow.vector.extension.UuidType;
 import org.apache.arrow.vector.holders.DurationHolder;
 import org.apache.arrow.vector.holders.FixedSizeBinaryHolder;
+import org.apache.arrow.vector.holders.NullableUuidHolder;
 import org.apache.arrow.vector.holders.TimeStampMilliTZHolder;
-import org.apache.arrow.vector.holders.UuidHolder;
 import org.apache.arrow.vector.types.TimeUnit;
 import org.apache.arrow.vector.types.Types.MinorType;
 import org.apache.arrow.vector.types.pojo.ArrowType;
@@ -1254,14 +1254,14 @@ public class TestListVector {
       reader.setPosition(0);
       reader.next();
       FieldReader uuidReader = reader.reader();
-      UuidHolder holder = new UuidHolder();
+      NullableUuidHolder holder = new NullableUuidHolder();
       uuidReader.read(holder);
-      UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
+      UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 
holder.start);
       assertEquals(u1, actualUuid);
       reader.next();
       uuidReader = reader.reader();
       uuidReader.read(holder);
-      actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
+      actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, holder.start);
       assertEquals(u2, actualUuid);
     }
   }
@@ -1294,14 +1294,14 @@ public class TestListVector {
       reader.setPosition(0);
       reader.next();
       FieldReader uuidReader = reader.reader();
-      UuidHolder holder = new UuidHolder();
+      NullableUuidHolder holder = new NullableUuidHolder();
       uuidReader.read(holder);
-      UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
+      UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 
holder.start);
       assertEquals(u1, actualUuid);
       reader.next();
       uuidReader = reader.reader();
       uuidReader.read(holder);
-      actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
+      actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, holder.start);
       assertEquals(u2, actualUuid);
     }
   }
@@ -1350,14 +1350,14 @@ public class TestListVector {
       assertTrue(reader.isSet(), "first list shouldn't be null");
       reader.next();
       FieldReader uuidReader = reader.reader();
-      UuidHolder holder = new UuidHolder();
+      NullableUuidHolder holder = new NullableUuidHolder();
       uuidReader.read(holder);
-      UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
+      UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 
holder.start);
       assertEquals(u1, actualUuid);
       reader.next();
       uuidReader = reader.reader();
       uuidReader.read(holder);
-      actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
+      actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, holder.start);
       assertEquals(u2, actualUuid);
 
       // Verify second list
@@ -1366,12 +1366,12 @@ public class TestListVector {
       reader.next();
       uuidReader = reader.reader();
       uuidReader.read(holder);
-      actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
+      actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, holder.start);
       assertEquals(u3, actualUuid);
       reader.next();
       uuidReader = reader.reader();
       uuidReader.read(holder);
-      actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
+      actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, holder.start);
       assertEquals(u4, actualUuid);
       reader.next();
       uuidReader = reader.reader();
diff --git a/vector/src/test/java/org/apache/arrow/vector/TestMapVector.java 
b/vector/src/test/java/org/apache/arrow/vector/TestMapVector.java
index bfac1237a..274d2973b 100644
--- a/vector/src/test/java/org/apache/arrow/vector/TestMapVector.java
+++ b/vector/src/test/java/org/apache/arrow/vector/TestMapVector.java
@@ -42,7 +42,7 @@ import 
org.apache.arrow.vector.complex.writer.BaseWriter.MapWriter;
 import org.apache.arrow.vector.complex.writer.FieldWriter;
 import org.apache.arrow.vector.extension.UuidType;
 import org.apache.arrow.vector.holders.FixedSizeBinaryHolder;
-import org.apache.arrow.vector.holders.UuidHolder;
+import org.apache.arrow.vector.holders.NullableUuidHolder;
 import org.apache.arrow.vector.types.Types.MinorType;
 import org.apache.arrow.vector.types.pojo.ArrowType;
 import org.apache.arrow.vector.types.pojo.Field;
@@ -1299,14 +1299,14 @@ public class TestMapVector {
       mapReader.setPosition(0);
       mapReader.next();
       FieldReader uuidReader = mapReader.value();
-      UuidHolder holder = new UuidHolder();
+      NullableUuidHolder holder = new NullableUuidHolder();
       uuidReader.read(holder);
-      UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
+      UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 
holder.start);
       assertEquals(u1, actualUuid);
       mapReader.next();
       uuidReader = mapReader.value();
       uuidReader.read(holder);
-      actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
+      actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, holder.start);
       assertEquals(u2, actualUuid);
     }
   }
@@ -1341,14 +1341,14 @@ public class TestMapVector {
       mapReader.setPosition(0);
       mapReader.next();
       FieldReader uuidReader = mapReader.value();
-      UuidHolder holder = new UuidHolder();
+      NullableUuidHolder holder = new NullableUuidHolder();
       uuidReader.read(holder);
-      UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
+      UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 
holder.start);
       assertEquals(u1, actualUuid);
       mapReader.next();
       uuidReader = mapReader.value();
       uuidReader.read(holder);
-      actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
+      actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, holder.start);
       assertEquals(u2, actualUuid);
     }
   }
@@ -1626,9 +1626,9 @@ public class TestMapVector {
       // Read first entry
       mapReader.next();
       FieldReader keyReader = mapReader.key();
-      UuidHolder keyHolder = new UuidHolder();
+      NullableUuidHolder keyHolder = new NullableUuidHolder();
       keyReader.read(keyHolder);
-      UUID actualKey = UuidUtility.uuidFromArrowBuf(keyHolder.buffer, 0);
+      UUID actualKey = UuidUtility.uuidFromArrowBuf(keyHolder.buffer, 
keyHolder.start);
       assertEquals(key1, actualKey);
 
       FieldReader valueReader = mapReader.value();
@@ -1648,7 +1648,7 @@ public class TestMapVector {
       mapReader.next();
       keyReader = mapReader.key();
       keyReader.read(keyHolder);
-      actualKey = UuidUtility.uuidFromArrowBuf(keyHolder.buffer, 0);
+      actualKey = UuidUtility.uuidFromArrowBuf(keyHolder.buffer, 
keyHolder.start);
       assertEquals(key2, actualKey);
 
       valueReader = mapReader.value();
diff --git a/vector/src/test/java/org/apache/arrow/vector/TestUuidType.java 
b/vector/src/test/java/org/apache/arrow/vector/TestUuidType.java
index acf9dd686..99045d1cb 100644
--- a/vector/src/test/java/org/apache/arrow/vector/TestUuidType.java
+++ b/vector/src/test/java/org/apache/arrow/vector/TestUuidType.java
@@ -233,7 +233,8 @@ class TestUuidType {
 
       // Verify the bytes match
       byte[] actualBytes = new byte[UuidType.UUID_BYTE_WIDTH];
-      uuidVector.get(0).getBytes(0, actualBytes);
+      int offset = uuidVector.getStartOffset(0);
+      uuidVector.getDataBuffer().getBytes(offset, actualBytes);
       assertArrayEquals(uuidBytes, actualBytes);
     }
   }
diff --git a/vector/src/test/java/org/apache/arrow/vector/TestUuidVector.java 
b/vector/src/test/java/org/apache/arrow/vector/TestUuidVector.java
index a3690461c..b5dd12d89 100644
--- a/vector/src/test/java/org/apache/arrow/vector/TestUuidVector.java
+++ b/vector/src/test/java/org/apache/arrow/vector/TestUuidVector.java
@@ -27,6 +27,7 @@ import java.util.UUID;
 import org.apache.arrow.memory.ArrowBuf;
 import org.apache.arrow.memory.BufferAllocator;
 import org.apache.arrow.memory.RootAllocator;
+import org.apache.arrow.vector.complex.impl.NullableUuidHolderReaderImpl;
 import org.apache.arrow.vector.complex.impl.UuidReaderImpl;
 import org.apache.arrow.vector.complex.impl.UuidWriterImpl;
 import org.apache.arrow.vector.extension.UuidType;
@@ -136,8 +137,8 @@ class TestUuidVector {
       IllegalArgumentException exception =
           assertThrows(IllegalArgumentException.class, () -> 
writer.writeExtension("invalid-type"));
 
-      assertEquals(
-          "Unsupported value type for UUID: class java.lang.String", 
exception.getMessage());
+      assertTrue(
+          exception.getMessage().contains("Unsupported value type for UUID: 
java.lang.String"));
     }
   }
 
@@ -235,9 +236,9 @@ class TestUuidVector {
       UuidReaderImpl reader = (UuidReaderImpl) vectorForRead.getReader();
       reader.copyAsValue(writer);
       UuidReaderImpl reader2 = (UuidReaderImpl) vector.getReader();
-      UuidHolder holder = new UuidHolder();
+      NullableUuidHolder holder = new NullableUuidHolder();
       reader2.read(0, holder);
-      UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
+      UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 
holder.start);
       assertEquals(uuid, actualUuid);
     }
   }
@@ -252,10 +253,10 @@ class TestUuidVector {
       UuidReaderImpl reader = (UuidReaderImpl) vector.getReader();
       reader.setPosition(0);
 
-      UuidHolder holder = new UuidHolder();
+      NullableUuidHolder holder = new NullableUuidHolder();
       reader.read(holder);
 
-      UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
+      UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 
holder.start);
       assertEquals(uuid, actualUuid);
       assertEquals(1, holder.isSet);
     }
@@ -274,7 +275,7 @@ class TestUuidVector {
       NullableUuidHolder holder = new NullableUuidHolder();
       reader.read(holder);
 
-      UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
+      UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 
holder.start);
       assertEquals(uuid, actualUuid);
       assertEquals(1, holder.isSet);
     }
@@ -310,10 +311,10 @@ class TestUuidVector {
 
       UuidReaderImpl reader = (UuidReaderImpl) vector.getReader();
 
-      UuidHolder holder = new UuidHolder();
+      NullableUuidHolder holder = new NullableUuidHolder();
       reader.read(1, holder);
 
-      UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
+      UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 
holder.start);
       assertEquals(uuid2, actualUuid);
       assertEquals(1, holder.isSet);
     }
@@ -334,7 +335,7 @@ class TestUuidVector {
 
       NullableUuidHolder holder1 = new NullableUuidHolder();
       reader.read(0, holder1);
-      assertEquals(uuid1, UuidUtility.uuidFromArrowBuf(holder1.buffer, 0));
+      assertEquals(uuid1, UuidUtility.uuidFromArrowBuf(holder1.buffer, 
holder1.start));
       assertEquals(1, holder1.isSet);
 
       NullableUuidHolder holder2 = new NullableUuidHolder();
@@ -343,7 +344,7 @@ class TestUuidVector {
 
       NullableUuidHolder holder3 = new NullableUuidHolder();
       reader.read(2, holder3);
-      assertEquals(uuid2, UuidUtility.uuidFromArrowBuf(holder3.buffer, 0));
+      assertEquals(uuid2, UuidUtility.uuidFromArrowBuf(holder3.buffer, 
holder3.start));
       assertEquals(1, holder3.isSet);
     }
   }
@@ -374,31 +375,6 @@ class TestUuidVector {
     }
   }
 
-  @Test
-  void testReaderReadWithArrayIndexUnsupportedHolder() throws Exception {
-    try (UuidVector vector = new UuidVector("test", allocator)) {
-      UUID uuid = UUID.randomUUID();
-      vector.setSafe(0, uuid);
-      vector.setValueCount(1);
-
-      UuidReaderImpl reader = (UuidReaderImpl) vector.getReader();
-
-      // Create a mock unsupported holder
-      ExtensionHolder unsupportedHolder =
-          new ExtensionHolder() {
-            @Override
-            public ArrowType type() {
-              return null;
-            }
-          };
-
-      IllegalArgumentException exception =
-          assertThrows(IllegalArgumentException.class, () -> reader.read(0, 
unsupportedHolder));
-
-      assertTrue(exception.getMessage().contains("Unsupported holder type for 
UuidReader"));
-    }
-  }
-
   @Test
   void testReaderIsSet() throws Exception {
     try (UuidVector vector = new UuidVector("test", allocator)) {
@@ -461,4 +437,290 @@ class TestUuidVector {
       assertEquals("test", reader.getField().getName());
     }
   }
+
+  @Test
+  void testHolderStartOffsetWithMultipleValues() throws Exception {
+    try (UuidVector vector = new UuidVector("test", allocator)) {
+      UUID uuid1 = UUID.randomUUID();
+      UUID uuid2 = UUID.randomUUID();
+      UUID uuid3 = UUID.randomUUID();
+
+      vector.setSafe(0, uuid1);
+      vector.setSafe(1, uuid2);
+      vector.setSafe(2, uuid3);
+      vector.setValueCount(3);
+
+      // Test UuidHolder with different indices
+      NullableUuidHolder holder = new NullableUuidHolder();
+      vector.get(0, holder);
+      assertEquals(0, holder.start);
+      assertEquals(uuid1, UuidUtility.uuidFromArrowBuf(holder.buffer, 
holder.start));
+
+      vector.get(1, holder);
+      assertEquals(16, holder.start); // UUID_BYTE_WIDTH = 16
+      assertEquals(uuid2, UuidUtility.uuidFromArrowBuf(holder.buffer, 
holder.start));
+
+      vector.get(2, holder);
+      assertEquals(32, holder.start); // 2 * UUID_BYTE_WIDTH = 32
+      assertEquals(uuid3, UuidUtility.uuidFromArrowBuf(holder.buffer, 
holder.start));
+    }
+  }
+
+  @Test
+  void testNullableHolderStartOffsetWithMultipleValues() throws Exception {
+    try (UuidVector vector = new UuidVector("test", allocator)) {
+      UUID uuid1 = UUID.randomUUID();
+      UUID uuid2 = UUID.randomUUID();
+
+      vector.setSafe(0, uuid1);
+      vector.setNull(1);
+      vector.setSafe(2, uuid2);
+      vector.setValueCount(3);
+
+      // Test NullableUuidHolder with different indices
+      NullableUuidHolder holder1 = new NullableUuidHolder();
+      vector.get(0, holder1);
+      assertEquals(0, holder1.start);
+      assertEquals(1, holder1.isSet);
+      assertEquals(uuid1, UuidUtility.uuidFromArrowBuf(holder1.buffer, 
holder1.start));
+
+      NullableUuidHolder holder2 = new NullableUuidHolder();
+      vector.get(1, holder2);
+      assertEquals(0, holder2.isSet);
+
+      NullableUuidHolder holder3 = new NullableUuidHolder();
+      vector.get(2, holder3);
+      assertEquals(32, holder3.start); // 2 * UUID_BYTE_WIDTH = 32
+      assertEquals(1, holder3.isSet);
+      assertEquals(uuid2, UuidUtility.uuidFromArrowBuf(holder3.buffer, 
holder3.start));
+
+      // Verify all holders share the same buffer
+      assertEquals(holder1.buffer, holder3.buffer);
+    }
+  }
+
+  @Test
+  void testSetFromHolderWithStartOffset() throws Exception {
+    try (UuidVector sourceVector = new UuidVector("source", allocator);
+        UuidVector targetVector = new UuidVector("target", allocator)) {
+      UUID uuid1 = UUID.randomUUID();
+      UUID uuid2 = UUID.randomUUID();
+
+      sourceVector.setSafe(0, uuid1);
+      sourceVector.setSafe(1, uuid2);
+      sourceVector.setValueCount(3);
+
+      // Get holder from index 1 (should have start = 16)
+      NullableUuidHolder holder = new NullableUuidHolder();
+      sourceVector.get(1, holder);
+      assertEquals(16, holder.start);
+
+      // Set target vector using holder with non-zero start offset
+      targetVector.setSafe(0, holder);
+      targetVector.setValueCount(1);
+
+      // Verify the value was copied correctly
+      assertEquals(uuid2, targetVector.getObject(0));
+    }
+  }
+
+  @Test
+  void testSetFromNullableHolderWithStartOffset() throws Exception {
+    try (UuidVector sourceVector = new UuidVector("source", allocator);
+        UuidVector targetVector = new UuidVector("target", allocator)) {
+      UUID uuid1 = UUID.randomUUID();
+      UUID uuid2 = UUID.randomUUID();
+
+      sourceVector.setSafe(0, uuid1);
+      sourceVector.setNull(1);
+      sourceVector.setSafe(2, uuid2);
+      sourceVector.setValueCount(3);
+
+      // Get holder from index 2 (should have start = 32)
+      NullableUuidHolder holder = new NullableUuidHolder();
+      sourceVector.get(2, holder);
+      assertEquals(32, holder.start);
+      assertEquals(1, holder.isSet);
+
+      // Set target vector using holder with non-zero start offset
+      targetVector.setSafe(0, holder);
+      targetVector.setValueCount(1);
+
+      // Verify the value was copied correctly
+      assertEquals(uuid2, targetVector.getObject(0));
+
+      // Test with null holder
+      NullableUuidHolder nullHolder = new NullableUuidHolder();
+      sourceVector.get(1, nullHolder);
+      assertEquals(0, nullHolder.isSet);
+
+      targetVector.setSafe(1, nullHolder);
+      targetVector.setValueCount(2);
+      assertTrue(targetVector.isNull(1));
+    }
+  }
+
+  @Test
+  void testGetStartOffset() throws Exception {
+    try (UuidVector vector = new UuidVector("test", allocator)) {
+      vector.allocateNew(10);
+
+      // Test getStartOffset for various indices
+      assertEquals(0, vector.getStartOffset(0));
+      assertEquals(16, vector.getStartOffset(1));
+      assertEquals(32, vector.getStartOffset(2));
+      assertEquals(48, vector.getStartOffset(3));
+      assertEquals(160, vector.getStartOffset(10));
+    }
+  }
+
+  @Test
+  void testReaderWithStartOffsetMultipleReads() throws Exception {
+    try (UuidVector vector = new UuidVector("test", allocator)) {
+      UUID uuid1 = UUID.randomUUID();
+      UUID uuid2 = UUID.randomUUID();
+      UUID uuid3 = UUID.randomUUID();
+
+      vector.setSafe(0, uuid1);
+      vector.setSafe(1, uuid2);
+      vector.setSafe(2, uuid3);
+      vector.setValueCount(3);
+
+      UuidReaderImpl reader = (UuidReaderImpl) vector.getReader();
+      NullableUuidHolder holder = new NullableUuidHolder();
+
+      // Read from different positions and verify start offset
+      reader.read(0, holder);
+      assertEquals(0, holder.start);
+      assertEquals(uuid1, UuidUtility.uuidFromArrowBuf(holder.buffer, 
holder.start));
+
+      reader.read(1, holder);
+      assertEquals(16, holder.start);
+      assertEquals(uuid2, UuidUtility.uuidFromArrowBuf(holder.buffer, 
holder.start));
+
+      reader.read(2, holder);
+      assertEquals(32, holder.start);
+      assertEquals(uuid3, UuidUtility.uuidFromArrowBuf(holder.buffer, 
holder.start));
+    }
+  }
+
+  @Test
+  void testWriterWithExtensionHolder() throws Exception {
+    try (UuidVector sourceVector = new UuidVector("source", allocator);
+        UuidVector targetVector = new UuidVector("target", allocator)) {
+      UUID uuid = UUID.randomUUID();
+      sourceVector.setSafe(0, uuid);
+      sourceVector.setValueCount(1);
+
+      // Get holder from source
+      NullableUuidHolder holder = new NullableUuidHolder();
+      sourceVector.get(0, holder);
+
+      // Write using UuidWriterImpl with ExtensionHolder
+      UuidWriterImpl writer = new UuidWriterImpl(targetVector);
+      writer.setPosition(0);
+      writer.writeExtension(holder);
+
+      assertEquals(uuid, targetVector.getObject(0));
+    }
+  }
+
+  @Test
+  void testNullableUuidHolderReaderImpl() throws Exception {
+    try (UuidVector vector = new UuidVector("test", allocator)) {
+      UUID uuid = UUID.randomUUID();
+      vector.setSafe(0, uuid);
+      vector.setValueCount(1);
+
+      // Get holder from vector
+      NullableUuidHolder sourceHolder = new NullableUuidHolder();
+      vector.get(0, sourceHolder);
+      assertEquals(1, sourceHolder.isSet);
+      assertEquals(0, sourceHolder.start);
+
+      // Create reader from holder
+      NullableUuidHolderReaderImpl reader = new 
NullableUuidHolderReaderImpl(sourceHolder);
+      assertTrue(reader.isSet());
+      assertEquals(uuid, reader.readObject());
+
+      // Read into another holder
+      NullableUuidHolder targetHolder = new NullableUuidHolder();
+      reader.read(targetHolder);
+      assertEquals(1, targetHolder.isSet);
+      assertEquals(0, targetHolder.start);
+      assertEquals(uuid, UuidUtility.uuidFromArrowBuf(targetHolder.buffer, 
targetHolder.start));
+    }
+  }
+
+  @Test
+  void testNullableUuidHolderReaderImplWithNull() throws Exception {
+    try (UuidVector vector = new UuidVector("test", allocator)) {
+      vector.setNull(0);
+      vector.setValueCount(1);
+
+      // Get null holder from vector
+      NullableUuidHolder sourceHolder = new NullableUuidHolder();
+      vector.get(0, sourceHolder);
+      assertEquals(0, sourceHolder.isSet);
+
+      // Create reader from null holder
+      NullableUuidHolderReaderImpl reader = new 
NullableUuidHolderReaderImpl(sourceHolder);
+      assertFalse(reader.isSet());
+      assertNull(reader.readObject());
+
+      // Read into another holder
+      NullableUuidHolder targetHolder = new NullableUuidHolder();
+      reader.read(targetHolder);
+      assertEquals(0, targetHolder.isSet);
+    }
+  }
+
+  @Test
+  void testNullableUuidHolderReaderImplReadIntoUuidHolder() throws Exception {
+    try (UuidVector vector = new UuidVector("test", allocator)) {
+      UUID uuid = UUID.randomUUID();
+      vector.setSafe(0, uuid);
+      vector.setValueCount(1);
+
+      // Get holder from vector
+      NullableUuidHolder sourceHolder = new NullableUuidHolder();
+      vector.get(0, sourceHolder);
+
+      // Create reader from holder
+      NullableUuidHolderReaderImpl reader = new 
NullableUuidHolderReaderImpl(sourceHolder);
+
+      // Read into UuidHolder (non-nullable)
+      UuidHolder targetHolder = new UuidHolder();
+      reader.read(targetHolder);
+      assertEquals(0, targetHolder.start);
+      assertEquals(uuid, UuidUtility.uuidFromArrowBuf(targetHolder.buffer, 
targetHolder.start));
+    }
+  }
+
+  @Test
+  void testNullableUuidHolderReaderImplWithNonZeroStart() throws Exception {
+    try (UuidVector vector = new UuidVector("test", allocator)) {
+      UUID uuid1 = UUID.randomUUID();
+      UUID uuid2 = UUID.randomUUID();
+      vector.setSafe(0, uuid1);
+      vector.setSafe(1, uuid2);
+      vector.setValueCount(2);
+
+      // Get holder from index 1 (start = 16)
+      NullableUuidHolder sourceHolder = new NullableUuidHolder();
+      vector.get(1, sourceHolder);
+      assertEquals(1, sourceHolder.isSet);
+      assertEquals(16, sourceHolder.start);
+
+      // Create reader from holder
+      NullableUuidHolderReaderImpl reader = new 
NullableUuidHolderReaderImpl(sourceHolder);
+      assertEquals(uuid2, reader.readObject());
+
+      // Read into another holder and verify start is preserved
+      NullableUuidHolder targetHolder = new NullableUuidHolder();
+      reader.read(targetHolder);
+      assertEquals(16, targetHolder.start);
+      assertEquals(uuid2, UuidUtility.uuidFromArrowBuf(targetHolder.buffer, 
targetHolder.start));
+    }
+  }
 }
diff --git 
a/vector/src/test/java/org/apache/arrow/vector/complex/writer/TestComplexWriter.java
 
b/vector/src/test/java/org/apache/arrow/vector/complex/writer/TestComplexWriter.java
index b131bf07e..80d03cae6 100644
--- 
a/vector/src/test/java/org/apache/arrow/vector/complex/writer/TestComplexWriter.java
+++ 
b/vector/src/test/java/org/apache/arrow/vector/complex/writer/TestComplexWriter.java
@@ -1169,7 +1169,7 @@ public class TestComplexWriter {
       } else if (i % 5 == 4) {
         NullableUuidHolder holder = new NullableUuidHolder();
         unionReader.read(holder);
-        assertEquals(UuidUtility.uuidFromArrowBuf(holder.buffer, 0), uuid);
+        assertEquals(UuidUtility.uuidFromArrowBuf(holder.buffer, 
holder.start), uuid);
       } else {
         assertEquals((float) i, unionReader.readFloat(), 1e-12);
       }
@@ -2536,7 +2536,7 @@ public class TestComplexWriter {
       {
         FieldReader uuidReader = rootReader.reader("uuid1");
         uuidReader.setPosition(0);
-        UuidHolder uuidHolder = new UuidHolder();
+        NullableUuidHolder uuidHolder = new NullableUuidHolder();
         uuidReader.read(uuidHolder);
         UUID actualUuid = UuidUtility.uuidFromArrowBuf(uuidHolder.buffer, 0);
         assertEquals(u1, actualUuid);

Reply via email to