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

dataroaring pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 391412885f9 [Enhancement](memory) Add ConcurrentLong2ObjectHashMap and 
ConcurrentLong2LongHashMap (#61332)
391412885f9 is described below

commit 391412885f97dc8a59f4cbf251ef180ace00f45e
Author: Yongqiang YANG <[email protected]>
AuthorDate: Mon Mar 30 19:25:40 2026 -0700

    [Enhancement](memory) Add ConcurrentLong2ObjectHashMap and 
ConcurrentLong2LongHashMap (#61332)
    
    ## Summary
    
    Add two thread-safe primitive-key concurrent hash maps built on
    fastutil, designed as drop-in replacements for `ConcurrentHashMap<Long,
    V>` and `ConcurrentHashMap<Long, Long>` in memory-sensitive FE paths.
    
    - **`ConcurrentLong2ObjectHashMap<V>`** — replaces
    `ConcurrentHashMap<Long, V>`
    - **`ConcurrentLong2LongHashMap`** — replaces `ConcurrentHashMap<Long,
    Long>`
    
    ### Why
    
    `ConcurrentHashMap<Long, V>` costs ~64 bytes per entry due to Long
    boxing, Node wrapper, and segment overhead. These fastutil-based maps
    reduce that to ~16 bytes per entry — a **4x memory reduction**.
    
    In Doris FE, several critical data structures use
    `ConcurrentHashMap<Long, V>` at tablet/partition scale (millions of
    entries), making this a significant memory optimization opportunity.
    
    ### Design
    
    - **Segment-based locking** (default 16 segments) for concurrent
    throughput, similar to Java 7's ConcurrentHashMap design
    - Full `Map` interface compatibility for drop-in replacement
    - Atomic operations: `putIfAbsent`, `computeIfAbsent`, `replace`,
    `remove(key, value)`
    - Thread-safe iteration via snapshot-based
    `entrySet()`/`keySet()`/`values()`
    
    ### Memory comparison
    
    | Collection | Per-entry overhead | 1M entries |
    |------------|-------------------|------------|
    | `ConcurrentHashMap<Long, V>` | ~64 bytes | ~61 MB |
    | `ConcurrentLong2ObjectHashMap<V>` | ~16 bytes | ~15 MB |
    | `ConcurrentHashMap<Long, Long>` | ~80 bytes | ~76 MB |
    | `ConcurrentLong2LongHashMap` | ~16 bytes | ~15 MB |
    
    ## Test plan
    
    - [x] `ConcurrentLong2ObjectHashMapTest` — 432 lines covering
    put/get/remove, putIfAbsent, computeIfAbsent, replace, concurrent writes
    from multiple threads, iteration consistency, empty map edge cases
    - [x] `ConcurrentLong2LongHashMapTest` — 455 lines covering CRUD,
    default value semantics, concurrent operations, atomic operations,
    iteration, edge cases
    
    🤖 Generated with [Claude Code](https://claude.com/claude-code)
    
    ---------
    
    Co-authored-by: Claude Opus 4.6 <[email protected]>
---
 fe/fe-core/pom.xml                                 |   3 +-
 fe/fe-foundation/pom.xml                           |  14 +-
 .../util/ConcurrentLong2LongHashMap.java           | 526 +++++++++++++++++++++
 .../util/ConcurrentLong2ObjectHashMap.java         | 487 +++++++++++++++++++
 .../util/ConcurrentLong2LongHashMapTest.java       | 458 ++++++++++++++++++
 .../util/ConcurrentLong2ObjectHashMapTest.java     | 432 +++++++++++++++++
 fe/pom.xml                                         |   1 -
 7 files changed, 1917 insertions(+), 4 deletions(-)

diff --git a/fe/fe-core/pom.xml b/fe/fe-core/pom.xml
index 7a6feee5e79..9a826ad5083 100644
--- a/fe/fe-core/pom.xml
+++ b/fe/fe-core/pom.xml
@@ -790,7 +790,8 @@ under the License.
             <artifactId>mockito-inline</artifactId>
             <scope>test</scope>
         </dependency>
-        <!-- fastutil for memory-efficient primitive collections -->
+        <!-- fastutil-core: keep as direct dependency to ensure classpath 
priority
+             over the ancient unrelocated fastutil classes embedded in 
hive-catalog-shade -->
         <dependency>
             <groupId>it.unimi.dsi</groupId>
             <artifactId>fastutil-core</artifactId>
diff --git a/fe/fe-foundation/pom.xml b/fe/fe-foundation/pom.xml
index 2890ce4e43c..5486bf56c25 100644
--- a/fe/fe-foundation/pom.xml
+++ b/fe/fe-foundation/pom.xml
@@ -29,9 +29,19 @@ under the License.
     <artifactId>fe-foundation</artifactId>
     <packaging>jar</packaging>
     <name>Doris FE Foundation</name>
-    <description>Zero-dependency foundation utilities for Doris FE modules and 
SPI plugins</description>
+    <description>Foundation utilities for Doris FE modules and SPI 
plugins</description>
 
-    <!-- Intentionally NO dependencies. This module depends only on JDK. -->
+    <dependencies>
+        <dependency>
+            <groupId>it.unimi.dsi</groupId>
+            <artifactId>fastutil-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
 
     <build>
         <finalName>doris-fe-foundation</finalName>
diff --git 
a/fe/fe-foundation/src/main/java/org/apache/doris/foundation/util/ConcurrentLong2LongHashMap.java
 
b/fe/fe-foundation/src/main/java/org/apache/doris/foundation/util/ConcurrentLong2LongHashMap.java
new file mode 100644
index 00000000000..b62705712ea
--- /dev/null
+++ 
b/fe/fe-foundation/src/main/java/org/apache/doris/foundation/util/ConcurrentLong2LongHashMap.java
@@ -0,0 +1,526 @@
+// 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.doris.foundation.util;
+
+import it.unimi.dsi.fastutil.longs.AbstractLong2LongMap;
+import it.unimi.dsi.fastutil.longs.Long2LongFunction;
+import it.unimi.dsi.fastutil.longs.Long2LongMap;
+import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap;
+import it.unimi.dsi.fastutil.longs.LongArrayList;
+import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
+import it.unimi.dsi.fastutil.longs.LongSet;
+import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
+import it.unimi.dsi.fastutil.objects.ObjectSet;
+
+import java.util.Map;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.function.LongBinaryOperator;
+import java.util.function.LongUnaryOperator;
+
+/**
+ * A concurrent map with primitive long keys and primitive long values, backed 
by segmented
+ * {@link Long2LongOpenHashMap} instances with {@link ReentrantReadWriteLock} 
per segment.
+ *
+ * <p>This class saves ~48 bytes per entry compared to {@code 
ConcurrentHashMap<Long, Long>}
+ * by avoiding boxing of both keys and values. For fields like partition 
update row counts
+ * with millions of entries, this translates to hundreds of MB of heap savings.
+ *
+ * <p>The {@link #addTo(long, long)} method provides atomic increment 
semantics, useful for
+ * counter patterns.
+ *
+ * <p><b>Note:</b> The {@code defaultReturnValue} is fixed at 0. Calling
+ * {@link #defaultReturnValue(long)} will throw {@link 
UnsupportedOperationException}
+ * because it cannot be propagated to the underlying segment maps consistently.
+ *
+ * <p><b>Important:</b> All compound operations from both {@link Long2LongMap} 
and {@link Map}
+ * interfaces (computeIfAbsent, computeIfPresent, compute, merge, mergeLong, 
putIfAbsent,
+ * replace, remove) are overridden to ensure atomicity within a segment.
+ */
+public class ConcurrentLong2LongHashMap extends AbstractLong2LongMap {
+
+    private static final int DEFAULT_SEGMENT_COUNT = 16;
+    private static final int DEFAULT_INITIAL_CAPACITY_PER_SEGMENT = 16;
+
+    private final Segment[] segments;
+    private final int segmentMask;
+    private final int segmentBits;
+
+    public ConcurrentLong2LongHashMap() {
+        this(DEFAULT_SEGMENT_COUNT);
+    }
+
+    public ConcurrentLong2LongHashMap(int segmentCount) {
+        if (segmentCount <= 0 || (segmentCount & (segmentCount - 1)) != 0) {
+            throw new IllegalArgumentException("segmentCount must be a 
positive power of 2: " + segmentCount);
+        }
+        this.segmentBits = Integer.numberOfTrailingZeros(segmentCount);
+        this.segmentMask = segmentCount - 1;
+        this.segments = new Segment[segmentCount];
+        for (int i = 0; i < segmentCount; i++) {
+            segments[i] = new Segment(DEFAULT_INITIAL_CAPACITY_PER_SEGMENT);
+        }
+    }
+
+    @Override
+    public void defaultReturnValue(long rv) {
+        throw new UnsupportedOperationException(
+                "ConcurrentLong2LongHashMap does not support changing 
defaultReturnValue. "
+                + "It is fixed at 0.");
+    }
+
+    /** Murmur3 64-bit finalizer for segment selection. */
+    private static long mix(long x) {
+        x ^= x >>> 33;
+        x *= 0xff51afd7ed558ccdL;
+        x ^= x >>> 33;
+        x *= 0xc4ceb9fe1a85ec53L;
+        x ^= x >>> 33;
+        return x;
+    }
+
+    private Segment segmentFor(long key) {
+        return segments[(int) (mix(key) >>> (64 - segmentBits)) & segmentMask];
+    }
+
+    // ---- Read operations (read-lock) ----
+
+    @Override
+    public long get(long key) {
+        Segment seg = segmentFor(key);
+        seg.lock.readLock().lock();
+        try {
+            return seg.map.get(key);
+        } finally {
+            seg.lock.readLock().unlock();
+        }
+    }
+
+    public long getOrDefault(long key, long defaultValue) {
+        Segment seg = segmentFor(key);
+        seg.lock.readLock().lock();
+        try {
+            return seg.map.containsKey(key) ? seg.map.get(key) : defaultValue;
+        } finally {
+            seg.lock.readLock().unlock();
+        }
+    }
+
+    @Override
+    public boolean containsKey(long key) {
+        Segment seg = segmentFor(key);
+        seg.lock.readLock().lock();
+        try {
+            return seg.map.containsKey(key);
+        } finally {
+            seg.lock.readLock().unlock();
+        }
+    }
+
+    @Override
+    public boolean containsValue(long value) {
+        for (Segment seg : segments) {
+            seg.lock.readLock().lock();
+            try {
+                if (seg.map.containsValue(value)) {
+                    return true;
+                }
+            } finally {
+                seg.lock.readLock().unlock();
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public int size() {
+        long total = 0;
+        for (Segment seg : segments) {
+            seg.lock.readLock().lock();
+            try {
+                total += seg.map.size();
+            } finally {
+                seg.lock.readLock().unlock();
+            }
+        }
+        return (int) Math.min(total, Integer.MAX_VALUE);
+    }
+
+    @Override
+    public boolean isEmpty() {
+        for (Segment seg : segments) {
+            seg.lock.readLock().lock();
+            try {
+                if (!seg.map.isEmpty()) {
+                    return false;
+                }
+            } finally {
+                seg.lock.readLock().unlock();
+            }
+        }
+        return true;
+    }
+
+    // ---- Write operations (write-lock) ----
+
+    @Override
+    public long put(long key, long value) {
+        Segment seg = segmentFor(key);
+        seg.lock.writeLock().lock();
+        try {
+            return seg.map.put(key, value);
+        } finally {
+            seg.lock.writeLock().unlock();
+        }
+    }
+
+    @Override
+    public long remove(long key) {
+        Segment seg = segmentFor(key);
+        seg.lock.writeLock().lock();
+        try {
+            return seg.map.remove(key);
+        } finally {
+            seg.lock.writeLock().unlock();
+        }
+    }
+
+    public long putIfAbsent(long key, long value) {
+        Segment seg = segmentFor(key);
+        seg.lock.writeLock().lock();
+        try {
+            if (seg.map.containsKey(key)) {
+                return seg.map.get(key);
+            }
+            seg.map.put(key, value);
+            return defaultReturnValue();
+        } finally {
+            seg.lock.writeLock().unlock();
+        }
+    }
+
+    public boolean replace(long key, long oldValue, long newValue) {
+        Segment seg = segmentFor(key);
+        seg.lock.writeLock().lock();
+        try {
+            if (seg.map.containsKey(key) && seg.map.get(key) == oldValue) {
+                seg.map.put(key, newValue);
+                return true;
+            }
+            return false;
+        } finally {
+            seg.lock.writeLock().unlock();
+        }
+    }
+
+    public long replace(long key, long value) {
+        Segment seg = segmentFor(key);
+        seg.lock.writeLock().lock();
+        try {
+            if (seg.map.containsKey(key)) {
+                return seg.map.put(key, value);
+            }
+            return defaultReturnValue();
+        } finally {
+            seg.lock.writeLock().unlock();
+        }
+    }
+
+    @Override
+    public boolean remove(Object key, Object value) {
+        if (!(key instanceof Long) || !(value instanceof Long)) {
+            return false;
+        }
+        long k = (Long) key;
+        long v = (Long) value;
+        Segment seg = segmentFor(k);
+        seg.lock.writeLock().lock();
+        try {
+            if (!seg.map.containsKey(k) || seg.map.get(k) != v) {
+                return false;
+            }
+            seg.map.remove(k);
+            return true;
+        } finally {
+            seg.lock.writeLock().unlock();
+        }
+    }
+
+    @Override
+    public void clear() {
+        for (Segment seg : segments) {
+            seg.lock.writeLock().lock();
+            try {
+                seg.map.clear();
+            } finally {
+                seg.lock.writeLock().unlock();
+            }
+        }
+    }
+
+    @Override
+    public void putAll(Map<? extends Long, ? extends Long> m) {
+        for (Map.Entry<? extends Long, ? extends Long> entry : m.entrySet()) {
+            put(entry.getKey().longValue(), entry.getValue().longValue());
+        }
+    }
+
+    // ---- Atomic compound operations ----
+
+    /**
+     * Atomically adds the given increment to the value associated with the 
key.
+     * If the key is not present, the entry is created with the increment as 
value
+     * (starting from defaultReturnValue, which is 0L by default).
+     *
+     * @return the new value after the increment
+     */
+    public long addTo(long key, long increment) {
+        Segment seg = segmentFor(key);
+        seg.lock.writeLock().lock();
+        try {
+            long newValue = seg.map.addTo(key, increment) + increment;
+            return newValue;
+        } finally {
+            seg.lock.writeLock().unlock();
+        }
+    }
+
+    public long computeIfAbsent(long key, LongUnaryOperator mappingFunction) {
+        Segment seg = segmentFor(key);
+        seg.lock.writeLock().lock();
+        try {
+            if (seg.map.containsKey(key)) {
+                return seg.map.get(key);
+            }
+            long newValue = mappingFunction.applyAsLong(key);
+            seg.map.put(key, newValue);
+            return newValue;
+        } finally {
+            seg.lock.writeLock().unlock();
+        }
+    }
+
+    public long computeIfAbsent(long key, Long2LongFunction mappingFunction) {
+        Segment seg = segmentFor(key);
+        seg.lock.writeLock().lock();
+        try {
+            if (seg.map.containsKey(key)) {
+                return seg.map.get(key);
+            }
+            long newValue = mappingFunction.get(key);
+            seg.map.put(key, newValue);
+            return newValue;
+        } finally {
+            seg.lock.writeLock().unlock();
+        }
+    }
+
+    @Override
+    public Long computeIfAbsent(Long key, Function<? super Long, ? extends 
Long> mappingFunction) {
+        long k = key.longValue();
+        Segment seg = segmentFor(k);
+        seg.lock.writeLock().lock();
+        try {
+            if (seg.map.containsKey(k)) {
+                return seg.map.get(k);
+            }
+            Long newValue = mappingFunction.apply(key);
+            if (newValue != null) {
+                seg.map.put(k, newValue.longValue());
+            }
+            return newValue;
+        } finally {
+            seg.lock.writeLock().unlock();
+        }
+    }
+
+    public long computeIfPresent(long key,
+            BiFunction<? super Long, ? super Long, ? extends Long> 
remappingFunction) {
+        Segment seg = segmentFor(key);
+        seg.lock.writeLock().lock();
+        try {
+            if (!seg.map.containsKey(key)) {
+                return defaultReturnValue();
+            }
+            long oldValue = seg.map.get(key);
+            Long newValue = remappingFunction.apply(key, oldValue);
+            if (newValue != null) {
+                seg.map.put(key, newValue.longValue());
+                return newValue;
+            } else {
+                seg.map.remove(key);
+                return defaultReturnValue();
+            }
+        } finally {
+            seg.lock.writeLock().unlock();
+        }
+    }
+
+    public long compute(long key, BiFunction<? super Long, ? super Long, ? 
extends Long> remappingFunction) {
+        Segment seg = segmentFor(key);
+        seg.lock.writeLock().lock();
+        try {
+            Long oldValue = seg.map.containsKey(key) ? seg.map.get(key) : null;
+            Long newValue = remappingFunction.apply(key, oldValue);
+            if (newValue != null) {
+                seg.map.put(key, newValue.longValue());
+                return newValue;
+            } else if (oldValue != null) {
+                seg.map.remove(key);
+            }
+            return defaultReturnValue();
+        } finally {
+            seg.lock.writeLock().unlock();
+        }
+    }
+
+    public long merge(long key, long value,
+            BiFunction<? super Long, ? super Long, ? extends Long> 
remappingFunction) {
+        Segment seg = segmentFor(key);
+        seg.lock.writeLock().lock();
+        try {
+            if (!seg.map.containsKey(key)) {
+                seg.map.put(key, value);
+                return value;
+            }
+            long oldValue = seg.map.get(key);
+            Long newValue = remappingFunction.apply(oldValue, value);
+            if (newValue != null) {
+                seg.map.put(key, newValue.longValue());
+                return newValue;
+            } else {
+                seg.map.remove(key);
+                return defaultReturnValue();
+            }
+        } finally {
+            seg.lock.writeLock().unlock();
+        }
+    }
+
+    public long mergeLong(long key, long value, LongBinaryOperator 
remappingFunction) {
+        Segment seg = segmentFor(key);
+        seg.lock.writeLock().lock();
+        try {
+            if (!seg.map.containsKey(key)) {
+                seg.map.put(key, value);
+                return value;
+            }
+            long oldValue = seg.map.get(key);
+            long newValue = remappingFunction.applyAsLong(oldValue, value);
+            seg.map.put(key, newValue);
+            return newValue;
+        } finally {
+            seg.lock.writeLock().unlock();
+        }
+    }
+
+    // ---- Iteration (weakly consistent snapshots) ----
+
+    @Override
+    public ObjectSet<Long2LongMap.Entry> long2LongEntrySet() {
+        ObjectOpenHashSet<Long2LongMap.Entry> snapshot = new 
ObjectOpenHashSet<>();
+        for (Segment seg : segments) {
+            seg.lock.readLock().lock();
+            try {
+                for (Long2LongMap.Entry entry : seg.map.long2LongEntrySet()) {
+                    snapshot.add(new 
AbstractLong2LongMap.BasicEntry(entry.getLongKey(), entry.getLongValue()));
+                }
+            } finally {
+                seg.lock.readLock().unlock();
+            }
+        }
+        return snapshot;
+    }
+
+    @Override
+    public LongSet keySet() {
+        LongOpenHashSet snapshot = new LongOpenHashSet();
+        for (Segment seg : segments) {
+            seg.lock.readLock().lock();
+            try {
+                snapshot.addAll(seg.map.keySet());
+            } finally {
+                seg.lock.readLock().unlock();
+            }
+        }
+        return snapshot;
+    }
+
+    /**
+     * Returns the keys as a {@link LongArrayList}.
+     */
+    public LongArrayList keyList() {
+        LongArrayList list = new LongArrayList(size());
+        for (Segment seg : segments) {
+            seg.lock.readLock().lock();
+            try {
+                list.addAll(seg.map.keySet());
+            } finally {
+                seg.lock.readLock().unlock();
+            }
+        }
+        return list;
+    }
+
+    @Override
+    public it.unimi.dsi.fastutil.longs.LongCollection values() {
+        LongArrayList snapshot = new LongArrayList();
+        for (Segment seg : segments) {
+            seg.lock.readLock().lock();
+            try {
+                snapshot.addAll(seg.map.values());
+            } finally {
+                seg.lock.readLock().unlock();
+            }
+        }
+        return snapshot;
+    }
+
+    /**
+     * Applies the given action to each entry under read-lock per segment.
+     */
+    public void forEach(LongLongConsumer action) {
+        for (Segment seg : segments) {
+            seg.lock.readLock().lock();
+            try {
+                for (Long2LongMap.Entry entry : seg.map.long2LongEntrySet()) {
+                    action.accept(entry.getLongKey(), entry.getLongValue());
+                }
+            } finally {
+                seg.lock.readLock().unlock();
+            }
+        }
+    }
+
+    @FunctionalInterface
+    public interface LongLongConsumer {
+        void accept(long key, long value);
+    }
+
+    // ---- Segment inner class ----
+
+    private static final class Segment {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+        final Long2LongOpenHashMap map;
+
+        Segment(int initialCapacity) {
+            this.map = new Long2LongOpenHashMap(initialCapacity);
+        }
+    }
+}
diff --git 
a/fe/fe-foundation/src/main/java/org/apache/doris/foundation/util/ConcurrentLong2ObjectHashMap.java
 
b/fe/fe-foundation/src/main/java/org/apache/doris/foundation/util/ConcurrentLong2ObjectHashMap.java
new file mode 100644
index 00000000000..eab243776bc
--- /dev/null
+++ 
b/fe/fe-foundation/src/main/java/org/apache/doris/foundation/util/ConcurrentLong2ObjectHashMap.java
@@ -0,0 +1,487 @@
+// 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.doris.foundation.util;
+
+import it.unimi.dsi.fastutil.longs.AbstractLong2ObjectMap;
+import it.unimi.dsi.fastutil.longs.Long2ObjectFunction;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
+import it.unimi.dsi.fastutil.longs.LongArrayList;
+import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
+import it.unimi.dsi.fastutil.longs.LongSet;
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
+import it.unimi.dsi.fastutil.objects.ObjectCollection;
+import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
+import it.unimi.dsi.fastutil.objects.ObjectSet;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.function.LongFunction;
+
+/**
+ * A concurrent map with primitive long keys and object values, backed by 
segmented
+ * {@link Long2ObjectOpenHashMap} instances with {@link 
ReentrantReadWriteLock} per segment.
+ *
+ * <p>This class provides similar concurrency guarantees to {@link 
java.util.concurrent.ConcurrentHashMap}
+ * while avoiding the memory overhead of boxing long keys. For a cluster with 
millions of tablet entries,
+ * this saves ~32 bytes per entry compared to {@code ConcurrentHashMap<Long, 
V>}.
+ *
+ * <p>Like {@link java.util.concurrent.ConcurrentHashMap}, null values are not 
permitted.
+ *
+ * <p>Iteration methods ({@link #long2ObjectEntrySet()}, {@link #keySet()}, 
{@link #values()})
+ * return snapshot copies and are weakly consistent.
+ *
+ * <p><b>Important:</b> All compound operations from both {@link 
Long2ObjectMap} and {@link Map}
+ * interfaces (computeIfAbsent, computeIfPresent, compute, merge, putIfAbsent, 
replace, remove)
+ * are overridden to ensure atomicity within a segment.
+ *
+ * @param <V> the type of mapped values
+ */
+public class ConcurrentLong2ObjectHashMap<V> extends AbstractLong2ObjectMap<V> 
{
+
+    private static final int DEFAULT_SEGMENT_COUNT = 16;
+    private static final int DEFAULT_INITIAL_CAPACITY_PER_SEGMENT = 16;
+
+    private final Segment<V>[] segments;
+    private final int segmentMask;
+    private final int segmentBits;
+
+    public ConcurrentLong2ObjectHashMap() {
+        this(DEFAULT_SEGMENT_COUNT);
+    }
+
+    @SuppressWarnings("unchecked")
+    public ConcurrentLong2ObjectHashMap(int segmentCount) {
+        if (segmentCount <= 0 || (segmentCount & (segmentCount - 1)) != 0) {
+            throw new IllegalArgumentException("segmentCount must be a 
positive power of 2: " + segmentCount);
+        }
+        this.segmentBits = Integer.numberOfTrailingZeros(segmentCount);
+        this.segmentMask = segmentCount - 1;
+        this.segments = new Segment[segmentCount];
+        for (int i = 0; i < segmentCount; i++) {
+            segments[i] = new Segment<>(DEFAULT_INITIAL_CAPACITY_PER_SEGMENT);
+        }
+    }
+
+    /** Murmur3 64-bit finalizer for segment selection. */
+    private static long mix(long x) {
+        x ^= x >>> 33;
+        x *= 0xff51afd7ed558ccdL;
+        x ^= x >>> 33;
+        x *= 0xc4ceb9fe1a85ec53L;
+        x ^= x >>> 33;
+        return x;
+    }
+
+    private Segment<V> segmentFor(long key) {
+        return segments[(int) (mix(key) >>> (64 - segmentBits)) & segmentMask];
+    }
+
+    // ---- Read operations (read-lock) ----
+
+    @Override
+    public V get(long key) {
+        Segment<V> seg = segmentFor(key);
+        seg.lock.readLock().lock();
+        try {
+            return seg.map.get(key);
+        } finally {
+            seg.lock.readLock().unlock();
+        }
+    }
+
+    public V getOrDefault(long key, V defaultValue) {
+        Segment<V> seg = segmentFor(key);
+        seg.lock.readLock().lock();
+        try {
+            V val = seg.map.get(key);
+            return (val != null || seg.map.containsKey(key)) ? val : 
defaultValue;
+        } finally {
+            seg.lock.readLock().unlock();
+        }
+    }
+
+    @Override
+    public boolean containsKey(long key) {
+        Segment<V> seg = segmentFor(key);
+        seg.lock.readLock().lock();
+        try {
+            return seg.map.containsKey(key);
+        } finally {
+            seg.lock.readLock().unlock();
+        }
+    }
+
+    @Override
+    public boolean containsValue(Object value) {
+        for (Segment<V> seg : segments) {
+            seg.lock.readLock().lock();
+            try {
+                if (seg.map.containsValue(value)) {
+                    return true;
+                }
+            } finally {
+                seg.lock.readLock().unlock();
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public int size() {
+        long total = 0;
+        for (Segment<V> seg : segments) {
+            seg.lock.readLock().lock();
+            try {
+                total += seg.map.size();
+            } finally {
+                seg.lock.readLock().unlock();
+            }
+        }
+        return (int) Math.min(total, Integer.MAX_VALUE);
+    }
+
+    @Override
+    public boolean isEmpty() {
+        for (Segment<V> seg : segments) {
+            seg.lock.readLock().lock();
+            try {
+                if (!seg.map.isEmpty()) {
+                    return false;
+                }
+            } finally {
+                seg.lock.readLock().unlock();
+            }
+        }
+        return true;
+    }
+
+    // ---- Write operations (write-lock) ----
+
+    @Override
+    public V put(long key, V value) {
+        Objects.requireNonNull(value, "Null values are not permitted");
+        Segment<V> seg = segmentFor(key);
+        seg.lock.writeLock().lock();
+        try {
+            return seg.map.put(key, value);
+        } finally {
+            seg.lock.writeLock().unlock();
+        }
+    }
+
+    @Override
+    public V remove(long key) {
+        Segment<V> seg = segmentFor(key);
+        seg.lock.writeLock().lock();
+        try {
+            return seg.map.remove(key);
+        } finally {
+            seg.lock.writeLock().unlock();
+        }
+    }
+
+    public V putIfAbsent(long key, V value) {
+        Objects.requireNonNull(value, "Null values are not permitted");
+        Segment<V> seg = segmentFor(key);
+        seg.lock.writeLock().lock();
+        try {
+            V existing = seg.map.get(key);
+            if (existing != null || seg.map.containsKey(key)) {
+                return existing;
+            }
+            seg.map.put(key, value);
+            return null;
+        } finally {
+            seg.lock.writeLock().unlock();
+        }
+    }
+
+    public boolean replace(long key, V oldValue, V newValue) {
+        Objects.requireNonNull(newValue, "Null values are not permitted");
+        Segment<V> seg = segmentFor(key);
+        seg.lock.writeLock().lock();
+        try {
+            V curValue = seg.map.get(key);
+            if (!Objects.equals(curValue, oldValue) || (curValue == null && 
!seg.map.containsKey(key))) {
+                return false;
+            }
+            seg.map.put(key, newValue);
+            return true;
+        } finally {
+            seg.lock.writeLock().unlock();
+        }
+    }
+
+    public V replace(long key, V value) {
+        Objects.requireNonNull(value, "Null values are not permitted");
+        Segment<V> seg = segmentFor(key);
+        seg.lock.writeLock().lock();
+        try {
+            if (seg.map.containsKey(key)) {
+                return seg.map.put(key, value);
+            }
+            return null;
+        } finally {
+            seg.lock.writeLock().unlock();
+        }
+    }
+
+    @Override
+    public boolean remove(Object key, Object value) {
+        if (!(key instanceof Long)) {
+            return false;
+        }
+        long k = (Long) key;
+        Segment<V> seg = segmentFor(k);
+        seg.lock.writeLock().lock();
+        try {
+            V curValue = seg.map.get(k);
+            if (!Objects.equals(curValue, value) || (curValue == null && 
!seg.map.containsKey(k))) {
+                return false;
+            }
+            seg.map.remove(k);
+            return true;
+        } finally {
+            seg.lock.writeLock().unlock();
+        }
+    }
+
+    @Override
+    public void clear() {
+        for (Segment<V> seg : segments) {
+            seg.lock.writeLock().lock();
+            try {
+                seg.map.clear();
+            } finally {
+                seg.lock.writeLock().unlock();
+            }
+        }
+    }
+
+    @Override
+    public void putAll(Map<? extends Long, ? extends V> m) {
+        for (Map.Entry<? extends Long, ? extends V> entry : m.entrySet()) {
+            put(entry.getKey().longValue(), entry.getValue());
+        }
+    }
+
+    // ---- Atomic compound operations ----
+    // Override ALL compound methods from both Long2ObjectMap and Map 
interfaces
+    // to ensure the check-then-act is atomic within a segment's write lock.
+
+    public V computeIfAbsent(long key, LongFunction<? extends V> 
mappingFunction) {
+        Segment<V> seg = segmentFor(key);
+        seg.lock.writeLock().lock();
+        try {
+            V val = seg.map.get(key);
+            if (val != null || seg.map.containsKey(key)) {
+                return val;
+            }
+            V newValue = mappingFunction.apply(key);
+            if (newValue != null) {
+                seg.map.put(key, newValue);
+            }
+            return newValue;
+        } finally {
+            seg.lock.writeLock().unlock();
+        }
+    }
+
+    public V computeIfAbsent(long key, Long2ObjectFunction<? extends V> 
mappingFunction) {
+        Segment<V> seg = segmentFor(key);
+        seg.lock.writeLock().lock();
+        try {
+            V val = seg.map.get(key);
+            if (val != null || seg.map.containsKey(key)) {
+                return val;
+            }
+            V newValue = mappingFunction.get(key);
+            if (newValue != null) {
+                seg.map.put(key, newValue);
+            }
+            return newValue;
+        } finally {
+            seg.lock.writeLock().unlock();
+        }
+    }
+
+    @Override
+    public V computeIfAbsent(Long key, Function<? super Long, ? extends V> 
mappingFunction) {
+        return computeIfAbsent(key.longValue(), (long k) -> 
mappingFunction.apply(k));
+    }
+
+    public V computeIfPresent(long key, BiFunction<? super Long, ? super V, ? 
extends V> remappingFunction) {
+        Segment<V> seg = segmentFor(key);
+        seg.lock.writeLock().lock();
+        try {
+            V oldValue = seg.map.get(key);
+            if (oldValue != null || seg.map.containsKey(key)) {
+                V newValue = remappingFunction.apply(key, oldValue);
+                if (newValue != null) {
+                    seg.map.put(key, newValue);
+                } else {
+                    seg.map.remove(key);
+                }
+                return newValue;
+            }
+            return null;
+        } finally {
+            seg.lock.writeLock().unlock();
+        }
+    }
+
+    public V compute(long key, BiFunction<? super Long, ? super V, ? extends 
V> remappingFunction) {
+        Segment<V> seg = segmentFor(key);
+        seg.lock.writeLock().lock();
+        try {
+            V oldValue = seg.map.containsKey(key) ? seg.map.get(key) : null;
+            V newValue = remappingFunction.apply(key, oldValue);
+            if (newValue != null) {
+                seg.map.put(key, newValue);
+            } else if (seg.map.containsKey(key)) {
+                seg.map.remove(key);
+            }
+            return newValue;
+        } finally {
+            seg.lock.writeLock().unlock();
+        }
+    }
+
+    public V merge(long key, V value, BiFunction<? super V, ? super V, ? 
extends V> remappingFunction) {
+        Segment<V> seg = segmentFor(key);
+        seg.lock.writeLock().lock();
+        try {
+            V oldValue = seg.map.get(key);
+            V newValue;
+            if (oldValue != null || seg.map.containsKey(key)) {
+                newValue = remappingFunction.apply(oldValue, value);
+            } else {
+                newValue = value;
+            }
+            if (newValue != null) {
+                seg.map.put(key, newValue);
+            } else {
+                seg.map.remove(key);
+            }
+            return newValue;
+        } finally {
+            seg.lock.writeLock().unlock();
+        }
+    }
+
+    // ---- Iteration (weakly consistent snapshots) ----
+
+    @Override
+    public ObjectSet<Long2ObjectMap.Entry<V>> long2ObjectEntrySet() {
+        ObjectOpenHashSet<Long2ObjectMap.Entry<V>> snapshot = new 
ObjectOpenHashSet<>();
+        for (Segment<V> seg : segments) {
+            seg.lock.readLock().lock();
+            try {
+                for (Long2ObjectMap.Entry<V> entry : 
seg.map.long2ObjectEntrySet()) {
+                    snapshot.add(new 
AbstractLong2ObjectMap.BasicEntry<>(entry.getLongKey(), entry.getValue()));
+                }
+            } finally {
+                seg.lock.readLock().unlock();
+            }
+        }
+        return snapshot;
+    }
+
+    @Override
+    public LongSet keySet() {
+        LongOpenHashSet snapshot = new LongOpenHashSet();
+        for (Segment<V> seg : segments) {
+            seg.lock.readLock().lock();
+            try {
+                snapshot.addAll(seg.map.keySet());
+            } finally {
+                seg.lock.readLock().unlock();
+            }
+        }
+        return snapshot;
+    }
+
+    /**
+     * Returns the keys as a {@link LongArrayList}. Useful when callers need 
indexed access
+     * or will iterate the keys once. Snapshot-based and weakly consistent.
+     */
+    public LongArrayList keyList() {
+        LongArrayList list = new LongArrayList(size());
+        for (Segment<V> seg : segments) {
+            seg.lock.readLock().lock();
+            try {
+                list.addAll(seg.map.keySet());
+            } finally {
+                seg.lock.readLock().unlock();
+            }
+        }
+        return list;
+    }
+
+    @Override
+    public ObjectCollection<V> values() {
+        ObjectArrayList<V> snapshot = new ObjectArrayList<>();
+        for (Segment<V> seg : segments) {
+            seg.lock.readLock().lock();
+            try {
+                snapshot.addAll(seg.map.values());
+            } finally {
+                seg.lock.readLock().unlock();
+            }
+        }
+        return snapshot;
+    }
+
+    /**
+     * Applies the given action to each entry under read-lock per segment.
+     * This is more efficient than iterating {@link #long2ObjectEntrySet()} as 
it avoids
+     * creating a snapshot.
+     */
+    public void forEach(LongObjConsumer<? super V> action) {
+        for (Segment<V> seg : segments) {
+            seg.lock.readLock().lock();
+            try {
+                for (Long2ObjectMap.Entry<V> entry : 
seg.map.long2ObjectEntrySet()) {
+                    action.accept(entry.getLongKey(), entry.getValue());
+                }
+            } finally {
+                seg.lock.readLock().unlock();
+            }
+        }
+    }
+
+    @FunctionalInterface
+    public interface LongObjConsumer<V> {
+        void accept(long key, V value);
+    }
+
+    // ---- Segment inner class ----
+
+    private static final class Segment<V> {
+        final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+        final Long2ObjectOpenHashMap<V> map;
+
+        Segment(int initialCapacity) {
+            this.map = new Long2ObjectOpenHashMap<>(initialCapacity);
+        }
+    }
+}
diff --git 
a/fe/fe-foundation/src/test/java/org/apache/doris/foundation/util/ConcurrentLong2LongHashMapTest.java
 
b/fe/fe-foundation/src/test/java/org/apache/doris/foundation/util/ConcurrentLong2LongHashMapTest.java
new file mode 100644
index 00000000000..162a1817253
--- /dev/null
+++ 
b/fe/fe-foundation/src/test/java/org/apache/doris/foundation/util/ConcurrentLong2LongHashMapTest.java
@@ -0,0 +1,458 @@
+// 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.doris.foundation.util;
+
+import com.google.gson.Gson;
+import it.unimi.dsi.fastutil.longs.Long2LongMap;
+import it.unimi.dsi.fastutil.longs.LongSet;
+import it.unimi.dsi.fastutil.objects.ObjectSet;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicInteger;
+
+class ConcurrentLong2LongHashMapTest {
+
+    @Test
+    void testPutAndGet() {
+        ConcurrentLong2LongHashMap map = new ConcurrentLong2LongHashMap();
+        map.put(1L, 100L);
+        Assertions.assertEquals(100L, map.get(1L));
+        map.put(1L, 200L);
+        Assertions.assertEquals(200L, map.get(1L));
+    }
+
+    @Test
+    void testGetMissingKeyReturnsDefaultReturnValue() {
+        ConcurrentLong2LongHashMap map = new ConcurrentLong2LongHashMap();
+        // Default return value is 0L
+        Assertions.assertEquals(0L, map.get(999L));
+    }
+
+    @Test
+    void testGetOrDefault() {
+        ConcurrentLong2LongHashMap map = new ConcurrentLong2LongHashMap();
+        map.put(1L, 100L);
+        Assertions.assertEquals(100L, map.getOrDefault(1L, -1L));
+        Assertions.assertEquals(-1L, map.getOrDefault(2L, -1L));
+    }
+
+    @Test
+    void testRemove() {
+        ConcurrentLong2LongHashMap map = new ConcurrentLong2LongHashMap();
+        map.put(1L, 100L);
+        Assertions.assertEquals(100L, map.remove(1L));
+        Assertions.assertFalse(map.containsKey(1L));
+        // Remove non-existent key returns defaultReturnValue
+        Assertions.assertEquals(0L, map.remove(1L));
+    }
+
+    @Test
+    void testContainsKey() {
+        ConcurrentLong2LongHashMap map = new ConcurrentLong2LongHashMap();
+        Assertions.assertFalse(map.containsKey(1L));
+        map.put(1L, 0L);
+        Assertions.assertTrue(map.containsKey(1L));
+    }
+
+    @Test
+    void testContainsValue() {
+        ConcurrentLong2LongHashMap map = new ConcurrentLong2LongHashMap();
+        map.put(1L, 100L);
+        map.put(2L, 200L);
+        Assertions.assertTrue(map.containsValue(100L));
+        Assertions.assertFalse(map.containsValue(300L));
+    }
+
+    @Test
+    void testSizeAndIsEmpty() {
+        ConcurrentLong2LongHashMap map = new ConcurrentLong2LongHashMap();
+        Assertions.assertTrue(map.isEmpty());
+        Assertions.assertEquals(0, map.size());
+        map.put(1L, 100L);
+        map.put(2L, 200L);
+        Assertions.assertFalse(map.isEmpty());
+        Assertions.assertEquals(2, map.size());
+    }
+
+    @Test
+    void testClear() {
+        ConcurrentLong2LongHashMap map = new ConcurrentLong2LongHashMap();
+        map.put(1L, 100L);
+        map.put(2L, 200L);
+        map.clear();
+        Assertions.assertTrue(map.isEmpty());
+        Assertions.assertEquals(0, map.size());
+    }
+
+    @Test
+    void testPutAll() {
+        ConcurrentLong2LongHashMap map = new ConcurrentLong2LongHashMap();
+        Map<Long, Long> source = new HashMap<>();
+        source.put(1L, 100L);
+        source.put(2L, 200L);
+        source.put(3L, 300L);
+        map.putAll(source);
+        Assertions.assertEquals(3, map.size());
+        Assertions.assertEquals(200L, map.get(2L));
+    }
+
+    @Test
+    void testPutIfAbsent() {
+        ConcurrentLong2LongHashMap map = new ConcurrentLong2LongHashMap();
+        Assertions.assertEquals(0L, map.putIfAbsent(1L, 100L));
+        Assertions.assertEquals(100L, map.putIfAbsent(1L, 200L));
+        Assertions.assertEquals(100L, map.get(1L));
+    }
+
+    @Test
+    void testComputeIfAbsent() {
+        ConcurrentLong2LongHashMap map = new ConcurrentLong2LongHashMap();
+        long val = map.computeIfAbsent(1L, k -> k * 10);
+        Assertions.assertEquals(10L, val);
+        // Should not recompute
+        long val2 = map.computeIfAbsent(1L, k -> k * 20);
+        Assertions.assertEquals(10L, val2);
+    }
+
+    // ---- addTo tests ----
+
+    @Test
+    void testAddToNewKey() {
+        ConcurrentLong2LongHashMap map = new ConcurrentLong2LongHashMap();
+        long result = map.addTo(1L, 5L);
+        Assertions.assertEquals(5L, result);
+        Assertions.assertEquals(5L, map.get(1L));
+    }
+
+    @Test
+    void testAddToExistingKey() {
+        ConcurrentLong2LongHashMap map = new ConcurrentLong2LongHashMap();
+        map.put(1L, 10L);
+        long result = map.addTo(1L, 5L);
+        Assertions.assertEquals(15L, result);
+        Assertions.assertEquals(15L, map.get(1L));
+    }
+
+    @Test
+    void testAddToNegative() {
+        ConcurrentLong2LongHashMap map = new ConcurrentLong2LongHashMap();
+        map.put(1L, 10L);
+        long result = map.addTo(1L, -3L);
+        Assertions.assertEquals(7L, result);
+    }
+
+    // ---- Iteration tests ----
+
+    @Test
+    void testEntrySet() {
+        ConcurrentLong2LongHashMap map = new ConcurrentLong2LongHashMap();
+        map.put(1L, 100L);
+        map.put(2L, 200L);
+
+        ObjectSet<Long2LongMap.Entry> entries = map.long2LongEntrySet();
+        Assertions.assertEquals(2, entries.size());
+
+        Set<Long> keys = new HashSet<>();
+        for (Long2LongMap.Entry entry : entries) {
+            keys.add(entry.getLongKey());
+        }
+        Assertions.assertTrue(keys.contains(1L));
+        Assertions.assertTrue(keys.contains(2L));
+    }
+
+    @Test
+    void testKeySet() {
+        ConcurrentLong2LongHashMap map = new ConcurrentLong2LongHashMap();
+        map.put(10L, 100L);
+        map.put(20L, 200L);
+        LongSet keys = map.keySet();
+        Assertions.assertEquals(2, keys.size());
+        Assertions.assertTrue(keys.contains(10L));
+        Assertions.assertTrue(keys.contains(20L));
+    }
+
+    @Test
+    void testValues() {
+        ConcurrentLong2LongHashMap map = new ConcurrentLong2LongHashMap();
+        map.put(1L, 100L);
+        map.put(2L, 200L);
+        it.unimi.dsi.fastutil.longs.LongCollection values = map.values();
+        Assertions.assertEquals(2, values.size());
+        Assertions.assertTrue(values.contains(100L));
+        Assertions.assertTrue(values.contains(200L));
+    }
+
+    @Test
+    void testForEach() {
+        ConcurrentLong2LongHashMap map = new ConcurrentLong2LongHashMap();
+        map.put(1L, 100L);
+        map.put(2L, 200L);
+        Map<Long, Long> collected = new HashMap<>();
+        map.forEach((ConcurrentLong2LongHashMap.LongLongConsumer) 
collected::put);
+        Assertions.assertEquals(2, collected.size());
+        Assertions.assertEquals(100L, (long) collected.get(1L));
+    }
+
+    // ---- Large map test ----
+
+    @Test
+    void testLargeMap() {
+        ConcurrentLong2LongHashMap map = new ConcurrentLong2LongHashMap();
+        int count = 100_000;
+        for (long i = 0; i < count; i++) {
+            map.put(i, i * 3);
+        }
+        Assertions.assertEquals(count, map.size());
+        for (long i = 0; i < count; i++) {
+            Assertions.assertEquals(i * 3, map.get(i));
+        }
+    }
+
+    @Test
+    void testCustomSegmentCount() {
+        ConcurrentLong2LongHashMap map = new ConcurrentLong2LongHashMap(4);
+        for (long i = 0; i < 1000; i++) {
+            map.put(i, i);
+        }
+        Assertions.assertEquals(1000, map.size());
+    }
+
+    @Test
+    void testInvalidSegmentCount() {
+        Assertions.assertThrows(IllegalArgumentException.class, () -> new 
ConcurrentLong2LongHashMap(3));
+        Assertions.assertThrows(IllegalArgumentException.class, () -> new 
ConcurrentLong2LongHashMap(0));
+    }
+
+    // ---- Concurrency tests ----
+
+    @Test
+    void testConcurrentPuts() throws Exception {
+        ConcurrentLong2LongHashMap map = new ConcurrentLong2LongHashMap();
+        int threads = 8;
+        int keysPerThread = 10_000;
+        ExecutorService executor = Executors.newFixedThreadPool(threads);
+        CountDownLatch latch = new CountDownLatch(threads);
+
+        for (int t = 0; t < threads; t++) {
+            final int threadId = t;
+            executor.submit(() -> {
+                for (int i = 0; i < keysPerThread; i++) {
+                    long key = (long) threadId * keysPerThread + i;
+                    map.put(key, key * 2);
+                }
+                latch.countDown();
+            });
+        }
+        latch.await();
+        executor.shutdown();
+        Assertions.assertEquals(threads * keysPerThread, map.size());
+    }
+
+    @Test
+    void testConcurrentAddTo() throws Exception {
+        ConcurrentLong2LongHashMap map = new ConcurrentLong2LongHashMap();
+        int threads = 16;
+        int incrementsPerThread = 10_000;
+        long key = 42L;
+        ExecutorService executor = Executors.newFixedThreadPool(threads);
+        CountDownLatch latch = new CountDownLatch(threads);
+
+        for (int t = 0; t < threads; t++) {
+            executor.submit(() -> {
+                for (int i = 0; i < incrementsPerThread; i++) {
+                    map.addTo(key, 1L);
+                }
+                latch.countDown();
+            });
+        }
+        latch.await();
+        executor.shutdown();
+
+        Assertions.assertEquals((long) threads * incrementsPerThread, 
map.get(key));
+    }
+
+    @Test
+    void testConcurrentReadWrite() throws Exception {
+        ConcurrentLong2LongHashMap map = new ConcurrentLong2LongHashMap();
+        for (long i = 0; i < 1000; i++) {
+            map.put(i, i);
+        }
+
+        int threads = 8;
+        ExecutorService executor = Executors.newFixedThreadPool(threads);
+        CountDownLatch latch = new CountDownLatch(threads);
+        AtomicInteger errors = new AtomicInteger();
+
+        for (int t = 0; t < threads; t++) {
+            final int threadId = t;
+            executor.submit(() -> {
+                try {
+                    for (int i = 0; i < 5000; i++) {
+                        long key = i % 1000;
+                        if (threadId % 2 == 0) {
+                            map.get(key);
+                            map.containsKey(key);
+                        } else {
+                            map.put(key + 1000L * threadId, (long) i);
+                        }
+                    }
+                } catch (Exception e) {
+                    errors.incrementAndGet();
+                } finally {
+                    latch.countDown();
+                }
+            });
+        }
+        latch.await();
+        executor.shutdown();
+        Assertions.assertEquals(0, errors.get());
+    }
+
+    @Test
+    void testConcurrentComputeIfAbsent() throws Exception {
+        ConcurrentLong2LongHashMap map = new ConcurrentLong2LongHashMap();
+        int threads = 16;
+        long sharedKey = 42L;
+        AtomicInteger computeCount = new AtomicInteger();
+        ExecutorService executor = Executors.newFixedThreadPool(threads);
+        List<Future<Long>> futures = new ArrayList<>();
+
+        for (int t = 0; t < threads; t++) {
+            futures.add(executor.submit(() ->
+                    map.computeIfAbsent(sharedKey, k -> {
+                        computeCount.incrementAndGet();
+                        return k * 10;
+                    })
+            ));
+        }
+        Set<Long> results = new HashSet<>();
+        for (Future<Long> f : futures) {
+            results.add(f.get());
+        }
+        executor.shutdown();
+
+        Assertions.assertEquals(1, results.size());
+        Assertions.assertTrue(results.contains(420L));
+        Assertions.assertEquals(1, computeCount.get());
+    }
+
+    @Test
+    void testConcurrentIterationDuringModification() throws Exception {
+        ConcurrentLong2LongHashMap map = new ConcurrentLong2LongHashMap();
+        for (long i = 0; i < 1000; i++) {
+            map.put(i, i);
+        }
+
+        int threads = 4;
+        ExecutorService executor = Executors.newFixedThreadPool(threads);
+        CountDownLatch latch = new CountDownLatch(threads);
+        AtomicInteger errors = new AtomicInteger();
+
+        for (int t = 0; t < threads; t++) {
+            final int threadId = t;
+            executor.submit(() -> {
+                try {
+                    for (int i = 0; i < 100; i++) {
+                        if (threadId % 2 == 0) {
+                            map.keySet();
+                            map.values();
+                            map.long2LongEntrySet();
+                        } else {
+                            map.put(1000L + threadId * 100 + i, (long) i);
+                            map.remove((long) (i % 500));
+                        }
+                    }
+                } catch (Exception e) {
+                    errors.incrementAndGet();
+                } finally {
+                    latch.countDown();
+                }
+            });
+        }
+        latch.await();
+        executor.shutdown();
+        Assertions.assertEquals(0, errors.get());
+    }
+
+    // ---- Gson serialization tests ----
+
+    @Test
+    void testGsonRoundTrip() {
+        ConcurrentLong2LongHashMap map = new ConcurrentLong2LongHashMap();
+        map.put(100L, 1000L);
+        map.put(200L, 2000L);
+
+        String json = new Gson().toJson(map);
+
+        ConcurrentLong2LongHashMap deserialized = new Gson().fromJson(json, 
ConcurrentLong2LongHashMap.class);
+
+        Assertions.assertEquals(2, deserialized.size());
+        Assertions.assertEquals(1000L, deserialized.get(100L));
+        Assertions.assertEquals(2000L, deserialized.get(200L));
+    }
+
+    @Test
+    void testGsonFormatCompatibleWithConcurrentHashMap() {
+        ConcurrentHashMap<Long, Long> chm = new ConcurrentHashMap<>();
+        chm.put(1L, 100L);
+        chm.put(2L, 200L);
+        String chmJson = new Gson().toJson(chm);
+
+        ConcurrentLong2LongHashMap fastMap = new ConcurrentLong2LongHashMap();
+        fastMap.put(1L, 100L);
+        fastMap.put(2L, 200L);
+        String fastJson = new Gson().toJson(fastMap);
+
+        Gson gson = new Gson();
+        Map<?, ?> chmParsed = gson.fromJson(chmJson, Map.class);
+        Map<?, ?> fastParsed = gson.fromJson(fastJson, Map.class);
+        Assertions.assertEquals(chmParsed, fastParsed);
+    }
+
+    @Test
+    void testDefaultReturnValueBehavior() {
+        ConcurrentLong2LongHashMap map = new ConcurrentLong2LongHashMap();
+        // Primitive get returns 0L (defaultReturnValue) for missing keys
+        Assertions.assertEquals(0L, map.get(999L));
+
+        // Store 0L explicitly
+        map.put(1L, 0L);
+        Assertions.assertTrue(map.containsKey(1L));
+        Assertions.assertEquals(0L, map.get(1L));
+
+        // getOrDefault returns the specified default for missing keys
+        Assertions.assertEquals(0L, map.getOrDefault(999L, 
map.defaultReturnValue()));
+        // Boxed get via Map<Long,Long> interface returns null for missing keys
+        Assertions.assertNull(((Map<Long, Long>) map).get(999L));
+
+        // Changing defaultReturnValue is not supported
+        Assertions.assertThrows(UnsupportedOperationException.class, () -> 
map.defaultReturnValue(-1L));
+    }
+}
diff --git 
a/fe/fe-foundation/src/test/java/org/apache/doris/foundation/util/ConcurrentLong2ObjectHashMapTest.java
 
b/fe/fe-foundation/src/test/java/org/apache/doris/foundation/util/ConcurrentLong2ObjectHashMapTest.java
new file mode 100644
index 00000000000..178bf35b7bd
--- /dev/null
+++ 
b/fe/fe-foundation/src/test/java/org/apache/doris/foundation/util/ConcurrentLong2ObjectHashMapTest.java
@@ -0,0 +1,432 @@
+// 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.doris.foundation.util;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
+import it.unimi.dsi.fastutil.longs.LongSet;
+import it.unimi.dsi.fastutil.objects.ObjectCollection;
+import it.unimi.dsi.fastutil.objects.ObjectSet;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicInteger;
+
+class ConcurrentLong2ObjectHashMapTest {
+
+    @Test
+    void testPutAndGet() {
+        ConcurrentLong2ObjectHashMap<String> map = new 
ConcurrentLong2ObjectHashMap<>();
+        Assertions.assertNull(map.put(1L, "one"));
+        Assertions.assertEquals("one", map.get(1L));
+        Assertions.assertEquals("one", map.put(1L, "ONE"));
+        Assertions.assertEquals("ONE", map.get(1L));
+    }
+
+    @Test
+    void testGetMissingKey() {
+        ConcurrentLong2ObjectHashMap<String> map = new 
ConcurrentLong2ObjectHashMap<>();
+        Assertions.assertNull(map.get(999L));
+    }
+
+    @Test
+    void testGetOrDefault() {
+        ConcurrentLong2ObjectHashMap<String> map = new 
ConcurrentLong2ObjectHashMap<>();
+        map.put(1L, "one");
+        Assertions.assertEquals("one", map.getOrDefault(1L, "default"));
+        Assertions.assertEquals("default", map.getOrDefault(2L, "default"));
+    }
+
+    @Test
+    void testRemove() {
+        ConcurrentLong2ObjectHashMap<String> map = new 
ConcurrentLong2ObjectHashMap<>();
+        map.put(1L, "one");
+        Assertions.assertEquals("one", map.remove(1L));
+        Assertions.assertNull(map.get(1L));
+        Assertions.assertNull(map.remove(1L));
+    }
+
+    @Test
+    void testContainsKey() {
+        ConcurrentLong2ObjectHashMap<String> map = new 
ConcurrentLong2ObjectHashMap<>();
+        Assertions.assertFalse(map.containsKey(1L));
+        map.put(1L, "one");
+        Assertions.assertTrue(map.containsKey(1L));
+    }
+
+    @Test
+    void testContainsValue() {
+        ConcurrentLong2ObjectHashMap<String> map = new 
ConcurrentLong2ObjectHashMap<>();
+        map.put(1L, "one");
+        map.put(2L, "two");
+        Assertions.assertTrue(map.containsValue("one"));
+        Assertions.assertFalse(map.containsValue("three"));
+    }
+
+    @Test
+    void testSizeAndIsEmpty() {
+        ConcurrentLong2ObjectHashMap<String> map = new 
ConcurrentLong2ObjectHashMap<>();
+        Assertions.assertTrue(map.isEmpty());
+        Assertions.assertEquals(0, map.size());
+        map.put(1L, "one");
+        map.put(2L, "two");
+        Assertions.assertFalse(map.isEmpty());
+        Assertions.assertEquals(2, map.size());
+    }
+
+    @Test
+    void testClear() {
+        ConcurrentLong2ObjectHashMap<String> map = new 
ConcurrentLong2ObjectHashMap<>();
+        map.put(1L, "one");
+        map.put(2L, "two");
+        map.clear();
+        Assertions.assertTrue(map.isEmpty());
+        Assertions.assertEquals(0, map.size());
+    }
+
+    @Test
+    void testPutAll() {
+        ConcurrentLong2ObjectHashMap<String> map = new 
ConcurrentLong2ObjectHashMap<>();
+        Map<Long, String> source = new HashMap<>();
+        source.put(1L, "one");
+        source.put(2L, "two");
+        source.put(3L, "three");
+        map.putAll(source);
+        Assertions.assertEquals(3, map.size());
+        Assertions.assertEquals("two", map.get(2L));
+    }
+
+    @Test
+    void testPutIfAbsent() {
+        ConcurrentLong2ObjectHashMap<String> map = new 
ConcurrentLong2ObjectHashMap<>();
+        Assertions.assertNull(map.putIfAbsent(1L, "one"));
+        Assertions.assertEquals("one", map.putIfAbsent(1L, "ONE"));
+        Assertions.assertEquals("one", map.get(1L));
+    }
+
+    @Test
+    void testComputeIfAbsent() {
+        ConcurrentLong2ObjectHashMap<String> map = new 
ConcurrentLong2ObjectHashMap<>();
+        String val = map.computeIfAbsent(1L, k -> "computed-" + k);
+        Assertions.assertEquals("computed-1", val);
+        // Should not recompute
+        String val2 = map.computeIfAbsent(1L, k -> "recomputed-" + k);
+        Assertions.assertEquals("computed-1", val2);
+    }
+
+    @Test
+    void testComputeIfPresent() {
+        ConcurrentLong2ObjectHashMap<String> map = new 
ConcurrentLong2ObjectHashMap<>();
+        // Not present — should return null
+        Assertions.assertNull(map.computeIfPresent(1L, (k, v) -> v + 
"-updated"));
+
+        map.put(1L, "one");
+        String val = map.computeIfPresent(1L, (k, v) -> v + "-updated");
+        Assertions.assertEquals("one-updated", val);
+        Assertions.assertEquals("one-updated", map.get(1L));
+
+        // Return null to remove
+        Assertions.assertNull(map.computeIfPresent(1L, (k, v) -> null));
+        Assertions.assertFalse(map.containsKey(1L));
+    }
+
+    @Test
+    void testEntrySet() {
+        ConcurrentLong2ObjectHashMap<String> map = new 
ConcurrentLong2ObjectHashMap<>();
+        map.put(1L, "one");
+        map.put(2L, "two");
+
+        ObjectSet<Long2ObjectMap.Entry<String>> entries = 
map.long2ObjectEntrySet();
+        Assertions.assertEquals(2, entries.size());
+
+        Set<Long> keys = new HashSet<>();
+        for (Long2ObjectMap.Entry<String> entry : entries) {
+            keys.add(entry.getLongKey());
+        }
+        Assertions.assertTrue(keys.contains(1L));
+        Assertions.assertTrue(keys.contains(2L));
+    }
+
+    @Test
+    void testKeySet() {
+        ConcurrentLong2ObjectHashMap<String> map = new 
ConcurrentLong2ObjectHashMap<>();
+        map.put(10L, "ten");
+        map.put(20L, "twenty");
+        LongSet keys = map.keySet();
+        Assertions.assertEquals(2, keys.size());
+        Assertions.assertTrue(keys.contains(10L));
+        Assertions.assertTrue(keys.contains(20L));
+    }
+
+    @Test
+    void testValues() {
+        ConcurrentLong2ObjectHashMap<String> map = new 
ConcurrentLong2ObjectHashMap<>();
+        map.put(1L, "one");
+        map.put(2L, "two");
+        ObjectCollection<String> values = map.values();
+        Assertions.assertEquals(2, values.size());
+        Assertions.assertTrue(values.contains("one"));
+        Assertions.assertTrue(values.contains("two"));
+    }
+
+    @Test
+    void testStream() {
+        ConcurrentLong2ObjectHashMap<String> map = new 
ConcurrentLong2ObjectHashMap<>();
+        for (long i = 0; i < 100; i++) {
+            map.put(i, "val-" + i);
+        }
+        long count = map.values().stream().filter(v -> 
v.startsWith("val-")).count();
+        Assertions.assertEquals(100, count);
+    }
+
+    @Test
+    void testForEach() {
+        ConcurrentLong2ObjectHashMap<String> map = new 
ConcurrentLong2ObjectHashMap<>();
+        map.put(1L, "one");
+        map.put(2L, "two");
+        Map<Long, String> collected = new HashMap<>();
+        map.forEach((ConcurrentLong2ObjectHashMap.LongObjConsumer<String>) 
collected::put);
+        Assertions.assertEquals(2, collected.size());
+        Assertions.assertEquals("one", collected.get(1L));
+    }
+
+    @Test
+    void testNullValuesRejected() {
+        ConcurrentLong2ObjectHashMap<String> map = new 
ConcurrentLong2ObjectHashMap<>();
+        Assertions.assertThrows(NullPointerException.class, () -> map.put(1L, 
null));
+        Assertions.assertThrows(NullPointerException.class, () -> 
map.putIfAbsent(1L, null));
+        Assertions.assertThrows(NullPointerException.class, () -> 
map.replace(1L, null));
+        Assertions.assertThrows(NullPointerException.class, () -> 
map.replace(1L, "old", null));
+    }
+
+    @Test
+    void testLargeMap() {
+        ConcurrentLong2ObjectHashMap<Long> map = new 
ConcurrentLong2ObjectHashMap<>();
+        int count = 100_000;
+        for (long i = 0; i < count; i++) {
+            map.put(i, Long.valueOf(i * 2));
+        }
+        Assertions.assertEquals(count, map.size());
+        for (long i = 0; i < count; i++) {
+            Assertions.assertEquals(Long.valueOf(i * 2), map.get(i));
+        }
+    }
+
+    @Test
+    void testCustomSegmentCount() {
+        ConcurrentLong2ObjectHashMap<String> map = new 
ConcurrentLong2ObjectHashMap<>(4);
+        for (long i = 0; i < 1000; i++) {
+            map.put(i, "v" + i);
+        }
+        Assertions.assertEquals(1000, map.size());
+    }
+
+    @Test
+    void testInvalidSegmentCount() {
+        Assertions.assertThrows(IllegalArgumentException.class, () -> new 
ConcurrentLong2ObjectHashMap<>(3));
+        Assertions.assertThrows(IllegalArgumentException.class, () -> new 
ConcurrentLong2ObjectHashMap<>(0));
+        Assertions.assertThrows(IllegalArgumentException.class, () -> new 
ConcurrentLong2ObjectHashMap<>(-1));
+    }
+
+    // ---- Concurrency tests ----
+
+    @Test
+    void testConcurrentPuts() throws Exception {
+        ConcurrentLong2ObjectHashMap<String> map = new 
ConcurrentLong2ObjectHashMap<>();
+        int threads = 8;
+        int keysPerThread = 10_000;
+        ExecutorService executor = Executors.newFixedThreadPool(threads);
+        CountDownLatch latch = new CountDownLatch(threads);
+
+        for (int t = 0; t < threads; t++) {
+            final int threadId = t;
+            executor.submit(() -> {
+                for (int i = 0; i < keysPerThread; i++) {
+                    long key = (long) threadId * keysPerThread + i;
+                    map.put(key, "t" + threadId + "-" + i);
+                }
+                latch.countDown();
+            });
+        }
+        latch.await();
+        executor.shutdown();
+
+        Assertions.assertEquals(threads * keysPerThread, map.size());
+    }
+
+    @Test
+    void testConcurrentReadWrite() throws Exception {
+        ConcurrentLong2ObjectHashMap<String> map = new 
ConcurrentLong2ObjectHashMap<>();
+        // Pre-populate
+        for (long i = 0; i < 1000; i++) {
+            map.put(i, "v" + i);
+        }
+
+        int threads = 8;
+        ExecutorService executor = Executors.newFixedThreadPool(threads);
+        CountDownLatch latch = new CountDownLatch(threads);
+        AtomicInteger errors = new AtomicInteger();
+
+        for (int t = 0; t < threads; t++) {
+            final int threadId = t;
+            executor.submit(() -> {
+                try {
+                    for (int i = 0; i < 5000; i++) {
+                        long key = i % 1000;
+                        if (threadId % 2 == 0) {
+                            // Reader
+                            map.get(key);
+                            map.containsKey(key);
+                        } else {
+                            // Writer
+                            map.put(key + 1000L * threadId, "new-" + i);
+                        }
+                    }
+                } catch (Exception e) {
+                    errors.incrementAndGet();
+                } finally {
+                    latch.countDown();
+                }
+            });
+        }
+        latch.await();
+        executor.shutdown();
+        Assertions.assertEquals(0, errors.get());
+    }
+
+    @Test
+    void testConcurrentComputeIfAbsent() throws Exception {
+        ConcurrentLong2ObjectHashMap<String> map = new 
ConcurrentLong2ObjectHashMap<>();
+        int threads = 16;
+        long sharedKey = 42L;
+        AtomicInteger computeCount = new AtomicInteger();
+        ExecutorService executor = Executors.newFixedThreadPool(threads);
+        List<Future<String>> futures = new ArrayList<>();
+
+        for (int t = 0; t < threads; t++) {
+            futures.add(executor.submit(() ->
+                    map.computeIfAbsent(sharedKey, k -> {
+                        computeCount.incrementAndGet();
+                        return "computed";
+                    })
+            ));
+        }
+        Set<String> results = new HashSet<>();
+        for (Future<String> f : futures) {
+            results.add(f.get());
+        }
+        executor.shutdown();
+
+        // All threads should get the same value
+        Assertions.assertEquals(1, results.size());
+        Assertions.assertTrue(results.contains("computed"));
+        // The function should have been called exactly once
+        Assertions.assertEquals(1, computeCount.get());
+    }
+
+    @Test
+    void testConcurrentIterationDuringModification() throws Exception {
+        ConcurrentLong2ObjectHashMap<String> map = new 
ConcurrentLong2ObjectHashMap<>();
+        for (long i = 0; i < 1000; i++) {
+            map.put(i, "v" + i);
+        }
+
+        int threads = 4;
+        ExecutorService executor = Executors.newFixedThreadPool(threads);
+        CountDownLatch latch = new CountDownLatch(threads);
+        AtomicInteger errors = new AtomicInteger();
+
+        for (int t = 0; t < threads; t++) {
+            final int threadId = t;
+            executor.submit(() -> {
+                try {
+                    for (int i = 0; i < 100; i++) {
+                        if (threadId % 2 == 0) {
+                            // Iterator - should not throw
+                            map.keySet();
+                            map.values();
+                            map.long2ObjectEntrySet();
+                        } else {
+                            // Modifier
+                            map.put(1000L + threadId * 100 + i, "new");
+                            map.remove((long) (i % 500));
+                        }
+                    }
+                } catch (Exception e) {
+                    errors.incrementAndGet();
+                } finally {
+                    latch.countDown();
+                }
+            });
+        }
+        latch.await();
+        executor.shutdown();
+        Assertions.assertEquals(0, errors.get());
+    }
+
+    // ---- Gson serialization tests ----
+
+    @Test
+    void testGsonRoundTrip() {
+        ConcurrentLong2ObjectHashMap<String> map = new 
ConcurrentLong2ObjectHashMap<>();
+        map.put(100L, "hundred");
+        map.put(200L, "two-hundred");
+
+        String json = new Gson().toJson(map);
+
+        Type type = new TypeToken<ConcurrentLong2ObjectHashMap<String>>() 
{}.getType();
+        ConcurrentLong2ObjectHashMap<String> deserialized = new 
Gson().fromJson(json, type);
+
+        Assertions.assertEquals(2, deserialized.size());
+        Assertions.assertEquals("hundred", deserialized.get(100L));
+        Assertions.assertEquals("two-hundred", deserialized.get(200L));
+    }
+
+    @Test
+    void testGsonFormatCompatibleWithConcurrentHashMap() {
+        // Verify the JSON format matches what ConcurrentHashMap<Long, String> 
produces
+        ConcurrentHashMap<Long, String> chm = new ConcurrentHashMap<>();
+        chm.put(1L, "one");
+        chm.put(2L, "two");
+        String chmJson = new Gson().toJson(chm);
+
+        ConcurrentLong2ObjectHashMap<String> fastMap = new 
ConcurrentLong2ObjectHashMap<>();
+        fastMap.put(1L, "one");
+        fastMap.put(2L, "two");
+        String fastJson = new Gson().toJson(fastMap);
+
+        // Both should be parseable as the same JSON object
+        Gson gson = new Gson();
+        Map<?, ?> chmParsed = gson.fromJson(chmJson, Map.class);
+        Map<?, ?> fastParsed = gson.fromJson(fastJson, Map.class);
+        Assertions.assertEquals(chmParsed, fastParsed);
+    }
+}
diff --git a/fe/pom.xml b/fe/pom.xml
index b327978bbf6..68a7f40c591 100644
--- a/fe/pom.xml
+++ b/fe/pom.xml
@@ -1864,7 +1864,6 @@ under the License.
                 <artifactId>mockito-inline</artifactId>
                 <version>${mockito.version}</version>
             </dependency>
-            <!-- fastutil for memory-efficient primitive collections -->
             <dependency>
                 <groupId>it.unimi.dsi</groupId>
                 <artifactId>fastutil-core</artifactId>


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to