You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@baremaps.apache.org by bc...@apache.org on 2025/04/05 12:44:34 UTC

(incubator-baremaps) branch main updated: Data collection refactoring (#955)

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

bchapuis pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-baremaps.git


The following commit(s) were added to refs/heads/main by this push:
     new dc8ff321e Data collection refactoring (#955)
dc8ff321e is described below

commit dc8ff321e9419222e4e0239681bddb1c42f62615
Author: Bertil Chapuis <bc...@gmail.com>
AuthorDate: Sat Apr 5 14:44:30 2025 +0200

    Data collection refactoring (#955)
    
    * Improve resource management
    
    * Persist size to memory in AppendOnlyLog
    
    * Make DataCollection extend AutoCloseable
    
    * Make DataMap extend AutoCloseable
    
    * Add DirectHashDataMap
    
    * Add builders to all data collections
    
    * Make data collections constructors private
    
    * Use the builders consistently in calling code
    
    * Improve the javadoc and make it more consistent
    
    * Clean unused files
---
 .../apache/baremaps/workflow/WorkflowContext.java  |  47 +-
 .../org/apache/baremaps/calcite/CalciteTest.java   |  12 +-
 .../baremaps/tasks/ImportUpdateSampleTest.java     |  14 +-
 .../baremaps/data/collection/AppendOnlyLog.java    | 129 +++++-
 .../baremaps/data/collection/DataCollection.java   |  51 ++-
 .../data/collection/DataCollectionException.java   |  22 +-
 .../data/collection/DataCollectionMapper.java      |  16 +-
 .../baremaps/data/collection/DataConversions.java  |  69 ++-
 .../apache/baremaps/data/collection/DataList.java  |  21 +-
 .../apache/baremaps/data/collection/DataMap.java   |  76 ++--
 .../data/collection/DirectHashDataMap.java         | 496 +++++++++++++++++++++
 .../data/collection/FixedSizeDataList.java         |  87 +++-
 .../baremaps/data/collection/IndexedDataList.java  |  82 +++-
 .../baremaps/data/collection/IndexedDataMap.java   |  79 +++-
 .../data/collection/MemoryAlignedDataList.java     |  90 +++-
 .../data/collection/MemoryAlignedDataMap.java      |  83 +++-
 .../baremaps/data/collection/MonotonicDataMap.java | 112 +++--
 .../data/collection/MonotonicFixedSizeDataMap.java |  99 +++-
 .../data/collection/MonotonicPairedDataMap.java    |  80 +++-
 .../org/apache/baremaps/data/memory/Memory.java    | 114 +++--
 .../baremaps/data/memory/MemoryException.java      |  18 +-
 .../data/memory/MemoryMappedDirectory.java         |  44 +-
 .../baremaps/data/memory/MemoryMappedFile.java     |  42 +-
 .../apache/baremaps/data/memory/OffHeapMemory.java |  32 +-
 .../apache/baremaps/data/memory/OnHeapMemory.java  |  31 +-
 .../baremaps/data/type/BooleanArrayDataType.java   |   4 +-
 .../apache/baremaps/data/type/BooleanDataType.java |   8 +-
 .../baremaps/data/type/BooleanListDataType.java    |   4 +-
 .../baremaps/data/type/ByteArrayDataType.java      |   2 +-
 .../apache/baremaps/data/type/ByteDataType.java    |  10 +-
 .../baremaps/data/type/ByteListDataType.java       |   2 +-
 .../data/type/CoordinateArrayDataType.java         |   3 +-
 .../baremaps/data/type/CoordinateDataType.java     |   6 +-
 .../org/apache/baremaps/data/type/DataType.java    |  24 +-
 .../baremaps/data/type/DoubleArrayDataType.java    |   6 +-
 .../apache/baremaps/data/type/DoubleDataType.java  |  10 +-
 .../baremaps/data/type/DoubleListDataType.java     |   6 +-
 .../baremaps/data/type/FixedSizeDataType.java      |   8 +-
 .../baremaps/data/type/FloatArrayDataType.java     |   6 +-
 .../apache/baremaps/data/type/FloatDataType.java   |   6 +-
 .../baremaps/data/type/FloatListDataType.java      |   6 +-
 .../data/type/GeometryCollectionDataType.java      |  11 +-
 .../baremaps/data/type/GeometryDataType.java       |   8 +-
 .../baremaps/data/type/IntegerArrayDataType.java   |   7 +-
 .../apache/baremaps/data/type/IntegerDataType.java |   6 +-
 .../baremaps/data/type/IntegerListDataType.java    |   7 +-
 .../baremaps/data/type/LineStringDataType.java     |   6 +-
 .../apache/baremaps/data/type/ListDataType.java    |   9 +-
 .../apache/baremaps/data/type/LonLatDataType.java  |  23 +-
 .../baremaps/data/type/LongArrayDataType.java      |   6 +-
 .../apache/baremaps/data/type/LongDataType.java    |   7 +-
 .../baremaps/data/type/LongListDataType.java       |   6 +-
 .../org/apache/baremaps/data/type/MapDataType.java |  16 +
 .../baremaps/data/type/MemoryAlignedDataType.java  |   7 +-
 .../data/type/MultiLineStringDataType.java         |   7 +-
 .../baremaps/data/type/MultiPointDataType.java     |   6 +-
 .../baremaps/data/type/MultiPolygonDataType.java   |   6 +-
 .../baremaps/data/type/NullableDataType.java       |  10 +-
 .../apache/baremaps/data/type/PairDataType.java    |  35 +-
 .../apache/baremaps/data/type/PointDataType.java   |   6 +-
 .../apache/baremaps/data/type/PolygonDataType.java |   6 +-
 .../org/apache/baremaps/data/type/RowDataType.java |  14 +-
 .../baremaps/data/type/ShortArrayDataType.java     |   4 +-
 .../apache/baremaps/data/type/ShortDataType.java   |   8 +-
 .../baremaps/data/type/ShortListDataType.java      |   6 +-
 .../baremaps/data/type/SmallIntegerDataType.java   |  12 +-
 .../baremaps/data/type/SmallLongDataType.java      |  12 +-
 .../apache/baremaps/data/type/StringDataType.java  |   2 +-
 .../org/apache/baremaps/data/type/WKBDataType.java |  24 +-
 .../apache/baremaps/data/AppendOnlyLogTest.java    |  15 +-
 .../org/apache/baremaps/data/DataListTest.java     |  18 +-
 .../java/org/apache/baremaps/data/DataMapTest.java |  45 +-
 .../apache/baremaps/data/SizedDataListTest.java    |  10 +-
 .../data/collection/IndexedDataMapTest.java        |   6 +-
 .../data/collection/MemoryAlignedDataMapTest.java  |   5 +-
 .../baremaps/data/sort/BinarySearchTest.java       |   4 +-
 .../baremaps/data/sort/ExternalMergeSortTest.java  |  13 +-
 77 files changed, 1949 insertions(+), 501 deletions(-)

diff --git a/baremaps-core/src/main/java/org/apache/baremaps/workflow/WorkflowContext.java b/baremaps-core/src/main/java/org/apache/baremaps/workflow/WorkflowContext.java
index bf9c03c10..f940edc3d 100644
--- a/baremaps-core/src/main/java/org/apache/baremaps/workflow/WorkflowContext.java
+++ b/baremaps-core/src/main/java/org/apache/baremaps/workflow/WorkflowContext.java
@@ -73,34 +73,43 @@ public class WorkflowContext {
 
   private <T> DataMap<Long, T> getMemoryAlignedDataMap(String name, FixedSizeDataType<T> dataType)
       throws IOException {
-    var coordinateDir = Files.createDirectories(cacheDir.resolve(name));
-    return new MemoryAlignedDataMap<>(
-        dataType,
-        new MemoryMappedDirectory(coordinateDir));
+    Path coordinateDir = Files.createDirectories(cacheDir.resolve(name));
+    return MemoryAlignedDataMap.<T>builder()
+        .dataType(dataType)
+        .memory(new MemoryMappedDirectory(coordinateDir))
+        .build();
   }
 
   private <T> DataMap<Long, T> getMonotonicDataMap(String name, DataType<T> dataType)
       throws IOException {
-    var mapDir = Files.createDirectories(cacheDir.resolve(name));
-    var keysDir = Files.createDirectories(mapDir.resolve("keys"));
-    var valuesDir = Files.createDirectories(mapDir.resolve("values"));
-    return new MonotonicDataMap<>(
-        new MemoryAlignedDataList<>(
-            new PairDataType<>(new LongDataType(), new LongDataType()),
-            new MemoryMappedDirectory(keysDir)),
-        new AppendOnlyLog<>(
-            dataType,
-            new MemoryMappedDirectory(valuesDir)));
+    Path mapDir = Files.createDirectories(cacheDir.resolve(name));
+    Path keysDir = Files.createDirectories(mapDir.resolve("keys"));
+    Path valuesDir = Files.createDirectories(mapDir.resolve("values"));
+    MemoryAlignedDataList<PairDataType.Pair<Long, Long>> keys =
+        MemoryAlignedDataList.<PairDataType.Pair<Long, Long>>builder()
+            .dataType(new PairDataType<>(new LongDataType(), new LongDataType()))
+            .memory(new MemoryMappedDirectory(keysDir))
+            .build();
+    AppendOnlyLog<T> values = AppendOnlyLog.<T>builder()
+        .dataType(dataType)
+        .values(new MemoryMappedDirectory(valuesDir))
+        .build();
+    return MonotonicDataMap.<T>builder()
+        .keys(keys)
+        .values(values)
+        .build();
   }
 
   private DataMap<Long, Coordinate> getMonotonicPairedDataMap(String name,
       DataType<Coordinate> dataType)
       throws IOException {
-    var mapDir = Files.createDirectories(cacheDir.resolve(name));
-    return new MonotonicPairedDataMap<>(
-        new MemoryAlignedDataList<>(
-            new PairDataType<>(new LongDataType(), new LonLatDataType()),
-            new MemoryMappedDirectory(Files.createDirectories(mapDir))));
+    Path mapDir = Files.createDirectories(cacheDir.resolve(name));
+    return MonotonicPairedDataMap.<Coordinate>builder()
+        .values(MemoryAlignedDataList.<PairDataType.Pair<Long, Coordinate>>builder()
+            .dataType(new PairDataType<>(new LongDataType(), new LonLatDataType()))
+            .memory(new MemoryMappedDirectory(Files.createDirectories(mapDir)))
+            .build())
+        .build();
   }
 
   public void cleanCache() throws IOException {
diff --git a/baremaps-core/src/test/java/org/apache/baremaps/calcite/CalciteTest.java b/baremaps-core/src/test/java/org/apache/baremaps/calcite/CalciteTest.java
index efc13eb4d..482d15885 100644
--- a/baremaps-core/src/test/java/org/apache/baremaps/calcite/CalciteTest.java
+++ b/baremaps-core/src/test/java/org/apache/baremaps/calcite/CalciteTest.java
@@ -80,7 +80,11 @@ class CalciteTest {
           new DataColumnFixed("geometry", Cardinality.OPTIONAL, Type.GEOMETRY)));
       DataTable cityDataTable = new BaremapsDataTable(
           cityRowType,
-          new IndexedDataList<>(new AppendOnlyLog<>(new RowDataType(cityRowType))));
+          IndexedDataList.<DataRow>builder()
+              .values(AppendOnlyLog.<DataRow>builder()
+                  .dataType(new RowDataType(cityRowType))
+                  .build())
+              .build());
       cityDataTable.add(new DataRowImpl(cityDataTable.schema(),
           List.of(1, "Paris", geometryFactory.createPoint(new Coordinate(2.3522, 48.8566)))));
       cityDataTable.add(new DataRowImpl(cityDataTable.schema(),
@@ -94,7 +98,11 @@ class CalciteTest {
           new DataColumnFixed("population", Cardinality.OPTIONAL, Type.INTEGER)));
       DataTable populationDataTable = new BaremapsDataTable(
           populationRowType,
-          new IndexedDataList<>(new AppendOnlyLog<>(new RowDataType(populationRowType))));
+          IndexedDataList.<DataRow>builder()
+              .values(AppendOnlyLog.<DataRow>builder()
+                  .dataType(new RowDataType(populationRowType))
+                  .build())
+              .build());
       populationDataTable
           .add(new DataRowImpl(populationDataTable.schema(), List.of(1, 2_161_000)));
       populationDataTable
diff --git a/baremaps-core/src/test/java/org/apache/baremaps/tasks/ImportUpdateSampleTest.java b/baremaps-core/src/test/java/org/apache/baremaps/tasks/ImportUpdateSampleTest.java
index 4aa327f47..1926f4059 100644
--- a/baremaps-core/src/test/java/org/apache/baremaps/tasks/ImportUpdateSampleTest.java
+++ b/baremaps-core/src/test/java/org/apache/baremaps/tasks/ImportUpdateSampleTest.java
@@ -55,9 +55,19 @@ class ImportUpdateSampleTest extends PostgresRepositoryTest {
 
     // Initialize the data maps
     Map<Long, Coordinate> coordinateMap = DataConversions.asMap(
-        new IndexedDataMap<>(new AppendOnlyLog<>(new CoordinateDataType(), new OnHeapMemory())));
+        IndexedDataMap.<Coordinate>builder()
+            .values(AppendOnlyLog.<Coordinate>builder()
+                .dataType(new CoordinateDataType())
+                .memory(new OnHeapMemory())
+                .build())
+            .build());
     Map<Long, List<Long>> referenceMap = DataConversions.asMap(
-        new IndexedDataMap<>(new AppendOnlyLog<>(new LongListDataType(), new OnHeapMemory())));
+        IndexedDataMap.<List<Long>>builder()
+            .values(AppendOnlyLog.<List<Long>>builder()
+                .dataType(new LongListDataType())
+                .memory(new OnHeapMemory())
+                .build())
+            .build());
 
     // Import the sample data
     ImportOsmPbf.execute(TestFiles.SAMPLE_OSM_PBF, coordinateMap, referenceMap, headerRepository,
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/AppendOnlyLog.java b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/AppendOnlyLog.java
index fbb549988..c5e9adbdc 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/AppendOnlyLog.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/AppendOnlyLog.java
@@ -30,11 +30,10 @@ import org.apache.baremaps.data.memory.OffHeapMemory;
 import org.apache.baremaps.data.type.DataType;
 
 /**
- * A log of records backed by a {@link DataType} and a {@link Memory}. Elements are appended to the
- * log and can be accessed by their position in the {@link Memory}. Appending elements to the log is
- * thread-safe.
+ * A log of elements that can only be appended to. Elements are stored in memory and can be accessed
+ * by their position. Append operations are thread-safe.
  *
- * @param <E> The type of the data.
+ * @param <E> The type of elements in the log
  */
 public class AppendOnlyLog<E> implements DataCollection<E> {
 
@@ -47,21 +46,84 @@ public class AppendOnlyLog<E> implements DataCollection<E> {
   private final Lock lock = new ReentrantLock();
 
   /**
-   * Constructs an {@link AppendOnlyLog}.
+   * Creates a new builder for an AppendOnlyLog.
    *
-   * @param dataType the data type
+   * @param <E> the type of elements
+   * @return a new builder
    */
-  public AppendOnlyLog(DataType<E> dataType) {
-    this(dataType, new OffHeapMemory());
+  public static <E> Builder<E> builder() {
+    return new Builder<>();
   }
 
   /**
-   * Constructs an append only log.
+   * Builder for AppendOnlyLog.
+   *
+   * @param <E> the type of elements
+   */
+  public static class Builder<E> {
+    private DataType<E> dataType;
+    private Memory<?> memory;
+
+    /**
+     * Sets the data type for the log.
+     *
+     * @param dataType the data type
+     * @return this builder
+     */
+    public Builder<E> dataType(DataType<?> dataType) {
+      @SuppressWarnings("unchecked")
+      DataType<E> castedDataType = (DataType<E>) dataType;
+      this.dataType = castedDataType;
+      return this;
+    }
+
+    /**
+     * Sets the memory for the log.
+     *
+     * @param memory the memory
+     * @return this builder
+     */
+    public Builder<E> memory(Memory<?> memory) {
+      this.memory = memory;
+      return this;
+    }
+
+    /**
+     * Sets the memory for the log values.
+     *
+     * @param memory the memory
+     * @return this builder
+     */
+    public Builder<E> values(Memory<?> memory) {
+      return memory(memory);
+    }
+
+    /**
+     * Builds a new AppendOnlyLog.
+     *
+     * @return a new AppendOnlyLog
+     * @throws IllegalStateException if required parameters are missing
+     */
+    public AppendOnlyLog<E> build() {
+      if (dataType == null) {
+        throw new IllegalStateException("Data type must be specified");
+      }
+
+      if (memory == null) {
+        memory = new OffHeapMemory();
+      }
+
+      return new AppendOnlyLog<>(dataType, memory);
+    }
+  }
+
+  /**
+   * Constructs an AppendOnlyLog.
    *
    * @param dataType the data type
    * @param memory the memory
    */
-  public AppendOnlyLog(DataType<E> dataType, Memory<?> memory) {
+  private AppendOnlyLog(DataType<E> dataType, Memory<?> memory) {
     this.dataType = dataType;
     this.memory = memory;
     this.segmentSize = memory.segmentSize();
@@ -70,10 +132,18 @@ public class AppendOnlyLog<E> implements DataCollection<E> {
   }
 
   /**
-   * Appends the value to the log and returns its position in the memory.
+   * Persists the current size to memory.
+   */
+  public void persistSize() {
+    memory.segment(0).putLong(0, size);
+  }
+
+  /**
+   * Appends an element to the log and returns its position in memory.
    *
-   * @param value the value
-   * @return the position of the value in the memory.
+   * @param value the element to add
+   * @return the position of the element in memory
+   * @throws DataCollectionException if the element is too large for a segment
    */
   public long addPositioned(E value) {
     int valueSize = dataType.size(value);
@@ -102,10 +172,10 @@ public class AppendOnlyLog<E> implements DataCollection<E> {
   }
 
   /**
-   * Returns a values at the specified position in the memory.
+   * Returns the element at the specified position in memory.
    *
-   * @param position the position of the value
-   * @return the value
+   * @param position the position of the element
+   * @return the element
    */
   public E getPositioned(long position) {
     long segmentIndex = position / segmentSize;
@@ -130,32 +200,39 @@ public class AppendOnlyLog<E> implements DataCollection<E> {
   public void clear() {
     try {
       memory.clear();
+      this.size = 0;
+      persistSize();
     } catch (IOException e) {
       throw new DataCollectionException(e);
     }
   }
 
   /**
-   * Returns an iterator over the values of the log, starting at the beginning of the log. The
-   * iterator allows to get the current position in the memory.
+   * Returns an iterator over the elements of this log.
    * 
-   * @return an iterator over the values of the log
+   * @return an iterator over the elements
    */
   @Override
   public AppendOnlyLogIterator iterator() {
     return new AppendOnlyLogIterator(size);
   }
 
+  @Override
+  public void close() throws Exception {
+    try {
+      memory.close();
+    } catch (IOException e) {
+      throw new DataCollectionException(e);
+    }
+  }
+
   /**
-   * An iterator over the values of the log that can be used to iterate over the values of the log
-   * and to get the current position in the memory.
+   * An iterator over the elements in this log.
    */
   public class AppendOnlyLogIterator implements Iterator<E> {
 
     private final long size;
-
     private long index;
-
     private long position;
 
     private AppendOnlyLogIterator(long size) {
@@ -199,9 +276,13 @@ public class AppendOnlyLog<E> implements DataCollection<E> {
       return dataType.read(segment, (int) segmentOffset);
     }
 
+    /**
+     * Returns the current position in memory.
+     *
+     * @return the current position
+     */
     public long getPosition() {
       return position;
     }
-
   }
 }
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/DataCollection.java b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/DataCollection.java
index 1189cd846..5c5f549f6 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/DataCollection.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/DataCollection.java
@@ -22,29 +22,31 @@ import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
 
 /**
- * A {@code DataCollection<E>} is a group of elements that can be iterated over. It is similar to a
- * {@link java.util.Collection<E> Collection}, but can hold up to {@link Long#MAX_VALUE} elements.
+ * A collection of elements that can be iterated over. Similar to {@link java.util.Collection} but
+ * supports up to {@link Long#MAX_VALUE} elements.
+ *
+ * @param <E> The type of elements in the collection
  */
-public interface DataCollection<E> extends Iterable<E> {
+public interface DataCollection<E> extends Iterable<E>, AutoCloseable {
 
   /**
-   * Returns the number of values stored in the data collection.
+   * Returns the number of elements in this collection.
    *
-   * @return the number of values
+   * @return the number of elements
    */
   long size();
 
   /**
-   * Returns true if the data collection is empty.
+   * Returns true if this collection contains no elements.
    *
-   * @return true if the data collection is empty
+   * @return true if this collection is empty
    */
   default boolean isEmpty() {
     return size() == 0;
   }
 
   /**
-   * Returns an iterator over the elements in the data collection.
+   * Returns an iterator over the elements in this collection.
    *
    * @return an iterator
    */
@@ -52,7 +54,7 @@ public interface DataCollection<E> extends Iterable<E> {
   Iterator<E> iterator();
 
   /**
-   * Returns a spliterator over the elements in the data collection.
+   * Returns a spliterator over the elements in this collection.
    *
    * @return a spliterator
    */
@@ -62,16 +64,16 @@ public interface DataCollection<E> extends Iterable<E> {
   }
 
   /**
-   * Returns a stream over the elements in the data collection.
+   * Returns a sequential stream over the elements in this collection.
    *
-   * @return a stream
+   * @return a sequential stream
    */
   default Stream<E> stream() {
     return StreamSupport.stream(spliterator(), false);
   }
 
   /**
-   * Returns a parallel stream over the elements in the data collection.
+   * Returns a parallel stream over the elements in this collection.
    *
    * @return a parallel stream
    */
@@ -80,20 +82,20 @@ public interface DataCollection<E> extends Iterable<E> {
   }
 
   /**
-   * Adds a value to the data collection.
+   * Adds an element to this collection.
    *
-   * @param e the value to add
-   * @return true if the data collection has been modified
+   * @param e the element to add
+   * @return true if the collection changed as a result of the call
    */
   default boolean add(E e) {
     throw new UnsupportedOperationException();
   }
 
   /**
-   * Adds all the values in the specified collection to the data collection.
+   * Adds all elements from the specified collection to this collection.
    *
-   * @param c the collection of values to add
-   * @return true if the data collection has been modified
+   * @param c the collection of elements to add
+   * @return true if this collection changed as a result of the call
    */
   default boolean addAll(Iterable<? extends E> c) {
     boolean modified = false;
@@ -106,9 +108,10 @@ public interface DataCollection<E> extends Iterable<E> {
   }
 
   /**
-   * Returns true if the data collection contains the specified value.
+   * Returns true if this collection contains the specified element.
    *
-   * @param o the value to search for
+   * @param o the element to check for
+   * @return true if this collection contains the element
    */
   default boolean contains(Object o) {
     for (E e : this) {
@@ -120,10 +123,10 @@ public interface DataCollection<E> extends Iterable<E> {
   }
 
   /**
-   * Returns true if the data collection contains all the values in the specified collection.
+   * Returns true if this collection contains all elements in the specified collection.
    *
-   * @param c the collection of values to search for
-   * @return true if the data collection contains all the values
+   * @param c the collection to check against
+   * @return true if this collection contains all elements in the specified collection
    */
   default boolean containsAll(Iterable<?> c) {
     for (Object o : c) {
@@ -135,7 +138,7 @@ public interface DataCollection<E> extends Iterable<E> {
   }
 
   /**
-   * Removes all the values from the data collection.
+   * Removes all elements from this collection.
    */
   void clear();
 
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/DataCollectionException.java b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/DataCollectionException.java
index a4300ccf8..7720b2023 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/DataCollectionException.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/DataCollectionException.java
@@ -17,35 +17,39 @@
 
 package org.apache.baremaps.data.collection;
 
-/** Signals that an exception occurred in a store. */
+/**
+ * Exception thrown when an error occurs in a data collection operation.
+ */
 public class DataCollectionException extends RuntimeException {
 
-  /** Constructs a {@code DataCollectionException} with {@code null} as its error detail message. */
+  /**
+   * Constructs a new exception with null as its detail message.
+   */
   public DataCollectionException() {}
 
   /**
-   * Constructs an {@code DataCollectionException} with the specified detail message.
+   * Constructs a new exception with the specified detail message.
    *
-   * @param message the message
+   * @param message the detail message
    */
   public DataCollectionException(String message) {
     super(message);
   }
 
   /**
-   * Constructs a {@code DataCollectionException} with the specified cause.
+   * Constructs a new exception with the specified cause.
    *
-   * @param cause the cause
+   * @param cause the cause of this exception
    */
   public DataCollectionException(Throwable cause) {
     super(cause);
   }
 
   /**
-   * Constructs a {@code DataCollectionException} with the specified detail message and cause.
+   * Constructs a new exception with the specified detail message and cause.
    *
-   * @param message the message
-   * @param cause the cause
+   * @param message the detail message
+   * @param cause the cause of this exception
    */
   public DataCollectionException(String message, Throwable cause) {
     super(message, cause);
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/DataCollectionMapper.java b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/DataCollectionMapper.java
index 970407919..9ab9101c4 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/DataCollectionMapper.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/DataCollectionMapper.java
@@ -22,7 +22,10 @@ import java.util.Iterator;
 import java.util.function.Function;
 
 /**
- * A decorator for a table that transforms the geometries of the rows.
+ * A decorator that transforms elements of a data collection using a function.
+ *
+ * @param <S> The source element type
+ * @param <T> The transformed element type
  */
 public class DataCollectionMapper<S, T> implements DataCollection<T> {
 
@@ -31,10 +34,10 @@ public class DataCollectionMapper<S, T> implements DataCollection<T> {
   private final Function<S, T> mapper;
 
   /**
-   * Constructs a new table decorator.
+   * Constructs a new mapper for a data collection.
    *
-   * @param collection the table to decorate
-   * @param mapper the row transformer
+   * @param collection the source collection
+   * @param mapper the function to transform elements
    */
   public DataCollectionMapper(DataCollection<S> collection, Function<S, T> mapper) {
     this.collection = collection;
@@ -58,4 +61,9 @@ public class DataCollectionMapper<S, T> implements DataCollection<T> {
   public void clear() {
     collection.clear();
   }
+
+  @Override
+  public void close() throws Exception {
+    collection.close();
+  }
 }
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/DataConversions.java b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/DataConversions.java
index 593f6acd4..8f1957c16 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/DataConversions.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/DataConversions.java
@@ -21,7 +21,7 @@ import java.util.*;
 import java.util.Map.Entry;
 
 /**
- * Utility class for converting between collections and data collections.
+ * Utility class for converting between standard Java collections and data collections.
  */
 public class DataConversions {
 
@@ -30,11 +30,11 @@ public class DataConversions {
   }
 
   /**
-   * Converts a {@code DataCollection} to a {@code Collection}.
+   * Converts a DataCollection to a Collection.
    * 
-   * @param dataCollection
-   * @return
-   * @param <E>
+   * @param dataCollection the data collection to convert
+   * @return a Collection view of the data collection
+   * @param <E> the type of elements
    */
   public static <E> Collection<E> asCollection(DataCollection<E> dataCollection) {
     if (dataCollection instanceof DataCollectionAdapter<E>adapter) {
@@ -45,11 +45,11 @@ public class DataConversions {
   }
 
   /**
-   * Converts a {@code Collection} to a {@code DataCollection}.
+   * Converts a Collection to a DataCollection.
    *
-   * @param collection
-   * @return
-   * @param <E>
+   * @param collection the collection to convert
+   * @return a DataCollection view of the collection
+   * @param <E> the type of elements
    */
   public static <E> DataCollection<E> asDataCollection(Collection<E> collection) {
     if (collection instanceof CollectionAdapter<E>adapter) {
@@ -60,10 +60,11 @@ public class DataConversions {
   }
 
   /**
-   * Converts a {@code DataList} to a {@code List}.
+   * Converts a DataList to a List.
    *
-   * @param dataList the data list
-   * @return the list
+   * @param dataList the data list to convert
+   * @return a List view of the data list
+   * @param <E> the type of elements
    */
   public static <E> List<E> asList(DataList<E> dataList) {
     if (dataList instanceof DataListAdapter<E>adapter) {
@@ -74,10 +75,11 @@ public class DataConversions {
   }
 
   /**
-   * Converts a {@code List} to a {@code DataList}.
+   * Converts a List to a DataList.
    *
-   * @param list the list
-   * @return the data list
+   * @param list the list to convert
+   * @return a DataList view of the list
+   * @param <E> the type of elements
    */
   public static <E> DataList<E> asDataList(List<E> list) {
     if (list instanceof ListAdapter<E>adapter) {
@@ -88,10 +90,12 @@ public class DataConversions {
   }
 
   /**
-   * Converts a {@code DataMap} to a {@code Map}.
+   * Converts a DataMap to a Map.
    *
-   * @param dataMap the data map
-   * @return the map
+   * @param dataMap the data map to convert
+   * @return a Map view of the data map
+   * @param <K> the type of keys
+   * @param <V> the type of values
    */
   public static <K, V> Map<K, V> asMap(DataMap<K, V> dataMap) {
     if (dataMap instanceof DataMapAdapter<K, V>adapter) {
@@ -102,10 +106,12 @@ public class DataConversions {
   }
 
   /**
-   * Converts a {@code Map} to a {@code DataMap}.
+   * Converts a Map to a DataMap.
    *
-   * @param map the map
-   * @return the data map
+   * @param map the map to convert
+   * @return a DataMap view of the map
+   * @param <K> the type of keys
+   * @param <V> the type of values
    */
   public static <K, V> DataMap<K, V> asDataMap(Map<K, V> map) {
     if (map instanceof MapAdapter<K, V>adapter) {
@@ -163,6 +169,13 @@ public class DataConversions {
     public Iterator<E> iterator() {
       return collection.iterator();
     }
+
+    @Override
+    public void close() throws Exception {
+      if (collection instanceof AutoCloseable) {
+        ((AutoCloseable) collection).close();
+      }
+    }
   }
 
   private static class ListAdapter<E> extends AbstractList<E> {
@@ -251,6 +264,13 @@ public class DataConversions {
     public int hashCode() {
       return list.hashCode();
     }
+
+    @Override
+    public void close() throws Exception {
+      if (list instanceof AutoCloseable) {
+        ((AutoCloseable) list).close();
+      }
+    }
   }
 
   private static class MapAdapter<K, V> extends AbstractMap<K, V> {
@@ -361,6 +381,13 @@ public class DataConversions {
     public int hashCode() {
       return map.hashCode();
     }
+
+    @Override
+    public void close() throws Exception {
+      if (map instanceof AutoCloseable) {
+        ((AutoCloseable) map).close();
+      }
+    }
   }
 
 }
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/DataList.java b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/DataList.java
index de711d101..39c6c95c6 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/DataList.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/DataList.java
@@ -23,19 +23,18 @@ import java.util.Iterator;
 import java.util.NoSuchElementException;
 
 /**
- * A {@code DataList<E>} is a sequence of elements that can be iterated over and accessed by index.
- * It is similar to a {@link java.util.List<E> List}, but can hold up to {@link Long#MAX_VALUE}
- * elements.
+ * A sequence of elements that can be accessed by index. Similar to {@link java.util.List} but
+ * supports up to {@link Long#MAX_VALUE} elements.
  *
- * @param <E> The type of the elements.
+ * @param <E> The type of elements in the list
  */
 public interface DataList<E> extends DataCollection<E> {
 
   /**
-   * Adds a value to the data list and returns its index.
+   * Adds an element to this list and returns its index.
    *
-   * @param value the value
-   * @return the index of the value.
+   * @param value the element to add
+   * @return the index of the added element
    */
   long addIndexed(E value);
 
@@ -47,18 +46,18 @@ public interface DataList<E> extends DataCollection<E> {
   }
 
   /**
-   * Sets the value at the specified index
+   * Sets the element at the specified index.
    *
    * @param index the index
-   * @param value the value
+   * @param value the element to set
    */
   void set(long index, E value);
 
   /**
-   * Returns the value at the specified index.
+   * Returns the element at the specified index.
    *
    * @param index the index
-   * @return the value
+   * @return the element at the specified index
    */
   E get(long index);
 
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/DataMap.java b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/DataMap.java
index 1a9095246..ecd4738d6 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/DataMap.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/DataMap.java
@@ -25,34 +25,34 @@ import java.util.Map.Entry;
 import java.util.function.BiConsumer;
 
 /**
- * A {@code DataMap<E>} maps keys to values. It is similar to a {@link java.util.Map<K, V> Map}, but
- * can hold up to {@link Long#MAX_VALUE} entries.
+ * A map that associates keys with values. Similar to {@link java.util.Map} but supports up to
+ * {@link Long#MAX_VALUE} entries.
  *
- * @param <K> The type of the keys.
- * @param <V> The type of the values.
+ * @param <K> The type of keys in the map
+ * @param <V> The type of values in the map
  */
-public interface DataMap<K, V> {
+public interface DataMap<K, V> extends AutoCloseable {
 
   /**
-   * Returns the number of values stored in the data map.
+   * Returns the number of key-value mappings in this map.
    *
-   * @return the number of values
+   * @return the number of key-value mappings
    */
   long size();
 
   /**
-   * Returns the value associated with the specified key or null if the key is not present.
+   * Returns the value associated with the specified key, or null if no mapping exists.
    *
    * @param key the key
-   * @return the value
+   * @return the value associated with the key, or null if no mapping exists
    */
   V get(Object key);
 
   /**
-   * Returns the values associated with the specified keys or null if the key is not present.
+   * Returns the values associated with the specified keys, or null for keys with no mapping.
    *
    * @param keys the keys
-   * @return the values
+   * @return the values associated with the keys
    */
   default Iterable<V> getAll(Iterable<K> keys) {
     List<V> values = new ArrayList<>();
@@ -61,105 +61,105 @@ public interface DataMap<K, V> {
   }
 
   /**
-   * Associates the specified value with the specified key in the data map.
+   * Associates the specified value with the specified key in this map.
    *
    * @param key the key
    * @param value the value
-   * @return the previous value associated with the key, or null if there was no mapping for the key
+   * @return the previous value associated with the key, or null if there was no mapping
    */
   V put(K key, V value);
 
   /**
-   * Associates the specified values with the specified keys in the data map.
+   * Adds all entries from the specified iterable to this map.
    *
-   * @param entries the entries
+   * @param entries the entries to add
    */
   default void putAll(Iterable<Entry<K, V>> entries) {
     entries.forEach(entry -> put(entry.getKey(), entry.getValue()));
   }
 
   /**
-   * Returns true if the data map contains a mapping for the specified key.
+   * Returns true if this map contains a mapping for the specified key.
    *
-   * @param key the key
-   * @return true if the data map contains a mapping for the key
+   * @param key the key to check
+   * @return true if this map contains a mapping for the key
    */
   boolean containsKey(Object key);
 
   /**
-   * Returns true if the data map contains a mapping for the specified value.
+   * Returns true if this map maps one or more keys to the specified value.
    *
-   * @param value the value
-   * @return true if the data map contains a mapping for the value
+   * @param value the value to check
+   * @return true if this map maps one or more keys to the value
    */
   boolean containsValue(V value);
 
   /**
-   * Clears the data map.
+   * Removes all mappings from this map.
    */
   void clear();
 
   /**
-   * Returns true if the data map contains no elements.
+   * Returns true if this map contains no mappings.
    * 
-   * @return true if the data map contains no elements
+   * @return true if this map contains no mappings
    */
   default boolean isEmpty() {
     return size() == 0;
   }
 
   /**
-   * Returns an iterator over the keys of the data map.
+   * Returns an iterator over the keys in this map.
    *
-   * @return an iterator
+   * @return an iterator over the keys
    */
   Iterator<K> keyIterator();
 
   /**
-   * Returns an iterable over the keys of the data map.
+   * Returns an iterable over the keys in this map.
    *
-   * @return an iterable
+   * @return an iterable over the keys
    */
   default Iterable<K> keys() {
     return this::keyIterator;
   }
 
   /**
-   * Returns an iterator over the values of the data map.
+   * Returns an iterator over the values in this map.
    *
-   * @return an iterator
+   * @return an iterator over the values
    */
   Iterator<V> valueIterator();
 
   /**
-   * Returns an iterable over the values of the data map.
+   * Returns an iterable over the values in this map.
    *
-   * @return an iterable
+   * @return an iterable over the values
    */
   default Iterable<V> values() {
     return this::valueIterator;
   }
 
   /**
-   * Returns an iterator over the entries of the data map.
+   * Returns an iterator over the entries in this map.
    *
-   * @return an iterator
+   * @return an iterator over the entries
    */
   Iterator<Entry<K, V>> entryIterator();
 
   /**
-   * Returns an iterable over the entries of the data map.
+   * Returns an iterable over the entries in this map.
    *
-   * @return an iterable
+   * @return an iterable over the entries
    */
   default Iterable<Entry<K, V>> entries() {
     return this::entryIterator;
   }
 
   /**
-   * Performs the given action for each entry in the data map.
+   * Performs the given action for each entry in this map.
    *
-   * @param action the action to be performed
+   * @param action the action to perform on each entry
    */
   default void forEach(BiConsumer<? super K, ? super V> action) {
     for (Entry<K, V> entry : entries()) {
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/DirectHashDataMap.java b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/DirectHashDataMap.java
new file mode 100644
index 000000000..2fb63e086
--- /dev/null
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/DirectHashDataMap.java
@@ -0,0 +1,496 @@
+/*
+ * 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.baremaps.data.collection;
+
+import java.nio.ByteBuffer;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import org.apache.baremaps.data.memory.Memory;
+import org.apache.baremaps.data.type.FixedSizeDataType;
+
+/**
+ * A map that uses direct hashing with open addressing and linear probing. Provides O(1) access time
+ * for both get and put operations with a fixed capacity.
+ * 
+ * @param <V> the type of values in the map
+ */
+public class DirectHashDataMap<V> implements DataMap<Long, V> {
+
+  private static final long EMPTY_KEY = Long.MIN_VALUE;
+  private static final long TOMBSTONE = Long.MIN_VALUE + 1;
+
+  private final FixedSizeDataType<V> dataType;
+  private final FixedSizeDataType<Long> keyType;
+  private final Memory<?> keyMemory;
+  private final Memory<?> valueMemory;
+
+  private final long capacity;
+  private final int valueSize;
+  private final int keySize;
+
+  private long size;
+
+  /**
+   * Creates a new builder for a DirectHashDataMap.
+   *
+   * @param <V> the type of values
+   * @return a new builder
+   */
+  public static <V> Builder<V> builder() {
+    return new Builder<>();
+  }
+
+  /**
+   * Builder for DirectHashDataMap.
+   *
+   * @param <V> the type of values
+   */
+  public static class Builder<V> {
+    private FixedSizeDataType<Long> keyType;
+    private FixedSizeDataType<V> dataType;
+    private Memory<?> keyMemory;
+    private Memory<?> valueMemory;
+    private long capacity;
+
+    /**
+     * Sets the key type for the map.
+     *
+     * @param keyType the data type for keys
+     * @return this builder
+     */
+    public Builder<V> keyType(FixedSizeDataType<Long> keyType) {
+      this.keyType = keyType;
+      return this;
+    }
+
+    /**
+     * Sets the data type for the map.
+     *
+     * @param dataType the data type for values
+     * @return this builder
+     */
+    public Builder<V> dataType(FixedSizeDataType<V> dataType) {
+      this.dataType = dataType;
+      return this;
+    }
+
+    /**
+     * Sets the key memory for the map.
+     *
+     * @param keyMemory the memory for keys
+     * @return this builder
+     */
+    public Builder<V> keyMemory(Memory<?> keyMemory) {
+      this.keyMemory = keyMemory;
+      return this;
+    }
+
+    /**
+     * Sets the value memory for the map.
+     *
+     * @param valueMemory the memory for values
+     * @return this builder
+     */
+    public Builder<V> valueMemory(Memory<?> valueMemory) {
+      this.valueMemory = valueMemory;
+      return this;
+    }
+
+    /**
+     * Sets the capacity for the map.
+     *
+     * @param capacity the fixed capacity of the map
+     * @return this builder
+     */
+    public Builder<V> capacity(long capacity) {
+      this.capacity = capacity;
+      return this;
+    }
+
+    /**
+     * Builds a new DirectHashDataMap.
+     *
+     * @return a new DirectHashDataMap
+     * @throws IllegalStateException if any required parameter is missing
+     */
+    public DirectHashDataMap<V> build() {
+      if (keyType == null) {
+        throw new IllegalStateException("Key type must be specified");
+      }
+      if (dataType == null) {
+        throw new IllegalStateException("Data type must be specified");
+      }
+      if (keyMemory == null) {
+        throw new IllegalStateException("Key memory must be specified");
+      }
+      if (valueMemory == null) {
+        throw new IllegalStateException("Value memory must be specified");
+      }
+      if (capacity <= 0) {
+        throw new IllegalStateException("Capacity must be greater than zero");
+      }
+
+      return new DirectHashDataMap<>(keyType, dataType, keyMemory, valueMemory, capacity);
+    }
+  }
+
+  /**
+   * Constructs a DirectHashDataMap with the specified capacity.
+   *
+   * @param keyType the data type for keys
+   * @param dataType the data type for values
+   * @param keyMemory the memory for keys
+   * @param valueMemory the memory for values
+   * @param capacity the fixed capacity of the map
+   */
+  private DirectHashDataMap(
+      FixedSizeDataType<Long> keyType,
+      FixedSizeDataType<V> dataType,
+      Memory<?> keyMemory,
+      Memory<?> valueMemory,
+      long capacity) {
+    this.keyType = keyType;
+    this.dataType = dataType;
+    this.keyMemory = keyMemory;
+    this.valueMemory = valueMemory;
+    this.capacity = capacity;
+    this.keySize = keyType.size();
+    this.valueSize = dataType.size();
+    this.size = 0;
+
+    // Initialize all keys to EMPTY_KEY
+    for (long i = 0; i < capacity; i++) {
+      storeKey(i, EMPTY_KEY);
+    }
+  }
+
+  /**
+   * Computes the hash value for a key.
+   * 
+   * @param key the key to hash
+   * @return the hash value
+   */
+  private long hash(long key) {
+    // Using a variant of the Knuth multiplicative method
+    // with the golden ratio
+    final long GOLDEN_RATIO = 0x9E3779B97F4A7C15L;
+    return ((key * GOLDEN_RATIO) >>> 16) % capacity;
+  }
+
+  /**
+   * Finds the slot for a key, either for retrieval or insertion.
+   * 
+   * @param key the key to find
+   * @param forInsertion whether we're finding for insertion or retrieval
+   * @return the slot index or -1 if not found and not for insertion
+   */
+  private long findSlot(long key, boolean forInsertion) {
+    long index = hash(key);
+    long tombstoneSlot = -1;
+
+    // Linear probing to handle collisions
+    for (long i = 0; i < capacity; i++) {
+      long currentIndex = (index + i) % capacity;
+      long currentKey = readKey(currentIndex);
+
+      if (currentKey == EMPTY_KEY) {
+        // Found empty slot
+        return forInsertion ? (tombstoneSlot != -1 ? tombstoneSlot : currentIndex) : -1;
+      } else if (currentKey == TOMBSTONE) {
+        // Mark first tombstone for possible reuse
+        if (tombstoneSlot == -1) {
+          tombstoneSlot = currentIndex;
+        }
+      } else if (currentKey == key) {
+        // Found the key
+        return currentIndex;
+      }
+    }
+
+    // If we're inserting and found a tombstone earlier, use that
+    if (forInsertion && tombstoneSlot != -1) {
+      return tombstoneSlot;
+    }
+
+    // Map is full or key not found
+    return -1;
+  }
+
+  /**
+   * Reads a key from the specified slot.
+   * 
+   * @param slot the slot index
+   * @return the key at the slot
+   */
+  private long readKey(long slot) {
+    long position = slot * keySize;
+    int segmentIndex = (int) (position >>> keyMemory.segmentShift());
+    int segmentOffset = (int) (position & keyMemory.segmentMask());
+    ByteBuffer segment = keyMemory.segment(segmentIndex);
+    return keyType.read(segment, segmentOffset);
+  }
+
+  /**
+   * Stores a key at the specified slot.
+   * 
+   * @param slot the slot index
+   * @param key the key to store
+   */
+  private void storeKey(long slot, long key) {
+    long position = slot * keySize;
+    int segmentIndex = (int) (position >>> keyMemory.segmentShift());
+    int segmentOffset = (int) (position & keyMemory.segmentMask());
+    ByteBuffer segment = keyMemory.segment(segmentIndex);
+    keyType.write(segment, segmentOffset, key);
+  }
+
+  /**
+   * Reads a value from the specified slot.
+   * 
+   * @param slot the slot index
+   * @return the value at the slot
+   */
+  private V readValue(long slot) {
+    long position = slot * valueSize;
+    int segmentIndex = (int) (position >>> valueMemory.segmentShift());
+    int segmentOffset = (int) (position & valueMemory.segmentMask());
+    ByteBuffer segment = valueMemory.segment(segmentIndex);
+    return dataType.read(segment, segmentOffset);
+  }
+
+  /**
+   * Stores a value at the specified slot.
+   * 
+   * @param slot the slot index
+   * @param value the value to store
+   */
+  private void storeValue(long slot, V value) {
+    long position = slot * valueSize;
+    int segmentIndex = (int) (position >>> valueMemory.segmentShift());
+    int segmentOffset = (int) (position & valueMemory.segmentMask());
+    ByteBuffer segment = valueMemory.segment(segmentIndex);
+    dataType.write(segment, segmentOffset, value);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public V put(Long key, V value) {
+    Objects.requireNonNull(key, "Key cannot be null");
+    Objects.requireNonNull(value, "Value cannot be null");
+
+    if (key == EMPTY_KEY || key == TOMBSTONE) {
+      throw new IllegalArgumentException("Reserved key value: " + key);
+    }
+
+    long slot = findSlot(key, true);
+    if (slot == -1) {
+      throw new IllegalStateException("Map is full");
+    }
+
+    long existingKey = readKey(slot);
+    V previousValue = null;
+
+    if (existingKey != EMPTY_KEY && existingKey != TOMBSTONE) {
+      // Slot contains an existing key
+      previousValue = readValue(slot);
+    } else {
+      // New entry
+      size++;
+    }
+
+    storeKey(slot, key);
+    storeValue(slot, value);
+
+    return previousValue;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public V get(Object keyObj) {
+    if (!(keyObj instanceof Long key)) {
+      return null;
+    }
+
+    if (key == EMPTY_KEY || key == TOMBSTONE) {
+      return null;
+    }
+
+    long slot = findSlot(key, false);
+    if (slot == -1) {
+      return null;
+    }
+
+    return readValue(slot);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean containsKey(Object keyObj) {
+    if (!(keyObj instanceof Long key)) {
+      return false;
+    }
+
+    if (key == EMPTY_KEY || key == TOMBSTONE) {
+      return false;
+    }
+
+    return findSlot(key, false) != -1;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean containsValue(Object value) {
+    if (value == null) {
+      return false;
+    }
+
+    Iterator<V> iterator = valueIterator();
+    while (iterator.hasNext()) {
+      if (value.equals(iterator.next())) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public long size() {
+    return size;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public void clear() {
+    for (long i = 0; i < capacity; i++) {
+      storeKey(i, EMPTY_KEY);
+    }
+    size = 0;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public Iterator<Long> keyIterator() {
+    return new Iterator<>() {
+      private long currentIndex = 0;
+      private long returnedCount = 0;
+
+      @Override
+      public boolean hasNext() {
+        return returnedCount < size;
+      }
+
+      @Override
+      public Long next() {
+        if (!hasNext()) {
+          throw new NoSuchElementException();
+        }
+
+        while (currentIndex < capacity) {
+          long key = readKey(currentIndex++);
+          if (key != EMPTY_KEY && key != TOMBSTONE) {
+            returnedCount++;
+            return key;
+          }
+        }
+
+        throw new NoSuchElementException();
+      }
+    };
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public Iterator<V> valueIterator() {
+    return new Iterator<>() {
+      private long currentIndex = 0;
+      private long returnedCount = 0;
+
+      @Override
+      public boolean hasNext() {
+        return returnedCount < size;
+      }
+
+      @Override
+      public V next() {
+        if (!hasNext()) {
+          throw new NoSuchElementException();
+        }
+
+        while (currentIndex < capacity) {
+          long key = readKey(currentIndex);
+          if (key != EMPTY_KEY && key != TOMBSTONE) {
+            returnedCount++;
+            return readValue(currentIndex++);
+          }
+          currentIndex++;
+        }
+
+        throw new NoSuchElementException();
+      }
+    };
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public Iterator<Entry<Long, V>> entryIterator() {
+    return new Iterator<>() {
+      private long currentIndex = 0;
+      private long returnedCount = 0;
+
+      @Override
+      public boolean hasNext() {
+        return returnedCount < size;
+      }
+
+      @Override
+      public Entry<Long, V> next() {
+        if (!hasNext()) {
+          throw new NoSuchElementException();
+        }
+
+        while (currentIndex < capacity) {
+          long key = readKey(currentIndex);
+          if (key != EMPTY_KEY && key != TOMBSTONE) {
+            V value = readValue(currentIndex);
+            currentIndex++;
+            returnedCount++;
+            return Map.entry(key, value);
+          }
+          currentIndex++;
+        }
+
+        throw new NoSuchElementException();
+      }
+    };
+  }
+
+  @Override
+  public void close() throws Exception {
+    try {
+      keyMemory.close();
+      valueMemory.close();
+    } catch (Exception e) {
+      throw new DataCollectionException(e);
+    }
+  }
+}
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/FixedSizeDataList.java b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/FixedSizeDataList.java
index 1d5d01dc9..5c7b57478 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/FixedSizeDataList.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/FixedSizeDataList.java
@@ -26,35 +26,85 @@ import org.apache.baremaps.data.memory.OffHeapMemory;
 import org.apache.baremaps.data.type.FixedSizeDataType;
 
 /**
- * A {@link DataList} that can hold a large number of fixed size data elements. This data list is
- * backed by a memory that can be either heap, off-heap, or memory mapped.
+ * A list that stores fixed-size elements in memory. Elements can be stored in heap memory, off-heap
+ * memory, or memory-mapped files.
  *
- * @param <E> The type of the elements.
+ * @param <E> The type of elements in the list
  */
 public class FixedSizeDataList<E> implements DataList<E> {
 
   private final FixedSizeDataType<E> dataType;
-
   private final Memory<?> memory;
-
   private AtomicLong size;
 
   /**
-   * Constructs a {@link FixedSizeDataList}.
+   * Creates a new builder for a FixedSizeDataList.
    *
-   * @param dataType the data type
+   * @param <E> the type of elements
+   * @return a new builder
+   */
+  public static <E> Builder<E> builder() {
+    return new Builder<>();
+  }
+
+  /**
+   * Builder for FixedSizeDataList.
+   *
+   * @param <E> the type of elements
    */
-  public FixedSizeDataList(FixedSizeDataType<E> dataType) {
-    this(dataType, new OffHeapMemory());
+  public static class Builder<E> {
+    private FixedSizeDataType<E> dataType;
+    private Memory<?> memory;
+
+    /**
+     * Sets the data type for the list.
+     *
+     * @param dataType the data type
+     * @return this builder
+     */
+    public Builder<E> dataType(FixedSizeDataType<E> dataType) {
+      this.dataType = dataType;
+      return this;
+    }
+
+    /**
+     * Sets the memory for the list.
+     *
+     * @param memory the memory
+     * @return this builder
+     */
+    public Builder<E> memory(Memory<?> memory) {
+      this.memory = memory;
+      return this;
+    }
+
+    /**
+     * Builds a new FixedSizeDataList.
+     *
+     * @return a new FixedSizeDataList
+     * @throws IllegalStateException if the data type is missing
+     */
+    public FixedSizeDataList<E> build() {
+      if (dataType == null) {
+        throw new IllegalStateException("Data type must be specified");
+      }
+
+      if (memory == null) {
+        memory = new OffHeapMemory();
+      }
+
+      return new FixedSizeDataList<>(dataType, memory);
+    }
   }
 
   /**
-   * Constructs a list.
+   * Constructs a FixedSizeDataList.
    *
    * @param dataType the data type
    * @param memory the memory
+   * @throws DataCollectionException if the data type is too large for the memory segment size
    */
-  public FixedSizeDataList(FixedSizeDataType<E> dataType, Memory<?> memory) {
+  private FixedSizeDataList(FixedSizeDataType<E> dataType, Memory<?> memory) {
     if (dataType.size() > memory.segmentSize()) {
       throw new DataCollectionException("The segment size is too small for the data type");
     }
@@ -63,6 +113,12 @@ public class FixedSizeDataList<E> implements DataList<E> {
     this.size = new AtomicLong(0);
   }
 
+  /**
+   * Writes an element at the specified index.
+   *
+   * @param index the index
+   * @param value the element to write
+   */
   private void write(long index, E value) {
     long position = index * dataType.size();
     int segmentIndex = (int) (position / memory.segmentSize());
@@ -109,4 +165,13 @@ public class FixedSizeDataList<E> implements DataList<E> {
   public void clear() {
     size.set(0);
   }
+
+  @Override
+  public void close() throws Exception {
+    try {
+      memory.close();
+    } catch (Exception e) {
+      throw new DataCollectionException(e);
+    }
+  }
 }
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/IndexedDataList.java b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/IndexedDataList.java
index dbe7e555d..1a14640f5 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/IndexedDataList.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/IndexedDataList.java
@@ -22,33 +22,85 @@ package org.apache.baremaps.data.collection;
 import org.apache.baremaps.data.type.LongDataType;
 
 /**
- * A data list that can hold a large number of variable size data elements. This data list is backed
- * by an index and a buffer that can be either heap, off-heap, or memory mapped.
+ * A list that stores variable-size elements using an index. Elements are stored in an append-only
+ * log with fixed indices for fast access.
  *
- * @param <E> The type of the elements.
+ * @param <E> The type of elements in the list
  */
 public class IndexedDataList<E> implements DataList<E> {
 
   private final DataList<Long> index;
-
   private final AppendOnlyLog<E> values;
 
   /**
-   * Constructs a {@link IndexedDataList}.
+   * Creates a new builder for an IndexedDataList.
    *
-   * @param values the values
+   * @param <E> the type of elements
+   * @return a new builder
    */
-  public IndexedDataList(AppendOnlyLog<E> values) {
-    this(new MemoryAlignedDataList<>(new LongDataType()), values);
+  public static <E> Builder<E> builder() {
+    return new Builder<>();
   }
 
   /**
-   * Constructs a {@link IndexedDataList}.
+   * Builder for IndexedDataList.
+   *
+   * @param <E> the type of elements
+   */
+  public static class Builder<E> {
+    private DataList<Long> index;
+    private AppendOnlyLog<E> values;
+
+    /**
+     * Sets the index for the list.
+     *
+     * @param index the index
+     * @return this builder
+     */
+    public Builder<E> index(DataList<Long> index) {
+      this.index = index;
+      return this;
+    }
+
+    /**
+     * Sets the values for the list.
+     *
+     * @param values the values
+     * @return this builder
+     */
+    public Builder<E> values(AppendOnlyLog<E> values) {
+      this.values = values;
+      return this;
+    }
+
+    /**
+     * Builds a new IndexedDataList.
+     *
+     * @return a new IndexedDataList
+     * @throws IllegalStateException if values are missing
+     */
+    public IndexedDataList<E> build() {
+      if (values == null) {
+        throw new IllegalStateException("Values must be specified");
+      }
+
+      if (index == null) {
+        index = MemoryAlignedDataList.<Long>builder()
+            .dataType(new LongDataType())
+            .build();
+      }
+
+      return new IndexedDataList<>(index, values);
+    }
+  }
+
+  /**
+   * Constructs an IndexedDataList.
    *
    * @param index the index
    * @param values the values
    */
-  public IndexedDataList(DataList<Long> index, AppendOnlyLog<E> values) {
+  private IndexedDataList(DataList<Long> index, AppendOnlyLog<E> values) {
     this.index = index;
     this.values = values;
   }
@@ -86,4 +138,14 @@ public class IndexedDataList<E> implements DataList<E> {
     index.clear();
     values.clear();
   }
+
+  @Override
+  public void close() throws Exception {
+    try {
+      index.close();
+      values.close();
+    } catch (Exception e) {
+      throw new DataCollectionException(e);
+    }
+  }
 }
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/IndexedDataMap.java b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/IndexedDataMap.java
index f1c6d546f..949d2dd90 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/IndexedDataMap.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/IndexedDataMap.java
@@ -26,33 +26,83 @@ import java.util.Map;
 import java.util.Map.Entry;
 
 /**
- * A {@link DataMap} that can hold a large number of variable size data elements. This data map is
- * backed by an index and a buffer that can be either heap, off-heap, or memory mapped.
+ * A map that stores variable-size values using an index. Values are stored in an append-only log
+ * with indices for fast access.
  *
- * @param <E> The type of the elements.
+ * @param <E> The type of values in the map
  */
 public class IndexedDataMap<E> implements DataMap<Long, E> {
 
   private final Map<Long, Long> index;
-
   private final AppendOnlyLog<E> values;
 
   /**
-   * Constructs a {@link IndexedDataMap}.
+   * Creates a new builder for an IndexedDataMap.
    *
-   * @param values the values
+   * @param <E> the type of values
+   * @return a new builder
+   */
+  public static <E> Builder<E> builder() {
+    return new Builder<>();
+  }
+
+  /**
+   * Builder for IndexedDataMap.
+   *
+   * @param <E> the type of values
    */
-  public IndexedDataMap(AppendOnlyLog<E> values) {
-    this(new HashMap<>(), values);
+  public static class Builder<E> {
+    private Map<Long, Long> index;
+    private AppendOnlyLog<E> values;
+
+    /**
+     * Sets the index for the map.
+     *
+     * @param index the index
+     * @return this builder
+     */
+    public Builder<E> index(Map<Long, Long> index) {
+      this.index = index;
+      return this;
+    }
+
+    /**
+     * Sets the values for the map.
+     *
+     * @param values the values
+     * @return this builder
+     */
+    public Builder<E> values(AppendOnlyLog<E> values) {
+      this.values = values;
+      return this;
+    }
+
+    /**
+     * Builds a new IndexedDataMap.
+     *
+     * @return a new IndexedDataMap
+     * @throws IllegalStateException if values are missing
+     */
+    public IndexedDataMap<E> build() {
+      if (values == null) {
+        throw new IllegalStateException("Values must be specified");
+      }
+
+      if (index == null) {
+        index = new HashMap<>();
+      }
+
+      return new IndexedDataMap<>(index, values);
+    }
   }
 
   /**
-   * Constructs a {@link IndexedDataMap}.
+   * Constructs an IndexedDataMap.
    *
    * @param index the index
    * @param values the values
    */
-  public IndexedDataMap(Map<Long, Long> index, AppendOnlyLog<E> values) {
+  private IndexedDataMap(Map<Long, Long> index, AppendOnlyLog<E> values) {
     this.index = index;
     this.values = values;
   }
@@ -121,4 +171,13 @@ public class IndexedDataMap<E> implements DataMap<Long, E> {
     index.clear();
     values.clear();
   }
+
+  @Override
+  public void close() throws Exception {
+    try {
+      values.close();
+    } catch (Exception e) {
+      throw new DataCollectionException(e);
+    }
+  }
 }
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/MemoryAlignedDataList.java b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/MemoryAlignedDataList.java
index e29f33839..b60294e6b 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/MemoryAlignedDataList.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/MemoryAlignedDataList.java
@@ -28,41 +28,89 @@ import org.apache.baremaps.data.memory.OffHeapMemory;
 import org.apache.baremaps.data.type.FixedSizeDataType;
 
 /**
- * A {@link DataList} that can hold a large number of fixed-size memory-aligned data elements. This
- * data list is backed by a memory that can be either heap, off-heap, or memory mapped.
+ * A list that stores fixed-size memory-aligned elements. Optimized for performance with bit-shift
+ * operations for memory addressing.
  *
- * @param <E> The type of the elements.
+ * @param <E> The type of elements in the list
  */
 public class MemoryAlignedDataList<E> implements DataList<E> {
 
   private final FixedSizeDataType<E> dataType;
-
   private final Memory<?> memory;
-
   private final int valueShift;
-
   private final long segmentShift;
-
   private final long segmentMask;
-
   private AtomicLong size;
 
   /**
-   * Constructs a {@link MemoryAlignedDataList}.
+   * Creates a new builder for a MemoryAlignedDataList.
    *
-   * @param dataType the data type
+   * @param <E> the type of elements
+   * @return a new builder
    */
-  public MemoryAlignedDataList(FixedSizeDataType<E> dataType) {
-    this(dataType, new OffHeapMemory());
+  public static <E> Builder<E> builder() {
+    return new Builder<>();
   }
 
   /**
-   * Constructs a {@link MemoryAlignedDataList}.
+   * Builder for MemoryAlignedDataList.
+   *
+   * @param <E> the type of elements
+   */
+  public static class Builder<E> {
+    private FixedSizeDataType<E> dataType;
+    private Memory<?> memory;
+
+    /**
+     * Sets the data type for the list.
+     *
+     * @param dataType the data type
+     * @return this builder
+     */
+    public Builder<E> dataType(FixedSizeDataType<E> dataType) {
+      this.dataType = dataType;
+      return this;
+    }
+
+    /**
+     * Sets the memory for the list.
+     *
+     * @param memory the memory
+     * @return this builder
+     */
+    public Builder<E> memory(Memory<?> memory) {
+      this.memory = memory;
+      return this;
+    }
+
+    /**
+     * Builds a new MemoryAlignedDataList.
+     *
+     * @return a new MemoryAlignedDataList
+     * @throws IllegalStateException if the data type is missing
+     */
+    public MemoryAlignedDataList<E> build() {
+      if (dataType == null) {
+        throw new IllegalStateException("Data type must be specified");
+      }
+
+      if (memory == null) {
+        memory = new OffHeapMemory();
+      }
+
+      return new MemoryAlignedDataList<>(dataType, memory);
+    }
+  }
+
+  /**
+   * Constructs a MemoryAlignedDataList.
    *
    * @param dataType the data type
    * @param memory the memory
+   * @throws DataCollectionException if memory and data type size requirements are not met
+   * @throws IllegalArgumentException if data type size is not a power of 2
    */
-  public MemoryAlignedDataList(FixedSizeDataType<E> dataType, Memory<?> memory) {
+  private MemoryAlignedDataList(FixedSizeDataType<E> dataType, Memory<?> memory) {
     if (dataType.size() > memory.segmentSize()) {
       throw new DataCollectionException("The segment size is too small for the data type");
     }
@@ -80,6 +128,12 @@ public class MemoryAlignedDataList<E> implements DataList<E> {
     this.size = new AtomicLong(0);
   }
 
+  /**
+   * Writes an element at the specified index.
+   *
+   * @param index the index
+   * @param value the element to write
+   */
   private void write(long index, E value) {
     long position = index << valueShift;
     int segmentIndex = (int) (position >>> segmentShift);
@@ -129,4 +183,12 @@ public class MemoryAlignedDataList<E> implements DataList<E> {
     }
   }
 
+  @Override
+  public void close() throws Exception {
+    try {
+      memory.close();
+    } catch (IOException e) {
+      throw new MemoryException(e);
+    }
+  }
 }
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/MemoryAlignedDataMap.java b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/MemoryAlignedDataMap.java
index aeeea0107..1fbd9abab 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/MemoryAlignedDataMap.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/MemoryAlignedDataMap.java
@@ -26,7 +26,8 @@ import org.apache.baremaps.data.memory.Memory;
 import org.apache.baremaps.data.type.FixedSizeDataType;
 
 /**
- * A {@link DataMap} that can hold a large number of fixed-size memory-aligned data elements.
+ * A map that stores fixed-size memory-aligned elements with optimized memory addressing.
+ * Uses bit-shift operations for fast memory access.
  *
  * <p>
  * This code has been adapted from Planetiler (Apache license).
@@ -46,12 +47,73 @@ public class MemoryAlignedDataMap<E> implements DataMap<Long, E> {
   private final long upperBoundary;
 
   /**
-   * Constructs a {@link MemoryAlignedDataMap}.
+   * Creates a new builder for a MemoryAlignedDataMap.
+   *
+   * @param <E> the type of values
+   * @return a new builder
+   */
+  public static <E> Builder<E> builder() {
+    return new Builder<>();
+  }
+
+  /**
+   * Builder for MemoryAlignedDataMap.
+   *
+   * @param <E> the type of values
+   */
+  public static class Builder<E> {
+    private FixedSizeDataType<E> dataType;
+    private Memory<?> memory;
+
+    /**
+     * Sets the data type for the map.
+     *
+     * @param dataType the data type
+     * @return this builder
+     */
+    public Builder<E> dataType(FixedSizeDataType<E> dataType) {
+      this.dataType = dataType;
+      return this;
+    }
+
+    /**
+     * Sets the memory for the map.
+     *
+     * @param memory the memory
+     * @return this builder
+     */
+    public Builder<E> memory(Memory<?> memory) {
+      this.memory = memory;
+      return this;
+    }
+
+    /**
+     * Builds a new MemoryAlignedDataMap.
+     *
+     * @return a new MemoryAlignedDataMap
+     * @throws IllegalStateException if required parameters are missing
+     */
+    public MemoryAlignedDataMap<E> build() {
+      if (dataType == null) {
+        throw new IllegalStateException("Data type must be specified");
+      }
+      if (memory == null) {
+        throw new IllegalStateException("Memory must be specified");
+      }
+      
+      return new MemoryAlignedDataMap<>(dataType, memory);
+    }
+  }
+
+  /**
+   * Constructs a MemoryAlignedDataMap.
    *
    * @param dataType the data type
    * @param memory the memory
+   * @throws DataCollectionException if memory and data type size requirements are not met
+   * @throws IllegalArgumentException if data type size is not a power of 2
    */
-  public MemoryAlignedDataMap(FixedSizeDataType<E> dataType, Memory<?> memory) {
+  private MemoryAlignedDataMap(FixedSizeDataType<E> dataType, Memory<?> memory) {
     if (dataType.size() > memory.segmentSize()) {
       throw new DataCollectionException("The segment size is too small for the data type");
     }
@@ -70,6 +132,13 @@ public class MemoryAlignedDataMap<E> implements DataMap<Long, E> {
         : Long.MAX_VALUE >> (32 - segmentShift + valueShift);
   }
 
+  /**
+   * Checks if the key is within valid boundaries.
+   *
+   * @param key the key to check
+   * @throws NullPointerException if key is null
+   * @throws IndexOutOfBoundsException if key is outside valid range
+   */
   private void checkBoundary(Long key) {
     Objects.requireNonNull(key, "Key couldn't be null");
     if (key < 0 || key > upperBoundary) {
@@ -210,4 +279,12 @@ public class MemoryAlignedDataMap<E> implements DataMap<Long, E> {
     };
   }
 
+  @Override
+  public void close() throws Exception {
+    try {
+      memory.close();
+    } catch (Exception e) {
+      throw new DataCollectionException(e);
+    }
+  }
 }
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/MonotonicDataMap.java b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/MonotonicDataMap.java
index f590e815c..6d3621640 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/MonotonicDataMap.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/MonotonicDataMap.java
@@ -24,15 +24,11 @@ import org.apache.baremaps.data.type.PairDataType;
 import org.apache.baremaps.data.type.PairDataType.Pair;
 
 /**
- * A {@link DataMap} that can hold a large number of variable-size data elements. The elements must
- * be sorted by their key and inserted in a monotonic way. The elements cannot be removed or updated
- * once inserted.
+ * A map that stores values in a monotonically increasing key order. Optimized for 
+ * sequential insertions and binary search lookups with chunked indexing.
  *
  * <p>
  * This code has been adapted from Planetiler (Apache license).
- *
- * <p>
- * Copyright (c) Planetiler.
  */
 public class MonotonicDataMap<E> implements DataMap<Long, E> {
 
@@ -41,40 +37,95 @@ public class MonotonicDataMap<E> implements DataMap<Long, E> {
   private final AppendOnlyLog<E> values;
 
   private long lastChunk = -1;
-
+  
   /**
-   * Constructs a {@link MonotonicDataMap} with default lists for storing offsets.
+   * Creates a new builder for a MonotonicDataMap.
    *
-   * @param values the buffer of values
+   * @param <E> the type of values in the map
+   * @return a new builder
    */
-  public MonotonicDataMap(AppendOnlyLog<E> values) {
-    this(
-        new MemoryAlignedDataList<>(new LongDataType()),
-        new MemoryAlignedDataList<>(new PairDataType<>(new LongDataType(), new LongDataType())),
-        values);
+  public static <E> Builder<E> builder() {
+    return new Builder<>();
   }
-
+  
   /**
-   * Constructs a {@link MonotonicDataMap} with default lists for storing offsets and keys.
+   * Builder for MonotonicDataMap.
    *
-   * @param keys the list of keys
-   * @param values the buffer of values
+   * @param <E> the type of values in the map
    */
-  public MonotonicDataMap(DataList<Pair<Long, Long>> keys, AppendOnlyLog<E> values) {
-    this(
-        new MemoryAlignedDataList<>(new LongDataType()),
-        keys,
-        values);
+  public static class Builder<E> {
+    private DataList<Long> offsets;
+    private DataList<Pair<Long, Long>> keys;
+    private AppendOnlyLog<E> values;
+    
+    /**
+     * Sets the offsets list.
+     *
+     * @param offsets the list of offsets
+     * @return this builder
+     */
+    public Builder<E> offsets(DataList<Long> offsets) {
+      this.offsets = offsets;
+      return this;
+    }
+    
+    /**
+     * Sets the keys list.
+     *
+     * @param keys the list of keys
+     * @return this builder
+     */
+    public Builder<E> keys(DataList<Pair<Long, Long>> keys) {
+      this.keys = keys;
+      return this;
+    }
+    
+    /**
+     * Sets the values buffer.
+     *
+     * @param values the buffer of values
+     * @return this builder
+     */
+    public Builder<E> values(AppendOnlyLog<E> values) {
+      this.values = values;
+      return this;
+    }
+    
+    /**
+     * Builds a new MonotonicDataMap.
+     *
+     * @return a new MonotonicDataMap
+     * @throws IllegalArgumentException if the values buffer is not provided
+     */
+    public MonotonicDataMap<E> build() {
+      if (values == null) {
+        throw new IllegalArgumentException("Values buffer must be provided");
+      }
+      
+      if (offsets == null) {
+        offsets = MemoryAlignedDataList.<Long>builder()
+            .dataType(new LongDataType())
+            .build();
+      }
+      
+      if (keys == null) {
+        keys = MemoryAlignedDataList.<Pair<Long, Long>>builder()
+            .dataType(new PairDataType<>(new LongDataType(), new LongDataType()))
+            .build();
+      }
+      
+      return new MonotonicDataMap<>(offsets, keys, values);
+    }
   }
 
   /**
-   * Constructs a {@link MonotonicDataMap}.
+   * Constructs a MonotonicDataMap.
    *
    * @param offsets the list of offsets
    * @param keys the list of keys
    * @param values the buffer of values
    */
-  public MonotonicDataMap(DataList<Long> offsets, DataList<Pair<Long, Long>> keys,
+  private MonotonicDataMap(DataList<Long> offsets, DataList<Pair<Long, Long>> keys,
       AppendOnlyLog<E> values) {
     this.offsets = offsets;
     this.keys = keys;
@@ -172,4 +223,15 @@ public class MonotonicDataMap<E> implements DataMap<Long, E> {
     values.clear();
   }
 
+  /** {@inheritDoc} */
+  @Override
+  public void close() throws Exception {
+    try {
+      offsets.close();
+      keys.close();
+      values.close();
+    } catch (Exception e) {
+      throw new DataCollectionException(e);
+    }
+  }
 }
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/MonotonicFixedSizeDataMap.java b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/MonotonicFixedSizeDataMap.java
index f0992cb94..a16b6f640 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/MonotonicFixedSizeDataMap.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/MonotonicFixedSizeDataMap.java
@@ -23,9 +23,8 @@ import java.util.Map.Entry;
 import org.apache.baremaps.data.type.LongDataType;
 
 /**
- * A {@link DataMap} that can hold a large number of fixed-size data elements. The elements must be
- * sorted by their key and inserted in a monotonic way. The elements cannot be removed or updated
- * once inserted.
+ * A map that stores fixed-size values in monotonically increasing key order.
+ * Optimized for sequential insertions and fast binary search lookups.
  *
  * <p>
  * This code has been adapted from Planetiler (Apache license).
@@ -41,23 +40,93 @@ public class MonotonicFixedSizeDataMap<E> implements DataMap<Long, E> {
   private long lastChunk = -1;
 
   /**
-   * Constructs a {@link MonotonicFixedSizeDataMap} with default lists for storing offsets and keys.
+   * Static factory method to create a new builder.
    *
-   * @param values the list of values
+   * @param <E> the type of elements
+   * @return a new builder
    */
-  public MonotonicFixedSizeDataMap(DataList<E> values) {
-    this(new MemoryAlignedDataList<>(new LongDataType()),
-        new MemoryAlignedDataList<>(new LongDataType()), values);
+  public static <E> Builder<E> builder() {
+    return new Builder<>();
   }
 
   /**
-   * Constructs a {@link MonotonicFixedSizeDataMap}.
+   * Builder for {@link MonotonicFixedSizeDataMap}.
+   *
+   * @param <E> the type of elements
+   */
+  public static class Builder<E> {
+    private DataList<Long> offsets;
+    private DataList<Long> keys;
+    private DataList<E> values;
+
+    /**
+     * Sets the offsets for the map.
+     *
+     * @param offsets the list of offsets
+     * @return this builder
+     */
+    public Builder<E> offsets(DataList<Long> offsets) {
+      this.offsets = offsets;
+      return this;
+    }
+
+    /**
+     * Sets the keys for the map.
+     *
+     * @param keys the list of keys
+     * @return this builder
+     */
+    public Builder<E> keys(DataList<Long> keys) {
+      this.keys = keys;
+      return this;
+    }
+
+    /**
+     * Sets the values for the map.
+     *
+     * @param values the list of values
+     * @return this builder
+     */
+    public Builder<E> values(DataList<E> values) {
+      this.values = values;
+      return this;
+    }
+
+    /**
+     * Builds a new {@link MonotonicFixedSizeDataMap}.
+     *
+     * @return a new MonotonicFixedSizeDataMap
+     * @throws IllegalStateException if values are missing
+     */
+    public MonotonicFixedSizeDataMap<E> build() {
+      if (values == null) {
+        throw new IllegalStateException("Values must be specified");
+      }
+      
+      if (offsets == null) {
+        offsets = MemoryAlignedDataList.<Long>builder()
+            .dataType(new LongDataType())
+            .build();
+      }
+      
+      if (keys == null) {
+        keys = MemoryAlignedDataList.<Long>builder()
+            .dataType(new LongDataType())
+            .build();
+      }
+      
+      return new MonotonicFixedSizeDataMap<>(offsets, keys, values);
+    }
+  }
+
+  /**
+   * Private constructor for {@link MonotonicFixedSizeDataMap}.
    *
    * @param offsets the list of offsets
    * @param keys the list of keys
    * @param values the list of values
    */
-  public MonotonicFixedSizeDataMap(
+  private MonotonicFixedSizeDataMap(
       DataList<Long> offsets,
       DataList<Long> keys,
       DataList<E> values) {
@@ -152,4 +221,14 @@ public class MonotonicFixedSizeDataMap<E> implements DataMap<Long, E> {
     values.clear();
   }
 
+  @Override
+  public void close() throws Exception {
+    try {
+      offsets.close();
+      keys.close();
+      values.close();
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+  }
 }
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/MonotonicPairedDataMap.java b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/MonotonicPairedDataMap.java
index ae64e0ddb..41fd57026 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/collection/MonotonicPairedDataMap.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/collection/MonotonicPairedDataMap.java
@@ -26,9 +26,8 @@ import org.apache.baremaps.data.type.LongDataType;
 import org.apache.baremaps.data.type.PairDataType.Pair;
 
 /**
- * A {@link DataMap} that can hold a large number of variable-size data elements. The elements must
- * be sorted by their key and inserted in a monotonic way. The elements cannot be removed or updated
- * once inserted.
+ * A map that stores values as key-value pairs in monotonically increasing key order. Optimized for
+ * sequential insertions and fast binary search lookups.
  *
  * <p>
  * This code has been adapted from Planetiler (Apache license).
@@ -44,19 +43,74 @@ public class MonotonicPairedDataMap<E> implements DataMap<Long, E> {
   private long lastChunk = -1;
 
   /**
-   * Constructs a {@link MonotonicPairedDataMap}.
+   * Creates a new builder for a MonotonicPairedDataMap.
+   *
+   * @param <E> the type of values
+   * @return a new builder
    */
-  public MonotonicPairedDataMap(MemoryAlignedDataList<Pair<Long, E>> values) {
-    this(new MemoryAlignedDataList<>(new LongDataType()), values);
+  public static <E> Builder<E> builder() {
+    return new Builder<>();
   }
 
   /**
-   * Constructs a {@link MonotonicPairedDataMap}.
+   * Builder for MonotonicPairedDataMap.
+   *
+   * @param <E> the type of values
+   */
+  public static class Builder<E> {
+    private DataList<Long> offsets;
+    private MemoryAlignedDataList<Pair<Long, E>> values;
+
+    /**
+     * Sets the offsets for the map.
+     *
+     * @param offsets the list of offsets
+     * @return this builder
+     */
+    public Builder<E> offsets(DataList<Long> offsets) {
+      this.offsets = offsets;
+      return this;
+    }
+
+    /**
+     * Sets the values for the map.
+     *
+     * @param values the buffer of values
+     * @return this builder
+     */
+    public Builder<E> values(MemoryAlignedDataList<Pair<Long, E>> values) {
+      this.values = values;
+      return this;
+    }
+
+    /**
+     * Builds a new MonotonicPairedDataMap.
+     *
+     * @return a new MonotonicPairedDataMap
+     * @throws IllegalStateException if values are missing
+     */
+    public MonotonicPairedDataMap<E> build() {
+      if (values == null) {
+        throw new IllegalStateException("Values must be specified");
+      }
+
+      if (offsets == null) {
+        offsets = MemoryAlignedDataList.<Long>builder()
+            .dataType(new LongDataType())
+            .build();
+      }
+
+      return new MonotonicPairedDataMap<>(offsets, values);
+    }
+  }
+
+  /**
+   * Constructs a MonotonicPairedDataMap.
    *
    * @param offsets the list of offsets
    * @param values the buffer of values
    */
-  public MonotonicPairedDataMap(DataList<Long> offsets,
+  private MonotonicPairedDataMap(DataList<Long> offsets,
       MemoryAlignedDataList<Pair<Long, E>> values) {
     this.offsets = offsets;
     this.values = values;
@@ -151,4 +205,14 @@ public class MonotonicPairedDataMap<E> implements DataMap<Long, E> {
     offsets.clear();
     values.clear();
   }
+
+  @Override
+  public void close() throws Exception {
+    try {
+      offsets.close();
+      values.close();
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+  }
 }
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/memory/Memory.java b/baremaps-data/src/main/java/org/apache/baremaps/data/memory/Memory.java
index d0a2580bf..56dd45a14 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/memory/Memory.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/memory/Memory.java
@@ -17,16 +17,13 @@
 
 package org.apache.baremaps.data.memory;
 
-
-
-import java.io.Closeable;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.List;
 
-/** A base class to manage segments of on-heap, off-heap, or on-disk memory. */
-public abstract class Memory<T extends ByteBuffer> implements Closeable {
+/** A base class for managing on-heap, off-heap, or memory-mapped segments. */
+public abstract class Memory<T extends ByteBuffer> implements AutoCloseable {
 
   private final int segmentSize;
 
@@ -36,10 +33,14 @@ public abstract class Memory<T extends ByteBuffer> implements Closeable {
 
   protected final List<T> segments = new ArrayList<>();
 
+  // Flag to track if this Memory has been closed
+  protected volatile boolean closed = false;
+
   /**
-   * Constructs a memory with a given segment size.
+   * Constructs a memory with the specified segment size.
    *
-   * @param segmentSize the size of the segments
+   * @param segmentSize the size of the segments (must be a power of 2)
+   * @throws IllegalArgumentException if the segment size is not a power of 2
    */
   protected Memory(int segmentSize) {
     if ((segmentSize & -segmentSize) != segmentSize) {
@@ -51,37 +52,38 @@ public abstract class Memory<T extends ByteBuffer> implements Closeable {
   }
 
   /**
-   * Returns the size of the segments.
+   * Returns the size of each segment.
    *
-   * @return the size of the segments
+   * @return the segment size in bytes
    */
   public int segmentSize() {
     return segmentSize;
   }
 
   /**
-   * Returns the bit shift to find a segment index from a memory position.
+   * Returns the bit shift used to find a segment index from a memory position.
    *
-   * @return the bit shift
+   * @return the bit shift value
    */
   public long segmentShift() {
     return segmentShift;
   }
 
   /**
-   * Returns the bit mask to find a segment offset from a memory position.
+   * Returns the bit mask used to find an offset within a segment from a memory position.
    *
-   * @return the bit mask
+   * @return the bit mask value
    */
   public long segmentMask() {
     return segmentMask;
   }
 
   /**
-   * Returns a segment of the memory.
+   * Returns a segment at the specified index, allocating it if necessary.
    *
-   * @param index the index of the segment
-   * @return the segment
+   * @param index the segment index
+   * @return the segment as a ByteBuffer
+   * @throws MemoryException if segment allocation fails
    */
   public ByteBuffer segment(int index) {
     if (segments.size() <= index) {
@@ -94,38 +96,82 @@ public abstract class Memory<T extends ByteBuffer> implements Closeable {
     return segment;
   }
 
-  /** The allocation of segments is synchronized to enable access by multiple threads. */
-  private synchronized ByteBuffer allocate(int index) {
-    while (segments.size() <= index) {
-      segments.add(null);
+  /**
+   * Checks if this Memory has been closed.
+   * 
+   * @throws IllegalStateException if this Memory has been closed
+   */
+  protected void checkNotClosed() {
+    if (closed) {
+      throw new IllegalStateException("Memory has been closed");
     }
-    T segment = segments.get(index);
-    if (segment == null) {
-      segment = allocate(index, segmentSize);
-      segments.set(index, segment);
+  }
+
+  /**
+   * Allocates a segment at the specified index. Thread-safe method.
+   *
+   * @param index the segment index
+   * @return the allocated segment
+   * @throws MemoryException if allocation fails
+   */
+  private synchronized ByteBuffer allocate(int index) {
+    checkNotClosed();
+
+    try {
+      while (segments.size() <= index) {
+        segments.add(null);
+      }
+      T segment = segments.get(index);
+      if (segment == null) {
+        segment = allocate(index, segmentSize);
+        segments.set(index, segment);
+      }
+      return segment;
+    } catch (OutOfMemoryError e) {
+      throw new MemoryException(
+          "Failed to allocate memory segment of size " + segmentSize + " bytes", e);
+    } catch (Exception e) {
+      throw new MemoryException("Failed to allocate memory segment", e);
     }
-    return segment;
   }
 
-  /** Returns the size of the allocated memory. */
+  /**
+   * Returns the total size of allocated memory.
+   *
+   * @return the size in bytes
+   */
   public long size() {
+    checkNotClosed();
     return (long) segments.size() * (long) segmentSize;
   }
 
   /**
-   * Allocates a segment for a given index and size.
+   * Allocates a segment of the specified size.
    *
-   * @param index the index of the segment
-   * @param size the size of the segment
-   * @return the segment
+   * @param index the segment index
+   * @param size the segment size in bytes
+   * @return the allocated segment
    */
   protected abstract T allocate(int index, int size);
 
   /**
-   * Clears the memory and the underlying resources.
+   * Releases resources associated with this memory. Unlike {@link #clear()}, this method does not
+   * delete underlying data.
+   *
+   * @throws IOException if an I/O error occurs
    */
-  public abstract void clear() throws IOException;
-
-
+  @Override
+  public synchronized void close() throws IOException {
+    if (!closed) {
+      closed = true;
+    }
+  }
 
+  /**
+   * Deletes all data managed by this memory. Unlike {@link #close()}, this method removes
+   * underlying data.
+   *
+   * @throws IOException if an I/O error occurs
+   */
+  public abstract void clear() throws IOException;
 }
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/memory/MemoryException.java b/baremaps-data/src/main/java/org/apache/baremaps/data/memory/MemoryException.java
index 3a4cbb6e0..e261269f2 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/memory/MemoryException.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/memory/MemoryException.java
@@ -17,35 +17,35 @@
 
 package org.apache.baremaps.data.memory;
 
-/** Signals that an exception occurred in a memory. */
+/** Exception thrown when a memory operation fails. */
 public class MemoryException extends RuntimeException {
 
-  /** Constructs a {@link MemoryException} with {@code null} as its error detail message. */
+  /** Constructs a MemoryException with no message or cause. */
   public MemoryException() {}
 
   /**
-   * Constructs an {@link MemoryException} with the specified detail message.
+   * Constructs a MemoryException with the specified message.
    *
-   * @param message the message
+   * @param message error message
    */
   public MemoryException(String message) {
     super(message);
   }
 
   /**
-   * Constructs a {@link MemoryException} with the specified cause.
+   * Constructs a MemoryException with the specified cause.
    *
-   * @param cause the cause
+   * @param cause the underlying cause
    */
   public MemoryException(Throwable cause) {
     super(cause);
   }
 
   /**
-   * Constructs a {@link MemoryException} with the specified detail message and cause.
+   * Constructs a MemoryException with the specified message and cause.
    *
-   * @param message the message
-   * @param cause the cause
+   * @param message error message
+   * @param cause the underlying cause
    */
   public MemoryException(String message, Throwable cause) {
     super(message, cause);
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/memory/MemoryMappedDirectory.java b/baremaps-data/src/main/java/org/apache/baremaps/data/memory/MemoryMappedDirectory.java
index 82d2e0af7..d00e3a90a 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/memory/MemoryMappedDirectory.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/memory/MemoryMappedDirectory.java
@@ -17,8 +17,6 @@
 
 package org.apache.baremaps.data.memory;
 
-
-
 import java.io.IOException;
 import java.nio.MappedByteBuffer;
 import java.nio.channels.FileChannel;
@@ -29,28 +27,25 @@ import org.apache.baremaps.data.util.FileUtils;
 import org.apache.baremaps.data.util.MappedByteBufferUtils;
 
 
-/**
- * A {@link Memory} that stores segments on-disk using mapped byte buffers in a directory of files.
- */
+/** Memory implementation that uses memory-mapped files in a directory for storage. */
 public class MemoryMappedDirectory extends Memory<MappedByteBuffer> {
 
   private final Path directory;
 
   /**
-   * Constructs an {@link MemoryMappedDirectory} with a custom directory and a default segment size
-   * of 1gb.
+   * Constructs a MemoryMappedDirectory with the specified directory and 1GB segment size.
    *
-   * @param directory the directory that stores the data
+   * @param directory the directory to store segments in
    */
   public MemoryMappedDirectory(Path directory) {
     this(directory, 1 << 30);
   }
 
   /**
-   * Constructs an {@link MemoryMappedDirectory} with a custom directory and a custom segment size.
+   * Constructs a MemoryMappedDirectory with the specified directory and segment size.
    *
-   * @param directory the directory that stores the data
-   * @param segmentBytes the size of the segments in bytes
+   * @param directory the directory to store segments in
+   * @param segmentBytes the size of each segment in bytes
    */
   public MemoryMappedDirectory(Path directory, int segmentBytes) {
     super(segmentBytes);
@@ -71,20 +66,35 @@ public class MemoryMappedDirectory extends Memory<MappedByteBuffer> {
     }
   }
 
-  /** {@inheritDoc} */
+  /**
+   * Releases all mapped buffers but keeps the files intact.
+   * 
+   * {@inheritDoc}
+   */
   @Override
-  public void close() throws IOException {
+  public synchronized void close() throws IOException {
+    // Release MappedByteBuffer resources
     for (MappedByteBuffer buffer : segments) {
-      MappedByteBufferUtils.unmap(buffer);
+      if (buffer != null) {
+        MappedByteBufferUtils.unmap(buffer);
+      }
     }
   }
 
-  /** {@inheritDoc} */
+  /**
+   * Unmaps all buffers and deletes the directory with all its files.
+   * 
+   * {@inheritDoc}
+   */
   @Override
-  public void clear() throws IOException {
+  public synchronized void clear() throws IOException {
+    // Release resources first
     close();
+
+    // Clear the segment list
     segments.clear();
+
+    // Delete the directory and all files in it
     FileUtils.deleteRecursively(directory);
   }
-
 }
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/memory/MemoryMappedFile.java b/baremaps-data/src/main/java/org/apache/baremaps/data/memory/MemoryMappedFile.java
index 5db613d34..54a9ddeb0 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/memory/MemoryMappedFile.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/memory/MemoryMappedFile.java
@@ -17,8 +17,6 @@
 
 package org.apache.baremaps.data.memory;
 
-
-
 import java.io.IOException;
 import java.nio.MappedByteBuffer;
 import java.nio.channels.FileChannel;
@@ -28,25 +26,25 @@ import java.nio.file.Path;
 import java.nio.file.StandardOpenOption;
 import org.apache.baremaps.data.util.MappedByteBufferUtils;
 
-/** A {@link Memory} that stores segments on-disk using mapped byte buffers in a file. */
+/** Memory implementation that uses memory-mapped file for storage. */
 public class MemoryMappedFile extends Memory<MappedByteBuffer> {
 
   private final Path file;
 
   /**
-   * Constructs an {@link MemoryMappedFile} with a custom file and a default segment size of 1gb.
+   * Constructs a MemoryMappedFile with the specified file and 1GB segment size.
    *
-   * @param file the file that stores the data
+   * @param file the file to memory-map
    */
   public MemoryMappedFile(Path file) {
     this(file, 1 << 30);
   }
 
   /**
-   * Constructs an {@link MemoryMappedFile} with a custom file and a custom segment size.
+   * Constructs a MemoryMappedFile with the specified file and segment size.
    *
-   * @param file the file that stores the data
-   * @param segmentBytes the size of the segments in bytes
+   * @param file the file to memory-map
+   * @param segmentBytes the size of each segment in bytes
    */
   public MemoryMappedFile(Path file, int segmentBytes) {
     super(segmentBytes);
@@ -66,19 +64,35 @@ public class MemoryMappedFile extends Memory<MappedByteBuffer> {
     }
   }
 
-  /** {@inheritDoc} */
+  /**
+   * Releases all mapped buffers but keeps the file intact.
+   * 
+   * {@inheritDoc}
+   */
   @Override
-  public void close() throws IOException {
+  public synchronized void close() throws IOException {
+    // Release MappedByteBuffer resources
     for (MappedByteBuffer buffer : segments) {
-      MappedByteBufferUtils.unmap(buffer);
+      if (buffer != null) {
+        MappedByteBufferUtils.unmap(buffer);
+      }
     }
   }
 
-  /** {@inheritDoc} */
+  /**
+   * Unmaps all buffers and deletes the file.
+   * 
+   * {@inheritDoc}
+   */
   @Override
-  public void clear() throws IOException {
+  public synchronized void clear() throws IOException {
+    // Release resources first
     close();
+
+    // Clear the segment list
     segments.clear();
-    Files.delete(file);
+
+    // Delete the file
+    Files.deleteIfExists(file);
   }
 }
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/memory/OffHeapMemory.java b/baremaps-data/src/main/java/org/apache/baremaps/data/memory/OffHeapMemory.java
index f95f03b51..66a2b7f2e 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/memory/OffHeapMemory.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/memory/OffHeapMemory.java
@@ -17,23 +17,21 @@
 
 package org.apache.baremaps.data.memory;
 
-
-
 import java.io.IOException;
 import java.nio.ByteBuffer;
 
-/** A {@link Memory} that stores segments off-heap using direct byte buffers. */
+/** Memory implementation that uses off-heap (direct) byte buffers for storage. */
 public class OffHeapMemory extends Memory<ByteBuffer> {
 
-  /** Constructs an {@link OffHeapMemory} with a default segment size of 1mb. */
+  /** Constructs an OffHeapMemory with a 1MB default segment size. */
   public OffHeapMemory() {
     this(1 << 20);
   }
 
   /**
-   * Constructs an {@link OffHeapMemory} with a custom segment size.
+   * Constructs an OffHeapMemory with the specified segment size.
    *
-   * @param segmentSize the size of the segments in bytes
+   * @param segmentSize the size of each segment in bytes
    */
   public OffHeapMemory(int segmentSize) {
     super(segmentSize);
@@ -45,15 +43,25 @@ public class OffHeapMemory extends Memory<ByteBuffer> {
     return ByteBuffer.allocateDirect(size);
   }
 
-  /** {@inheritDoc} */
+  /**
+   * Direct buffers are managed by GC and can't be explicitly freed in Java 8.
+   * 
+   * {@inheritDoc}
+   */
   @Override
-  public void close() throws IOException {
-    // Nothing to close
+  public synchronized void close() throws IOException {
+    // Direct ByteBuffers are managed by GC - we can't explicitly free them in Java 8
+    // In Java 9+, we could use Cleaner or Unsafe API if needed
   }
 
-  /** {@inheritDoc} */
+  /**
+   * Clears all segments to allow garbage collection.
+   * 
+   * {@inheritDoc}
+   */
   @Override
-  public void clear() throws IOException {
-    // Nothing to clean
+  public synchronized void clear() throws IOException {
+    // Clear the segment list to allow GC to reclaim the memory
+    segments.clear();
   }
 }
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/memory/OnHeapMemory.java b/baremaps-data/src/main/java/org/apache/baremaps/data/memory/OnHeapMemory.java
index 4b0fff757..d0be3968e 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/memory/OnHeapMemory.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/memory/OnHeapMemory.java
@@ -17,23 +17,21 @@
 
 package org.apache.baremaps.data.memory;
 
-
-
 import java.io.IOException;
 import java.nio.ByteBuffer;
 
-/** A {@link Memory} that stores segments on-heap using regular byte buffers. */
+/** Memory implementation that uses on-heap byte buffers for storage. */
 public class OnHeapMemory extends Memory<ByteBuffer> {
 
-  /** Constructs an {@link OnHeapMemory} with a default segment size of 1mb. */
+  /** Constructs an OnHeapMemory with a 1MB default segment size. */
   public OnHeapMemory() {
     this(1 << 20);
   }
 
   /**
-   * Constructs an {@link OnHeapMemory} with a custom segment size.
+   * Constructs an OnHeapMemory with the specified segment size.
    *
-   * @param segmentSize the size of the segments in bytes
+   * @param segmentSize the size of each segment in bytes
    */
   public OnHeapMemory(int segmentSize) {
     super(segmentSize);
@@ -45,15 +43,24 @@ public class OnHeapMemory extends Memory<ByteBuffer> {
     return ByteBuffer.allocate(size);
   }
 
-  /** {@inheritDoc} */
+  /**
+   * On-heap buffers don't require explicit cleanup.
+   * 
+   * {@inheritDoc}
+   */
   @Override
-  public void close() throws IOException {
-    // Nothing to close
+  public synchronized void close() throws IOException {
+    // On-heap ByteBuffers don't need special cleanup
   }
 
-  /** {@inheritDoc} */
+  /**
+   * Clears all segments to allow garbage collection.
+   * 
+   * {@inheritDoc}
+   */
   @Override
-  public void clear() throws IOException {
-    // Nothing to clean
+  public synchronized void clear() throws IOException {
+    // Clear the segment list to allow GC to reclaim the memory
+    segments.clear();
   }
 }
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/BooleanArrayDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/BooleanArrayDataType.java
index 328a483e0..628eaf413 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/BooleanArrayDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/BooleanArrayDataType.java
@@ -21,7 +21,9 @@ package org.apache.baremaps.data.type;
 
 import java.nio.ByteBuffer;
 
-/** A {@link DataType} for reading and writing lists of floats in {@link ByteBuffer}s. */
+/**
+ * A {@link DataType} for reading and writing arrays of boolean values in {@link ByteBuffer}s.
+ */
 public class BooleanArrayDataType implements DataType<boolean[]> {
 
   /** {@inheritDoc} */
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/BooleanDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/BooleanDataType.java
index 5758aad52..65e286c45 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/BooleanDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/BooleanDataType.java
@@ -23,10 +23,14 @@ import static java.lang.Boolean.TRUE;
 
 import java.nio.ByteBuffer;
 
-/** A {@link DataType} for reading and writing bytes in {@link ByteBuffer}s. */
+/**
+ * A {@link DataType} for reading and writing boolean values in {@link ByteBuffer}s.
+ */
 public class BooleanDataType extends MemoryAlignedDataType<Boolean> {
 
-  /** Constructs a {@link ByteDataType}. */
+  /**
+   * Constructs a {@link BooleanDataType} with a fixed size of {@link Byte#BYTES}.
+   */
   public BooleanDataType() {
     super(Byte.BYTES);
   }
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/BooleanListDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/BooleanListDataType.java
index 0368a67a1..b9395c29e 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/BooleanListDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/BooleanListDataType.java
@@ -23,7 +23,9 @@ import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.List;
 
-/** A {@link DataType} for reading and writing lists of floats in {@link ByteBuffer}s. */
+/**
+ * A {@link DataType} for reading and writing lists of boolean values in {@link ByteBuffer}s.
+ */
 public class BooleanListDataType implements DataType<List<Boolean>> {
 
   /** {@inheritDoc} */
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/ByteArrayDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/ByteArrayDataType.java
index c1210dcb1..787f4bb42 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/ByteArrayDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/ByteArrayDataType.java
@@ -21,7 +21,7 @@ package org.apache.baremaps.data.type;
 import java.nio.ByteBuffer;
 
 /**
- * A {@link DataType} for reading and writing lists of values in {@link ByteBuffer}s.
+ * A {@link DataType} for reading and writing byte arrays in {@link ByteBuffer}s.
  */
 public class ByteArrayDataType implements DataType<byte[]> {
 
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/ByteDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/ByteDataType.java
index 1c14bd945..b1899ec07 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/ByteDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/ByteDataType.java
@@ -17,14 +17,16 @@
 
 package org.apache.baremaps.data.type;
 
-
-
 import java.nio.ByteBuffer;
 
-/** A {@link DataType} for reading and writing bytes in {@link ByteBuffer}s. */
+/**
+ * A {@link DataType} for reading and writing byte values in {@link ByteBuffer}s.
+ */
 public class ByteDataType extends MemoryAlignedDataType<Byte> {
 
-  /** Constructs a {@link ByteDataType}. */
+  /**
+   * Constructs a {@link ByteDataType} with a fixed size of {@link Byte#BYTES}.
+   */
   public ByteDataType() {
     super(Byte.BYTES);
   }
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/ByteListDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/ByteListDataType.java
index 8ed14b208..41248ccd2 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/ByteListDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/ByteListDataType.java
@@ -24,7 +24,7 @@ import java.nio.ByteBuffer;
 import java.util.List;
 
 /**
- * A {@link DataType} for reading and writing lists of bytes in {@link ByteBuffer}s.
+ * A {@link DataType} for reading and writing lists of byte values in {@link ByteBuffer}s.
  */
 public class ByteListDataType implements DataType<List<Byte>> {
 
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/CoordinateArrayDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/CoordinateArrayDataType.java
index 124dc1e99..c339b78c5 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/CoordinateArrayDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/CoordinateArrayDataType.java
@@ -21,7 +21,8 @@ import java.nio.ByteBuffer;
 import org.locationtech.jts.geom.Coordinate;
 
 /**
- * A data type for {@link Coordinate} arrays.
+ * A {@link DataType} for reading and writing arrays of {@link Coordinate} values in
+ * {@link ByteBuffer}s.
  */
 public class CoordinateArrayDataType implements DataType<Coordinate[]> {
 
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/CoordinateDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/CoordinateDataType.java
index 0e4d99961..4d659af2c 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/CoordinateDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/CoordinateDataType.java
@@ -22,10 +22,12 @@ package org.apache.baremaps.data.type;
 import java.nio.ByteBuffer;
 import org.locationtech.jts.geom.Coordinate;
 
-/** A {@link DataType} for reading and writing {@link Coordinate}s in {@link ByteBuffer}s. */
+/** A {@link DataType} for reading and writing {@link Coordinate} values in {@link ByteBuffer}s. */
 public class CoordinateDataType extends MemoryAlignedDataType<Coordinate> {
 
-  /** Constructs a {@link CoordinateDataType}. */
+  /**
+   * Constructs a {@link CoordinateDataType} with a fixed size of two {@link Double#BYTES}.
+   */
   public CoordinateDataType() {
     super(Double.BYTES * 2);
   }
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/DataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/DataType.java
index 281cc794b..26f9901b1 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/DataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/DataType.java
@@ -25,7 +25,7 @@ import java.nio.ByteBuffer;
  * A data type for reading and writing values in {@link ByteBuffer}s. Read and write operations must
  * use absolute positions within the {@link ByteBuffer}.
  *
- * @param <T>
+ * @param <T> the type of value being read or written
  */
 public interface DataType<T> {
 
@@ -33,34 +33,34 @@ public interface DataType<T> {
    * Returns the size of the value.
    *
    * @param value the value
-   * @return the size of the value
+   * @return the size of the value in bytes
    */
   int size(final T value);
 
   /**
    * Returns the size of the value stored at the specified position in a {@link ByteBuffer}.
    *
-   * @param buffer the buffer
-   * @param position the position
-   * @return the size of the value
+   * @param buffer the buffer containing the value
+   * @param position the absolute position of the value within the buffer
+   * @return the size of the value in bytes
    */
   int size(final ByteBuffer buffer, final int position);
 
   /**
-   * Write a value.
+   * Writes a value to the specified position in a {@link ByteBuffer}.
    *
-   * @param buffer the source buffer
-   * @param position the absolute position of the value within the buffer
-   * @param value the value
+   * @param buffer the destination buffer
+   * @param position the absolute position within the buffer to write the value
+   * @param value the value to write
    */
   void write(final ByteBuffer buffer, final int position, final T value);
 
   /**
-   * Read a value.
+   * Reads a value from the specified position in a {@link ByteBuffer}.
    *
    * @param buffer the source buffer
-   * @param position the absolute position of the value within the buffer
-   * @return the object
+   * @param position the absolute position within the buffer to read the value
+   * @return the read value
    */
   T read(final ByteBuffer buffer, final int position);
 }
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/DoubleArrayDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/DoubleArrayDataType.java
index ecd83f681..cb2ce175c 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/DoubleArrayDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/DoubleArrayDataType.java
@@ -17,11 +17,11 @@
 
 package org.apache.baremaps.data.type;
 
-
-
 import java.nio.ByteBuffer;
 
-/** A {@link DataType} for reading and writing lists of doubles in {@link ByteBuffer}s. */
+/**
+ * A {@link DataType} for reading and writing arrays of double values in {@link ByteBuffer}s.
+ */
 public class DoubleArrayDataType implements DataType<double[]> {
 
   /** {@inheritDoc} */
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/DoubleDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/DoubleDataType.java
index 030b36c63..9a40116da 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/DoubleDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/DoubleDataType.java
@@ -17,14 +17,16 @@
 
 package org.apache.baremaps.data.type;
 
-
-
 import java.nio.ByteBuffer;
 
-/** A {@link DataType} for reading and writing doubles in {@link ByteBuffer}s. */
+/**
+ * A {@link DataType} for reading and writing double values in {@link ByteBuffer}s.
+ */
 public class DoubleDataType extends MemoryAlignedDataType<Double> {
 
-  /** Constructs a {@link DoubleDataType}. */
+  /**
+   * Constructs a {@link DoubleDataType} with a fixed size of {@link Double#BYTES}.
+   */
   public DoubleDataType() {
     super(Double.BYTES);
   }
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/DoubleListDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/DoubleListDataType.java
index 16e1133d6..5d2e8fa6f 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/DoubleListDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/DoubleListDataType.java
@@ -17,13 +17,13 @@
 
 package org.apache.baremaps.data.type;
 
-
-
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.List;
 
-/** A {@link DataType} for reading and writing lists of doubles in {@link ByteBuffer}s. */
+/**
+ * A {@link DataType} for reading and writing lists of double values in {@link ByteBuffer}s.
+ */
 public class DoubleListDataType implements DataType<List<Double>> {
 
   /** {@inheritDoc} */
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/FixedSizeDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/FixedSizeDataType.java
index 04a51bcfd..f62cb427b 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/FixedSizeDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/FixedSizeDataType.java
@@ -24,7 +24,7 @@ import java.nio.ByteBuffer;
 /**
  * A {@link DataType} for reading and writing values in {@link ByteBuffer}s whose size is fixed.
  *
- * @param <T>
+ * @param <T> the type of value being read or written
  */
 public abstract class FixedSizeDataType<T> implements DataType<T> {
 
@@ -33,7 +33,7 @@ public abstract class FixedSizeDataType<T> implements DataType<T> {
   /**
    * Creates a {@link FixedSizeDataType} with a fixed size.
    *
-   * @param size the size of the value
+   * @param size the size of the value in bytes
    */
   protected FixedSizeDataType(final int size) {
     this.size = size;
@@ -52,9 +52,9 @@ public abstract class FixedSizeDataType<T> implements DataType<T> {
   }
 
   /**
-   * Returns the size of the data type.
+   * Returns the fixed size of this data type.
    *
-   * @return the size of the data type
+   * @return the size in bytes
    */
   public int size() {
     return size;
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/FloatArrayDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/FloatArrayDataType.java
index a970f8f0d..4f0b303c2 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/FloatArrayDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/FloatArrayDataType.java
@@ -17,11 +17,11 @@
 
 package org.apache.baremaps.data.type;
 
-
-
 import java.nio.ByteBuffer;
 
-/** A {@link DataType} for reading and writing lists of floats in {@link ByteBuffer}s. */
+/**
+ * A {@link DataType} for reading and writing arrays of float values in {@link ByteBuffer}s.
+ */
 public class FloatArrayDataType implements DataType<float[]> {
 
   /** {@inheritDoc} */
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/FloatDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/FloatDataType.java
index 660a21bd5..8eca4fa10 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/FloatDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/FloatDataType.java
@@ -22,11 +22,13 @@ package org.apache.baremaps.data.type;
 import java.nio.ByteBuffer;
 
 /**
- * A {@link DataType} for reading and writing floats in {@link ByteBuffer}s.
+ * A {@link DataType} for reading and writing float values in {@link ByteBuffer}s.
  */
 public class FloatDataType extends MemoryAlignedDataType<Float> {
 
-  /** Constructs a {@link FloatDataType}. */
+  /**
+   * Constructs a {@link FloatDataType} with a fixed size of {@link Float#BYTES}.
+   */
   public FloatDataType() {
     super(Float.BYTES);
   }
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/FloatListDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/FloatListDataType.java
index 3f11fc5ba..e25e5f66e 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/FloatListDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/FloatListDataType.java
@@ -17,13 +17,13 @@
 
 package org.apache.baremaps.data.type;
 
-
-
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.List;
 
-/** A {@link DataType} for reading and writing lists of floats in {@link ByteBuffer}s. */
+/**
+ * A {@link DataType} for reading and writing lists of float values in {@link ByteBuffer}s.
+ */
 public class FloatListDataType implements DataType<List<Float>> {
 
   /** {@inheritDoc} */
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/GeometryCollectionDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/GeometryCollectionDataType.java
index 768a27b67..814ad6807 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/GeometryCollectionDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/GeometryCollectionDataType.java
@@ -24,7 +24,8 @@ import org.locationtech.jts.geom.GeometryCollection;
 import org.locationtech.jts.geom.GeometryFactory;
 
 /**
- * A data type for {@link GeometryCollection} objects.
+ * A {@link DataType} for reading and writing {@link GeometryCollection} objects in
+ * {@link ByteBuffer}s.
  */
 public class GeometryCollectionDataType implements DataType<GeometryCollection> {
 
@@ -33,14 +34,14 @@ public class GeometryCollectionDataType implements DataType<GeometryCollection>
   private final GeometryDataType geometryDataType;
 
   /**
-   * Constructs a {@code GeometryCollectionDataType} with a default {@code GeometryFactory}.
+   * Constructs a {@link GeometryCollectionDataType} with a default {@link GeometryFactory}.
    */
   public GeometryCollectionDataType() {
     this(new GeometryFactory(), new GeometryDataType());
   }
 
   /**
-   * Constructs a {@code GeometryCollectionDataType} with a specified {@code GeometryFactory}.
+   * Constructs a {@link GeometryCollectionDataType} with a specified {@link GeometryFactory}.
    *
    * @param geometryFactory the geometry factory
    */
@@ -49,8 +50,8 @@ public class GeometryCollectionDataType implements DataType<GeometryCollection>
   }
 
   /**
-   * Constructs a {@code GeometryCollectionDataType} with a specified {@code GeometryFactory} and
-   * {@code GeometryDataType}.
+   * Constructs a {@link GeometryCollectionDataType} with a specified {@link GeometryFactory} and
+   * {@link GeometryDataType}.
    *
    * @param geometryFactory the geometry factory
    * @param geometryDataType the geometry data type
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/GeometryDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/GeometryDataType.java
index 8e741d007..c124ff091 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/GeometryDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/GeometryDataType.java
@@ -22,7 +22,7 @@ import java.nio.ByteBuffer;
 import org.locationtech.jts.geom.*;
 
 /**
- * A {@code DataType} for {@link Geometry} objects.
+ * A {@link DataType} for reading and writing {@link Geometry} objects in {@link ByteBuffer}s.
  */
 public class GeometryDataType implements DataType<Geometry> {
 
@@ -42,16 +42,16 @@ public class GeometryDataType implements DataType<Geometry> {
   private final GeometryCollectionDataType geometryCollectionDataType;
 
   /**
-   * Constructs a {@code GeometryDataType} with a default {@code GeometryFactory}.
+   * Constructs a {@link GeometryDataType} with a default {@link GeometryFactory}.
    */
   public GeometryDataType() {
     this(new GeometryFactory());
   }
 
   /**
-   * Constructs a {@code GeometryDataType} with a specified {@code GeometryFactory}.
+   * Constructs a {@link GeometryDataType} with a specified {@link GeometryFactory}.
    *
-   * @param geometryFactory
+   * @param geometryFactory the geometry factory
    */
   public GeometryDataType(GeometryFactory geometryFactory) {
     this.pointDataType = new PointDataType(geometryFactory);
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/IntegerArrayDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/IntegerArrayDataType.java
index f66bb2255..45081b4c8 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/IntegerArrayDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/IntegerArrayDataType.java
@@ -17,11 +17,11 @@
 
 package org.apache.baremaps.data.type;
 
-
-
 import java.nio.ByteBuffer;
 
-/** A {@link DataType} for reading and writing lists of integers in {@link ByteBuffer}s. */
+/**
+ * A {@link DataType} for reading and writing arrays of integer values in {@link ByteBuffer}s.
+ */
 public class IntegerArrayDataType implements DataType<int[]> {
 
   /** {@inheritDoc} */
@@ -30,6 +30,7 @@ public class IntegerArrayDataType implements DataType<int[]> {
     return Integer.BYTES + values.length * Integer.BYTES;
   }
 
+  /** {@inheritDoc} */
   @Override
   public int size(final ByteBuffer buffer, final int position) {
     return buffer.getInt(position);
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/IntegerDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/IntegerDataType.java
index 7fdfd23e8..7fe6bb0f6 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/IntegerDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/IntegerDataType.java
@@ -22,11 +22,13 @@ package org.apache.baremaps.data.type;
 import java.nio.ByteBuffer;
 
 /**
- * A {@link DataType} for reading and writing integers in {@link ByteBuffer}s.
+ * A {@link DataType} for reading and writing integer values in {@link ByteBuffer}s.
  */
 public class IntegerDataType extends MemoryAlignedDataType<Integer> {
 
-  /** Constructs a {@link IntegerDataType}. */
+  /**
+   * Constructs an {@link IntegerDataType} with a fixed size of {@link Integer#BYTES}.
+   */
   public IntegerDataType() {
     super(Integer.BYTES);
   }
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/IntegerListDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/IntegerListDataType.java
index 35b7e813e..df4525ac3 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/IntegerListDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/IntegerListDataType.java
@@ -17,13 +17,13 @@
 
 package org.apache.baremaps.data.type;
 
-
-
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.List;
 
-/** A {@link DataType} for reading and writing lists of integers in {@link ByteBuffer}s. */
+/**
+ * A {@link DataType} for reading and writing lists of integer values in {@link ByteBuffer}s.
+ */
 public class IntegerListDataType implements DataType<List<Integer>> {
 
   /** {@inheritDoc} */
@@ -32,6 +32,7 @@ public class IntegerListDataType implements DataType<List<Integer>> {
     return Integer.BYTES + values.size() * Integer.BYTES;
   }
 
+  /** {@inheritDoc} */
   @Override
   public int size(final ByteBuffer buffer, final int position) {
     return buffer.getInt(position);
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/LineStringDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/LineStringDataType.java
index c6960230f..527639894 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/LineStringDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/LineStringDataType.java
@@ -22,7 +22,7 @@ import org.locationtech.jts.geom.GeometryFactory;
 import org.locationtech.jts.geom.LineString;
 
 /**
- * A data type for {@link LineString} objects.
+ * A {@link DataType} for reading and writing {@link LineString} objects in {@link ByteBuffer}s.
  */
 public class LineStringDataType implements DataType<LineString> {
 
@@ -31,14 +31,14 @@ public class LineStringDataType implements DataType<LineString> {
   private final CoordinateArrayDataType coordinateArrayDataType;
 
   /**
-   * Constructs a {@code LineStringDataType} with a default {@code GeometryFactory}.
+   * Constructs a {@link LineStringDataType} with a default {@link GeometryFactory}.
    */
   public LineStringDataType() {
     this(new GeometryFactory());
   }
 
   /**
-   * Constructs a {@code LineStringDataType} with a specified {@code GeometryFactory}.
+   * Constructs a {@link LineStringDataType} with a specified {@link GeometryFactory}.
    *
    * @param geometryFactory the geometry factory
    */
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/ListDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/ListDataType.java
index 0c0d9c819..abcda4835 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/ListDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/ListDataType.java
@@ -17,23 +17,23 @@
 
 package org.apache.baremaps.data.type;
 
-
-
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.List;
 
 /**
  * A {@link DataType} for reading and writing lists of objects in {@link ByteBuffer}s.
+ *
+ * @param <T> the type of elements in the list
  */
 public class ListDataType<T> implements DataType<List<T>> {
 
   public final DataType<T> dataType;
 
   /**
-   * Constructs a {@link ListDataType} with a data type.
+   * Constructs a {@link ListDataType} with a data type for its elements.
    *
-   * @param dataType the data type of the values
+   * @param dataType the data type of the list elements
    */
   public ListDataType(final DataType<T> dataType) {
     this.dataType = dataType;
@@ -49,6 +49,7 @@ public class ListDataType<T> implements DataType<List<T>> {
     return size;
   }
 
+  /** {@inheritDoc} */
   @Override
   public int size(final ByteBuffer buffer, final int position) {
     return buffer.getInt(position);
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/LonLatDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/LonLatDataType.java
index 939e6e8a1..8d59c9bbc 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/LonLatDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/LonLatDataType.java
@@ -32,11 +32,20 @@ public class LonLatDataType extends MemoryAlignedDataType<Coordinate> {
   private static final long SHIFT = 32;
   private static final long MASK = (1L << 32) - 1L;
 
-  /** Constructs a {@link LonLatDataType}. */
+  /**
+   * Constructs a {@link LonLatDataType} with a fixed size of {@link Long#BYTES}.
+   */
   public LonLatDataType() {
     super(Long.BYTES);
   }
 
+  /**
+   * Encodes longitude and latitude values into a single long value.
+   *
+   * @param lon the longitude value
+   * @param lat the latitude value
+   * @return the encoded coordinate as a long value
+   */
   public static long encodeLonLat(final double lon, final double lat) {
     long x = (long) (((lon + 180) / 360) * BITS);
     long y = (long) (((lat + 90) / 180) * BITS);
@@ -45,11 +54,23 @@ public class LonLatDataType extends MemoryAlignedDataType<Coordinate> {
     return l | r;
   }
 
+  /**
+   * Decodes the longitude value from an encoded coordinate.
+   *
+   * @param value the encoded coordinate
+   * @return the longitude value
+   */
   public static double decodeLon(final long value) {
     double l = (value >>> 32);
     return (l / BITS) * 360 - 180;
   }
 
+  /**
+   * Decodes the latitude value from an encoded coordinate.
+   *
+   * @param value the encoded coordinate
+   * @return the latitude value
+   */
   public static double decodeLat(final long value) {
     long r = (value & MASK);
     return (r / BITS) * 180 - 90;
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/LongArrayDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/LongArrayDataType.java
index d426b3aa6..2b03165e1 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/LongArrayDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/LongArrayDataType.java
@@ -17,11 +17,11 @@
 
 package org.apache.baremaps.data.type;
 
-
-
 import java.nio.ByteBuffer;
 
-/** A {@link DataType} for reading and writing lists of longs in {@link ByteBuffer}s. */
+/**
+ * A {@link DataType} for reading and writing arrays of long values in {@link ByteBuffer}s.
+ */
 public class LongArrayDataType implements DataType<long[]> {
 
   /** {@inheritDoc} */
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/LongDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/LongDataType.java
index ddff6d3e7..c4d5a86b9 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/LongDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/LongDataType.java
@@ -21,9 +21,14 @@ package org.apache.baremaps.data.type;
 
 import java.nio.ByteBuffer;
 
-/** A {@link DataType} for reading and writing longs in {@link ByteBuffer}s. */
+/**
+ * A {@link DataType} for reading and writing long values in {@link ByteBuffer}s.
+ */
 public class LongDataType extends MemoryAlignedDataType<Long> {
 
+  /**
+   * Constructs a {@link LongDataType} with a fixed size of {@link Long#BYTES}.
+   */
   public LongDataType() {
     super(Long.BYTES);
   }
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/LongListDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/LongListDataType.java
index df8af2146..926c391e6 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/LongListDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/LongListDataType.java
@@ -17,13 +17,13 @@
 
 package org.apache.baremaps.data.type;
 
-
-
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.List;
 
-/** A {@link DataType} for reading and writing lists of longs in {@link ByteBuffer}s. */
+/**
+ * A {@link DataType} for reading and writing lists of long values in {@link ByteBuffer}s.
+ */
 public class LongListDataType implements DataType<List<Long>> {
 
   /** {@inheritDoc} */
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/MapDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/MapDataType.java
index 82fda0bb8..141c3894e 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/MapDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/MapDataType.java
@@ -21,17 +21,30 @@ import java.nio.ByteBuffer;
 import java.util.HashMap;
 import java.util.Map;
 
+/**
+ * A {@link DataType} for reading and writing map objects in {@link ByteBuffer}s.
+ *
+ * @param <K> the type of keys in the map
+ * @param <V> the type of values in the map
+ */
 public class MapDataType<K, V> implements DataType<Map<K, V>> {
 
   private final DataType<K> keyType;
 
   private final DataType<V> valueType;
 
+  /**
+   * Constructs a {@link MapDataType} with data types for keys and values.
+   *
+   * @param keyType the data type for map keys
+   * @param valueType the data type for map values
+   */
   public MapDataType(final DataType<K> keyType, final DataType<V> valueType) {
     this.keyType = keyType;
     this.valueType = valueType;
   }
 
+  /** {@inheritDoc} */
   @Override
   public int size(final Map<K, V> value) {
     int size = Integer.BYTES;
@@ -42,11 +55,13 @@ public class MapDataType<K, V> implements DataType<Map<K, V>> {
     return size;
   }
 
+  /** {@inheritDoc} */
   @Override
   public int size(final ByteBuffer buffer, final int position) {
     return buffer.getInt(position);
   }
 
+  /** {@inheritDoc} */
   @Override
   public void write(final ByteBuffer buffer, final int position, final Map<K, V> value) {
     buffer.putInt(position, size(value));
@@ -59,6 +74,7 @@ public class MapDataType<K, V> implements DataType<Map<K, V>> {
     }
   }
 
+  /** {@inheritDoc} */
   @Override
   public Map<K, V> read(final ByteBuffer buffer, final int position) {
     int size = buffer.getInt(position);
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/MemoryAlignedDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/MemoryAlignedDataType.java
index 94c44a254..a6c6029b7 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/MemoryAlignedDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/MemoryAlignedDataType.java
@@ -25,14 +25,15 @@ import java.nio.ByteBuffer;
  * A {@link DataType} for reading and writing values in {@link ByteBuffer}s whose size is memory
  * aligned.
  *
- * @param <T>
+ * @param <T> the type of value being read or written
  */
 public abstract class MemoryAlignedDataType<T> extends FixedSizeDataType<T> {
 
   /**
-   * Constructs a {@link MemoryAlignedDataType}.
+   * Constructs a {@link MemoryAlignedDataType} with a memory-aligned size.
    *
-   * @param size
+   * @param size the size of the value in bytes (must be a power of 2)
+   * @throws IllegalArgumentException if the size is not a power of 2
    */
   protected MemoryAlignedDataType(int size) {
     super(size);
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/MultiLineStringDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/MultiLineStringDataType.java
index fd2e773ee..373dd74b3 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/MultiLineStringDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/MultiLineStringDataType.java
@@ -24,7 +24,8 @@ import org.locationtech.jts.geom.LineString;
 import org.locationtech.jts.geom.MultiLineString;
 
 /**
- * A data type for {@link MultiLineString} objects.
+ * A {@link DataType} for reading and writing {@link MultiLineString} objects in
+ * {@link ByteBuffer}s.
  */
 public class MultiLineStringDataType implements DataType<MultiLineString> {
 
@@ -33,14 +34,14 @@ public class MultiLineStringDataType implements DataType<MultiLineString> {
   private final GeometryFactory geometryFactory;
 
   /**
-   * Constructs a {@code MultiLineStringDataType} with a default {@code GeometryFactory}.
+   * Constructs a {@link MultiLineStringDataType} with a default {@link GeometryFactory}.
    */
   public MultiLineStringDataType() {
     this(new GeometryFactory());
   }
 
   /**
-   * Constructs a {@code MultiLineStringDataType} with a specified {@code GeometryFactory}.
+   * Constructs a {@link MultiLineStringDataType} with a specified {@link GeometryFactory}.
    *
    * @param geometryFactory the geometry factory
    */
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/MultiPointDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/MultiPointDataType.java
index 8161660c6..ad63c2042 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/MultiPointDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/MultiPointDataType.java
@@ -22,7 +22,7 @@ import org.locationtech.jts.geom.GeometryFactory;
 import org.locationtech.jts.geom.MultiPoint;
 
 /**
- * A data type for {@link MultiPoint} objects.
+ * A {@link DataType} for reading and writing {@link MultiPoint} objects in {@link ByteBuffer}s.
  */
 public class MultiPointDataType implements DataType<MultiPoint> {
 
@@ -31,14 +31,14 @@ public class MultiPointDataType implements DataType<MultiPoint> {
   private final GeometryFactory geometryFactory;
 
   /**
-   * Constructs a {@code MultiPointDataType} with a default {@code GeometryFactory}.
+   * Constructs a {@link MultiPointDataType} with a default {@link GeometryFactory}.
    */
   public MultiPointDataType() {
     this(new GeometryFactory());
   }
 
   /**
-   * Constructs a {@code MultiPointDataType} with a specified {@code GeometryFactory}.
+   * Constructs a {@link MultiPointDataType} with a specified {@link GeometryFactory}.
    *
    * @param geometryFactory the geometry factory
    */
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/MultiPolygonDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/MultiPolygonDataType.java
index b7a881852..be7b68863 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/MultiPolygonDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/MultiPolygonDataType.java
@@ -22,7 +22,7 @@ import java.util.ArrayList;
 import org.locationtech.jts.geom.*;
 
 /**
- * A data type for {@link GeometryCollection} objects.
+ * A {@link DataType} for reading and writing {@link MultiPolygon} objects in {@link ByteBuffer}s.
  */
 public class MultiPolygonDataType implements DataType<MultiPolygon> {
 
@@ -31,14 +31,14 @@ public class MultiPolygonDataType implements DataType<MultiPolygon> {
   private final PolygonDataType polygonDataType;
 
   /**
-   * Constructs a {@code MultiPolygonDataType} with a default {@code GeometryFactory}.
+   * Constructs a {@link MultiPolygonDataType} with a default {@link GeometryFactory}.
    */
   public MultiPolygonDataType() {
     this(new GeometryFactory());
   }
 
   /**
-   * Constructs a {@code MultiPolygonDataType} with a specified {@code GeometryFactory}.
+   * Constructs a {@link MultiPolygonDataType} with a specified {@link GeometryFactory}.
    *
    * @param geometryFactory the geometry factory
    */
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/NullableDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/NullableDataType.java
index 1a70d6d1e..83656c6ca 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/NullableDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/NullableDataType.java
@@ -21,15 +21,19 @@ package org.apache.baremaps.data.type;
 
 import java.nio.ByteBuffer;
 
-/** A {@link DataType} for reading and writing nullable values in {@link ByteBuffer}s. */
+/**
+ * A {@link DataType} for reading and writing nullable values in {@link ByteBuffer}s.
+ *
+ * @param <T> the type of value being read or written
+ */
 public class NullableDataType<T> implements DataType<T> {
 
   private final DataType<T> dataType;
 
   /**
-   * Constructs a {@link NullableDataType} with a data type.
+   * Constructs a {@link NullableDataType} with a data type for the wrapped value.
    *
-   * @param dataType the data type of the values
+   * @param dataType the data type for the non-null values
    */
   public NullableDataType(final DataType<T> dataType) {
     this.dataType = dataType;
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/PairDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/PairDataType.java
index 4f6bc37c5..37d52c963 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/PairDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/PairDataType.java
@@ -23,12 +23,23 @@ import java.nio.ByteBuffer;
 import java.util.Objects;
 import org.apache.baremaps.data.type.PairDataType.Pair;
 
-/** A {@link DataType} for reading and writing pairs of values in {@link ByteBuffer}s. */
+/**
+ * A {@link DataType} for reading and writing pairs of values in {@link ByteBuffer}s.
+ *
+ * @param <L> the type of the left value in the pair
+ * @param <R> the type of the right value in the pair
+ */
 public class PairDataType<L, R> extends FixedSizeDataType<Pair<L, R>> {
 
   private final FixedSizeDataType<L> left;
   private final FixedSizeDataType<R> right;
 
+  /**
+   * Constructs a {@link PairDataType} with data types for left and right values.
+   *
+   * @param left the data type for the left value
+   * @param right the data type for the right value
+   */
   public PairDataType(final FixedSizeDataType<L> left, final FixedSizeDataType<R> right) {
     super(left.size() + right.size());
     this.left = left;
@@ -50,21 +61,43 @@ public class PairDataType<L, R> extends FixedSizeDataType<Pair<L, R>> {
         right.read(buffer, position + left.size()));
   }
 
+  /**
+   * A class representing a pair of values.
+   *
+   * @param <L> the type of the left value
+   * @param <R> the type of the right value
+   */
   public static class Pair<L, R> {
 
     private final L left;
 
     private final R right;
 
+    /**
+     * Constructs a pair with left and right values.
+     *
+     * @param left the left value
+     * @param right the right value
+     */
     public Pair(L left, R right) {
       this.left = left;
       this.right = right;
     }
 
+    /**
+     * Returns the left value of the pair.
+     *
+     * @return the left value
+     */
     public L left() {
       return left;
     }
 
+    /**
+     * Returns the right value of the pair.
+     *
+     * @return the right value
+     */
     public R right() {
       return right;
     }
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/PointDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/PointDataType.java
index e48156acb..ed3e2a33a 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/PointDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/PointDataType.java
@@ -23,21 +23,21 @@ import org.locationtech.jts.geom.GeometryFactory;
 import org.locationtech.jts.geom.Point;
 
 /**
- * A data type for {@link Point} objects.
+ * A {@link DataType} for reading and writing {@link Point} objects in {@link ByteBuffer}s.
  */
 public class PointDataType implements DataType<Point> {
 
   private final GeometryFactory geometryFactory;
 
   /**
-   * Constructs a {@code PointDataType} with a default {@code GeometryFactory}.
+   * Constructs a {@link PointDataType} with a default {@link GeometryFactory}.
    */
   public PointDataType() {
     this(new GeometryFactory());
   }
 
   /**
-   * Constructs a {@code PointDataType} with a specified {@code GeometryFactory}.
+   * Constructs a {@link PointDataType} with a specified {@link GeometryFactory}.
    *
    * @param geometryFactory the geometry factory
    */
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/PolygonDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/PolygonDataType.java
index 86822094d..6bfb32146 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/PolygonDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/PolygonDataType.java
@@ -25,7 +25,7 @@ import org.locationtech.jts.geom.LinearRing;
 import org.locationtech.jts.geom.Polygon;
 
 /**
- * A data type for {@link Polygon} objects.
+ * A {@link DataType} for reading and writing {@link Polygon} objects in {@link ByteBuffer}s.
  */
 public class PolygonDataType implements DataType<Polygon> {
 
@@ -34,14 +34,14 @@ public class PolygonDataType implements DataType<Polygon> {
   private final GeometryFactory geometryFactory;
 
   /**
-   * Constructs a {@code PolygonDataType} with a default {@code GeometryFactory}.
+   * Constructs a {@link PolygonDataType} with a default {@link GeometryFactory}.
    */
   public PolygonDataType() {
     this(new GeometryFactory());
   }
 
   /**
-   * Constructs a {@code PolygonDataType} with a specified {@code GeometryFactory}.
+   * Constructs a {@link PolygonDataType} with a specified {@link GeometryFactory}.
    *
    * @param geometryFactory the geometry factory
    */
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/RowDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/RowDataType.java
index ba8bede60..a4ba70582 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/RowDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/RowDataType.java
@@ -27,10 +27,13 @@ import org.apache.baremaps.store.DataRowImpl;
 import org.apache.baremaps.store.DataSchema;
 
 /**
- * A data type for rows.
+ * A {@link DataType} for reading and writing {@link DataRow} objects in {@link ByteBuffer}s.
  */
 public class RowDataType implements DataType<DataRow> {
 
+  /**
+   * Map of data types for each of the supported column types.
+   */
   private static final EnumMap<Type, DataType> types = new EnumMap<>(Type.class);
 
   static {
@@ -55,10 +58,16 @@ public class RowDataType implements DataType<DataRow> {
 
   private final DataSchema rowType;
 
+  /**
+   * Constructs a {@link RowDataType} with a specified schema.
+   *
+   * @param rowType the data schema defining the row's structure
+   */
   public RowDataType(DataSchema rowType) {
     this.rowType = rowType;
   }
 
+  /** {@inheritDoc} */
   @Override
   public int size(final DataRow row) {
     int size = Integer.BYTES;
@@ -72,11 +81,13 @@ public class RowDataType implements DataType<DataRow> {
     return size;
   }
 
+  /** {@inheritDoc} */
   @Override
   public int size(final ByteBuffer buffer, final int position) {
     return buffer.getInt(position);
   }
 
+  /** {@inheritDoc} */
   @Override
   public void write(final ByteBuffer buffer, final int position, final DataRow row) {
     int p = position + Integer.BYTES;
@@ -92,6 +103,7 @@ public class RowDataType implements DataType<DataRow> {
     buffer.putInt(position, p - position);
   }
 
+  /** {@inheritDoc} */
   @Override
   public DataRow read(final ByteBuffer buffer, final int position) {
     int p = position + Integer.BYTES;
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/ShortArrayDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/ShortArrayDataType.java
index a45bb37bd..c242a25eb 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/ShortArrayDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/ShortArrayDataType.java
@@ -21,7 +21,9 @@ package org.apache.baremaps.data.type;
 
 import java.nio.ByteBuffer;
 
-/** A {@link DataType} for reading and writing lists of shorts in {@link ByteBuffer}s. */
+/**
+ * A {@link DataType} for reading and writing arrays of short values in {@link ByteBuffer}s.
+ */
 public class ShortArrayDataType implements DataType<short[]> {
 
   /** {@inheritDoc} */
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/ShortDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/ShortDataType.java
index 02ca4d1d3..5605d4711 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/ShortDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/ShortDataType.java
@@ -21,10 +21,14 @@ package org.apache.baremaps.data.type;
 
 import java.nio.ByteBuffer;
 
-/** A {@link DataType} for reading and writing shorts in {@link ByteBuffer}s. */
+/**
+ * A {@link DataType} for reading and writing short values in {@link ByteBuffer}s.
+ */
 public class ShortDataType extends FixedSizeDataType<Short> {
 
-  /** Constructs a {@link ShortDataType}. */
+  /**
+   * Constructs a {@link ShortDataType} with a fixed size of {@link Short#BYTES}.
+   */
   public ShortDataType() {
     super(Short.BYTES);
   }
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/ShortListDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/ShortListDataType.java
index 1f99d24a7..8888cddd0 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/ShortListDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/ShortListDataType.java
@@ -17,13 +17,13 @@
 
 package org.apache.baremaps.data.type;
 
-
-
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.List;
 
-/** A {@link DataType} for reading and writing lists of shorts in {@link ByteBuffer}s. */
+/**
+ * A {@link DataType} for reading and writing lists of short values in {@link ByteBuffer}s.
+ */
 public class ShortListDataType implements DataType<List<Short>> {
 
   /** {@inheritDoc} */
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/SmallIntegerDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/SmallIntegerDataType.java
index b8ccd7e5f..b3cfd3277 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/SmallIntegerDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/SmallIntegerDataType.java
@@ -17,19 +17,21 @@
 
 package org.apache.baremaps.data.type;
 
-
-
 import java.nio.ByteBuffer;
 
-/** A {@link DataType} for reading and writing small integers in {@link ByteBuffer}s. */
+/**
+ * A {@link DataType} for reading and writing small integer values in {@link ByteBuffer}s with a
+ * customizable storage size.
+ */
 public class SmallIntegerDataType extends FixedSizeDataType<Integer> {
 
   private final int n;
 
   /**
-   * Constructs a {@link SmallIntegerDataType}.
+   * Constructs a {@link SmallIntegerDataType} with a specified number of bytes.
    *
-   * @param n the number of bytes used to store the integer
+   * @param n the number of bytes used to store the integer (must be between 1 and 4)
+   * @throws IllegalArgumentException if n is less than 1 or greater than 4
    */
   public SmallIntegerDataType(int n) {
     super(n);
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/SmallLongDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/SmallLongDataType.java
index 2e7812f04..cb0a4f70b 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/SmallLongDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/SmallLongDataType.java
@@ -17,19 +17,21 @@
 
 package org.apache.baremaps.data.type;
 
-
-
 import java.nio.ByteBuffer;
 
-/** A {@link DataType} for reading and writing small longs in {@link ByteBuffer}s. */
+/**
+ * A {@link DataType} for reading and writing small long values in {@link ByteBuffer}s with a
+ * customizable storage size.
+ */
 public class SmallLongDataType extends FixedSizeDataType<Long> {
 
   private final int n;
 
   /**
-   * Constructs a {@link SmallIntegerDataType}.
+   * Constructs a {@link SmallLongDataType} with a specified number of bytes.
    *
-   * @param n the number of bytes used to store the integer
+   * @param n the number of bytes used to store the long value (must be between 1 and 8)
+   * @throws IllegalArgumentException if n is less than 1 or greater than 8
    */
   public SmallLongDataType(int n) {
     super(n);
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/StringDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/StringDataType.java
index a3eb253ad..7050140f8 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/StringDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/StringDataType.java
@@ -23,7 +23,7 @@ import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 
 /**
- * A {@link DataType} for reading and writing strings in {@link ByteBuffer}s.
+ * A {@link DataType} for reading and writing string values in {@link ByteBuffer}s.
  */
 public class StringDataType implements DataType<String> {
 
diff --git a/baremaps-data/src/main/java/org/apache/baremaps/data/type/WKBDataType.java b/baremaps-data/src/main/java/org/apache/baremaps/data/type/WKBDataType.java
index 1c3b3bfed..abd6c2ae1 100644
--- a/baremaps-data/src/main/java/org/apache/baremaps/data/type/WKBDataType.java
+++ b/baremaps-data/src/main/java/org/apache/baremaps/data/type/WKBDataType.java
@@ -17,19 +17,19 @@
 
 package org.apache.baremaps.data.type;
 
-
-
 import static org.locationtech.jts.io.WKBConstants.wkbNDR;
 
 import java.nio.ByteBuffer;
 import org.locationtech.jts.geom.Geometry;
 import org.locationtech.jts.geom.GeometryFactory;
-import org.locationtech.jts.geom.PrecisionModel;
 import org.locationtech.jts.io.ParseException;
 import org.locationtech.jts.io.WKBReader;
 import org.locationtech.jts.io.WKBWriter;
 
-/** A {@link DataType} for reading and writing {@link Geometry} in {@link ByteBuffer}s. */
+/**
+ * A {@link DataType} for reading and writing {@link Geometry} objects in Well-Known Binary (WKB)
+ * format in {@link ByteBuffer}s.
+ */
 public class WKBDataType implements DataType<Geometry> {
 
   /** {@inheritDoc} */
@@ -70,14 +70,11 @@ public class WKBDataType implements DataType<Geometry> {
     return deserialize(bytes);
   }
 
-  public static final GeometryFactory GEOMETRY_FACTORY_WGS84 =
-      new GeometryFactory(new PrecisionModel(), 4326);
-
   /**
-   * Serializes a geometry in the WKB format.
+   * Serializes a geometry into the WKB format.
    *
-   * @param geometry
-   * @return
+   * @param geometry the geometry to serialize
+   * @return the serialized geometry as a byte array, or null if the input is null
    */
   private static byte[] serialize(Geometry geometry) {
     if (geometry == null) {
@@ -88,10 +85,11 @@ public class WKBDataType implements DataType<Geometry> {
   }
 
   /**
-   * Deserializes a geometry in the WKB format.
+   * Deserializes a geometry from the WKB format.
    *
-   * @param wkb
-   * @return
+   * @param wkb the WKB byte array to deserialize
+   * @return the deserialized geometry, or null if the input is null
+   * @throws IllegalArgumentException if the WKB cannot be parsed
    */
   private static Geometry deserialize(byte[] wkb) {
     if (wkb == null) {
diff --git a/baremaps-data/src/test/java/org/apache/baremaps/data/AppendOnlyLogTest.java b/baremaps-data/src/test/java/org/apache/baremaps/data/AppendOnlyLogTest.java
index 96de22641..d9b354550 100644
--- a/baremaps-data/src/test/java/org/apache/baremaps/data/AppendOnlyLogTest.java
+++ b/baremaps-data/src/test/java/org/apache/baremaps/data/AppendOnlyLogTest.java
@@ -35,7 +35,10 @@ class AppendOnlyLogTest {
 
   @Test
   void addFixedSizeData() {
-    var collection = new AppendOnlyLog<>(new IntegerDataType(), new OffHeapMemory(1 << 10));
+    var collection = AppendOnlyLog.<Integer>builder()
+        .dataType(new IntegerDataType())
+        .memory(new OffHeapMemory(1 << 10))
+        .build();
     for (int i = 0; i < 1 << 20; i++) {
       assertEquals(Long.BYTES + (i << 2), collection.addPositioned(i));
     }
@@ -46,7 +49,10 @@ class AppendOnlyLogTest {
 
   @Test
   void addVariableSizeValues() {
-    var collection = new AppendOnlyLog<>(new IntegerListDataType(), new OffHeapMemory(1 << 10));
+    var collection = AppendOnlyLog.<ArrayList<Integer>>builder()
+        .dataType(new IntegerListDataType())
+        .memory(new OffHeapMemory(1 << 10))
+        .build();
     var random = new Random(0);
     var positions = new ArrayList<Long>();
     var values = new ArrayList<ArrayList<Integer>>();
@@ -69,7 +75,10 @@ class AppendOnlyLogTest {
   @MethodSource("org.apache.baremaps.data.type.DataTypeProvider#dataTypes")
   void testAllDataTypes(DataType dataType, Object value) {
     var num = 1000;
-    var collection = new AppendOnlyLog<>(dataType, new OffHeapMemory(1 << 22));
+    var collection = AppendOnlyLog.builder()
+        .dataType(dataType)
+        .memory(new OffHeapMemory(1 << 22))
+        .build();
 
     // write values
     for (int i = 0; i < num; i++) {
diff --git a/baremaps-data/src/test/java/org/apache/baremaps/data/DataListTest.java b/baremaps-data/src/test/java/org/apache/baremaps/data/DataListTest.java
index 63c1c21a2..6f697fba8 100644
--- a/baremaps-data/src/test/java/org/apache/baremaps/data/DataListTest.java
+++ b/baremaps-data/src/test/java/org/apache/baremaps/data/DataListTest.java
@@ -75,9 +75,19 @@ class DataListTest {
 
   static Stream<Arguments> listProvider() {
     return Stream.of(
-        Arguments.of(new FixedSizeDataList<>(new LongDataType())),
-        Arguments.of(new IndexedDataList<>(new MemoryAlignedDataList<>(new LongDataType()),
-            new AppendOnlyLog<>(new LongDataType()))),
-        Arguments.of(new MemoryAlignedDataList<>(new LongDataType())));
+        Arguments.of(FixedSizeDataList.<Long>builder()
+            .dataType(new LongDataType())
+            .build()),
+        Arguments.of(IndexedDataList.<Long>builder()
+            .index(MemoryAlignedDataList.<Long>builder()
+                .dataType(new LongDataType())
+                .build())
+            .values(AppendOnlyLog.<Long>builder()
+                .dataType(new LongDataType())
+                .build())
+            .build()),
+        Arguments.of(MemoryAlignedDataList.<Long>builder()
+            .dataType(new LongDataType())
+            .build()));
   }
 }
diff --git a/baremaps-data/src/test/java/org/apache/baremaps/data/DataMapTest.java b/baremaps-data/src/test/java/org/apache/baremaps/data/DataMapTest.java
index f62c79520..a66a16a67 100644
--- a/baremaps-data/src/test/java/org/apache/baremaps/data/DataMapTest.java
+++ b/baremaps-data/src/test/java/org/apache/baremaps/data/DataMapTest.java
@@ -165,16 +165,39 @@ class DataMapTest {
     return Stream
         .of(
             Arguments.of(
-                new IndexedDataMap<>(
-                    new AppendOnlyLog<>(new LongDataType(), new OffHeapMemory()))),
-            Arguments.of(new MonotonicFixedSizeDataMap<>(
-                new MemoryAlignedDataList<>(new LongDataType(), new OffHeapMemory()))),
-            Arguments.of(new MonotonicDataMap<>(
-                new MemoryAlignedDataList<>(
-                    new PairDataType<>(new LongDataType(), new LongDataType()),
-                    new OffHeapMemory()),
-                new AppendOnlyLog<>(new LongDataType(), new OffHeapMemory()))),
-            Arguments.of(new MonotonicPairedDataMap<>(new MemoryAlignedDataList<>(
-                new PairDataType<>(new LongDataType(), new LongDataType())))));
+                IndexedDataMap.<Long>builder()
+                    .values(AppendOnlyLog.<Long>builder()
+                        .dataType(new LongDataType())
+                        .memory(new OffHeapMemory())
+                        .build())
+                    .build()),
+            Arguments.of(MonotonicFixedSizeDataMap.<Long>builder()
+                .values(MemoryAlignedDataList.<Long>builder()
+                    .dataType(new LongDataType())
+                    .memory(new OffHeapMemory())
+                    .build())
+                .build()),
+            Arguments.of(MonotonicDataMap.<Long>builder()
+                .keys(MemoryAlignedDataList.<PairDataType.Pair<Long, Long>>builder()
+                    .dataType(new PairDataType<>(new LongDataType(), new LongDataType()))
+                    .memory(new OffHeapMemory())
+                    .build())
+                .values(AppendOnlyLog.<Long>builder()
+                    .dataType(new LongDataType())
+                    .memory(new OffHeapMemory())
+                    .build())
+                .build()),
+            Arguments.of(MonotonicPairedDataMap.<Long>builder()
+                .values(MemoryAlignedDataList.<PairDataType.Pair<Long, Long>>builder()
+                    .dataType(new PairDataType<>(new LongDataType(), new LongDataType()))
+                    .build())
+                .build()),
+            Arguments.of(DirectHashDataMap.<Long>builder()
+                .keyType(new LongDataType())
+                .dataType(new LongDataType())
+                .keyMemory(new OffHeapMemory())
+                .valueMemory(new OffHeapMemory())
+                .capacity(1000)
+                .build()));
   }
 }
diff --git a/baremaps-data/src/test/java/org/apache/baremaps/data/SizedDataListTest.java b/baremaps-data/src/test/java/org/apache/baremaps/data/SizedDataListTest.java
index f7ec72652..cb96ea10d 100644
--- a/baremaps-data/src/test/java/org/apache/baremaps/data/SizedDataListTest.java
+++ b/baremaps-data/src/test/java/org/apache/baremaps/data/SizedDataListTest.java
@@ -36,13 +36,19 @@ class SizedDataListTest {
   void segmentsTooSmall() {
     var dataType = new LongDataType();
     var memory = new OffHeapMemory(4);
-    assertThrows(DataCollectionException.class, () -> new FixedSizeDataList<>(dataType, memory));
+    assertThrows(DataCollectionException.class, () -> FixedSizeDataList.<Long>builder()
+        .dataType(dataType)
+        .memory(memory)
+        .build());
   }
 
   @ParameterizedTest
   @MethodSource("org.apache.baremaps.data.memory.MemoryProvider#memories")
   void appendFixedSizeValues(Memory memory) throws IOException {
-    var list = new FixedSizeDataList<>(new LongDataType(), memory);
+    var list = FixedSizeDataList.<Long>builder()
+        .dataType(new LongDataType())
+        .memory(memory)
+        .build();
     for (int i = 0; i < 1 << 10; i++) {
       assertEquals(i, list.addIndexed((long) i));
     }
diff --git a/baremaps-data/src/test/java/org/apache/baremaps/data/collection/IndexedDataMapTest.java b/baremaps-data/src/test/java/org/apache/baremaps/data/collection/IndexedDataMapTest.java
index 5eaa87fa8..666172923 100644
--- a/baremaps-data/src/test/java/org/apache/baremaps/data/collection/IndexedDataMapTest.java
+++ b/baremaps-data/src/test/java/org/apache/baremaps/data/collection/IndexedDataMapTest.java
@@ -37,7 +37,11 @@ class IndexedDataMapTest {
 
   @BeforeEach
   void setUp() {
-    map = new IndexedDataMap<>(new AppendOnlyLog<>(new IntegerDataType()));
+    map = IndexedDataMap.<Integer>builder()
+        .values(AppendOnlyLog.<Integer>builder()
+            .dataType(new IntegerDataType())
+            .build())
+        .build();
   }
 
   @AfterEach
diff --git a/baremaps-data/src/test/java/org/apache/baremaps/data/collection/MemoryAlignedDataMapTest.java b/baremaps-data/src/test/java/org/apache/baremaps/data/collection/MemoryAlignedDataMapTest.java
index 241cc4c26..912b2b26c 100644
--- a/baremaps-data/src/test/java/org/apache/baremaps/data/collection/MemoryAlignedDataMapTest.java
+++ b/baremaps-data/src/test/java/org/apache/baremaps/data/collection/MemoryAlignedDataMapTest.java
@@ -37,7 +37,10 @@ class MemoryAlignedDataMapTest {
 
   @BeforeEach
   void setUp() {
-    map = new MemoryAlignedDataMap<>(new IntegerDataType(), new OnHeapMemory(1024));
+    map = MemoryAlignedDataMap.<Integer>builder()
+        .dataType(new IntegerDataType())
+        .memory(new OnHeapMemory(1024))
+        .build();
   }
 
   @AfterEach
diff --git a/baremaps-data/src/test/java/org/apache/baremaps/data/sort/BinarySearchTest.java b/baremaps-data/src/test/java/org/apache/baremaps/data/sort/BinarySearchTest.java
index 3182d5b36..4d0f0af54 100644
--- a/baremaps-data/src/test/java/org/apache/baremaps/data/sort/BinarySearchTest.java
+++ b/baremaps-data/src/test/java/org/apache/baremaps/data/sort/BinarySearchTest.java
@@ -28,7 +28,9 @@ class BinarySearchTest {
 
   @Test
   void binarySearch() {
-    var list = new MemoryAlignedDataList<>(new LongDataType());
+    var list = MemoryAlignedDataList.<Long>builder()
+        .dataType(new LongDataType())
+        .build();
     for (long i = 0; i < 1000; i++) {
       list.add(i);
     }
diff --git a/baremaps-data/src/test/java/org/apache/baremaps/data/sort/ExternalMergeSortTest.java b/baremaps-data/src/test/java/org/apache/baremaps/data/sort/ExternalMergeSortTest.java
index 87b8ff345..0bb72050b 100644
--- a/baremaps-data/src/test/java/org/apache/baremaps/data/sort/ExternalMergeSortTest.java
+++ b/baremaps-data/src/test/java/org/apache/baremaps/data/sort/ExternalMergeSortTest.java
@@ -53,9 +53,16 @@ class ExternalMergeSortTest {
     stringsAsc = strings.stream().sorted(Comparator.naturalOrder()).toList();
     stringsDsc = strings.stream().sorted(Comparator.reverseOrder()).toList();
     stringsDistinct = stringsAsc.stream().distinct().toList();
-    supplier = () -> new IndexedDataList<>(
-        new MemoryAlignedDataList<>(new LongDataType(), new OnHeapMemory()),
-        new AppendOnlyLog<>(new StringDataType(), new OnHeapMemory()));
+    supplier = () -> IndexedDataList.<String>builder()
+        .index(MemoryAlignedDataList.<Long>builder()
+            .dataType(new LongDataType())
+            .memory(new OnHeapMemory())
+            .build())
+        .values(AppendOnlyLog.<String>builder()
+            .dataType(new StringDataType())
+            .memory(new OnHeapMemory())
+            .build())
+        .build();
     input = supplier.get();
     output = supplier.get();
     for (var string : strings) {