You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ir...@apache.org on 2019/08/11 23:33:32 UTC

[ignite] branch master updated: IGNITE-6957 Reduce excessive int boxing when accessing cache by ID - Fixes #6536.

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 4eda065  IGNITE-6957 Reduce excessive int boxing when accessing cache by ID - Fixes #6536.
4eda065 is described below

commit 4eda06575f1831350ff0545c2b78bb3cb9a811b5
Author: mstepachev <ma...@gmail.com>
AuthorDate: Mon Aug 12 02:32:55 2019 +0300

    IGNITE-6957 Reduce excessive int boxing when accessing cache by ID - Fixes #6536.
    
    Signed-off-by: Ivan Rakov <ir...@apache.org>
---
 .../affinity/GridAffinityAssignmentV2.java         |  15 +-
 .../affinity/HistoryAffinityAssignmentImpl.java    |   7 +-
 .../processors/cache/CacheMetricsImpl.java         |   9 +-
 .../cache/IgniteCacheOffheapManagerImpl.java       |  35 +--
 .../dht/topology/GridDhtLocalPartition.java        |  19 +-
 .../util/{ => collection}/BitSetIntSet.java        | 112 +++++---
 .../internal/util/collection/ImmutableIntSet.java  | 206 ++++++++++++++
 .../internal/util/collection/IntHashMap.java       | 309 +++++++++++++++++++++
 .../ignite/internal/util/collection/IntMap.java    |  85 ++++++
 .../internal/util/collection/IntRWHashMap.java     | 141 ++++++++++
 .../ignite/internal/util/collection/IntSet.java    |  37 +++
 .../main/resources/META-INF/classnames.properties  |   5 +-
 .../affinity/GridAffinityAssignmentV2Test.java     |   4 +-
 .../util/collection/AbstractBaseIntMapTest.java    | 248 +++++++++++++++++
 .../util/{ => collection}/BitSetIntSetTest.java    | 188 +++++++------
 .../util/collection/ImmutableIntSetTest.java       |  92 ++++++
 .../internal/util/collection/IntHashMapTest.java   | 126 +++++++++
 .../internal/util/collection/IntRWHashMapTest.java |  28 ++
 .../ignite/testsuites/IgniteBasicTestSuite.java    |   4 +-
 19 files changed, 1504 insertions(+), 166 deletions(-)

diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityAssignmentV2.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityAssignmentV2.java
index ddb5716..1c8ef38 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityAssignmentV2.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/GridAffinityAssignmentV2.java
@@ -31,7 +31,8 @@ import java.util.Set;
 import java.util.UUID;
 import org.apache.ignite.cluster.ClusterNode;
 import org.apache.ignite.internal.dto.IgniteDataTransferObject;
-import org.apache.ignite.internal.util.BitSetIntSet;
+import org.apache.ignite.internal.util.collection.BitSetIntSet;
+import org.apache.ignite.internal.util.collection.ImmutableIntSet;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.internal.util.typedef.internal.U;
@@ -117,16 +118,16 @@ public class GridAffinityAssignmentV2 extends IgniteDataTransferObject implement
         for (int partsCnt = assignment.size(), p = 0; p < partsCnt; p++) {
             isPrimary = true;
 
-            List<ClusterNode> currentOwners = assignment.get(p);
+            List<ClusterNode> currOwners = assignment.get(p);
 
-            for (ClusterNode node : currentOwners) {
+            for (ClusterNode node : currOwners) {
                 UUID id = node.id();
 
                 Map<UUID, Set<Integer>> tmp = isPrimary ? tmpPrimary : tmpBackup;
 
                 /*
                     https://issues.apache.org/jira/browse/IGNITE-4554 BitSet performs better than HashSet at most cases.
-                    However with 65k partition and high number of nodes (700+) BitSet is loosing HashSet.
+                    However with 65k partition and high number of nodes (700+) BitSet is losing HashSet.
                     We need to replace it with sparse bitsets.
                  */
                 tmp.computeIfAbsent(id, uuid ->
@@ -138,7 +139,7 @@ public class GridAffinityAssignmentV2 extends IgniteDataTransferObject implement
 
             List<ClusterNode> idealOwners = p < idealAssignment.size() ? idealAssignment.get(p) : Collections.emptyList();
 
-            ClusterNode curPrimary = !currentOwners.isEmpty() ? currentOwners.get(0) : null;
+            ClusterNode curPrimary = !currOwners.isEmpty() ? currOwners.get(0) : null;
             ClusterNode idealPrimary = !idealOwners.isEmpty() ? idealOwners.get(0) : null;
 
             if (curPrimary != null && !curPrimary.equals(idealPrimary))
@@ -288,7 +289,7 @@ public class GridAffinityAssignmentV2 extends IgniteDataTransferObject implement
     @Override public Set<Integer> primaryPartitions(UUID nodeId) {
         Set<Integer> set = primary.get(nodeId);
 
-        return set == null ? Collections.emptySet() : Collections.unmodifiableSet(set);
+        return set == null ? ImmutableIntSet.emptySet() : ImmutableIntSet.wrap(set);
     }
 
     /**
@@ -300,7 +301,7 @@ public class GridAffinityAssignmentV2 extends IgniteDataTransferObject implement
     @Override public Set<Integer> backupPartitions(UUID nodeId) {
         Set<Integer> set = backup.get(nodeId);
 
-        return set == null ? Collections.emptySet() : Collections.unmodifiableSet(set);
+        return set == null ? ImmutableIntSet.emptySet() : ImmutableIntSet.wrap(set);
     }
 
     /** {@inheritDoc} */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/HistoryAffinityAssignmentImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/HistoryAffinityAssignmentImpl.java
index 1123756..d303fdf 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/HistoryAffinityAssignmentImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/affinity/HistoryAffinityAssignmentImpl.java
@@ -29,7 +29,8 @@ import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
 import org.apache.ignite.cluster.ClusterNode;
-import org.apache.ignite.internal.util.BitSetIntSet;
+import org.apache.ignite.internal.util.collection.BitSetIntSet;
+import org.apache.ignite.internal.util.collection.ImmutableIntSet;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.internal.S;
 
@@ -300,7 +301,7 @@ public class HistoryAffinityAssignmentImpl implements HistoryAffinityAssignment
                 res.add(p);
         }
 
-        return Collections.unmodifiableSet(res);
+        return ImmutableIntSet.wrap(res);
     }
 
     /** {@inheritDoc} */
@@ -321,7 +322,7 @@ public class HistoryAffinityAssignmentImpl implements HistoryAffinityAssignment
             }
         }
 
-        return Collections.unmodifiableSet(res);
+        return ImmutableIntSet.wrap(res);
     }
 
     /** {@inheritDoc} */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMetricsImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMetricsImpl.java
index f26bf14..e1049a5 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMetricsImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMetricsImpl.java
@@ -17,7 +17,6 @@
 
 package org.apache.ignite.internal.processors.cache;
 
-import java.util.Set;
 import org.apache.ignite.IgniteSystemProperties;
 import org.apache.ignite.cache.CacheMetrics;
 import org.apache.ignite.cache.CachePeekMode;
@@ -28,9 +27,11 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.topology.Grid
 import org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtPartitionState;
 import org.apache.ignite.internal.processors.cache.store.GridCacheWriteBehindStore;
 import org.apache.ignite.internal.processors.metric.MetricRegistry;
-import org.apache.ignite.internal.processors.metric.impl.HitRateMetric;
 import org.apache.ignite.internal.processors.metric.impl.AtomicLongMetric;
+import org.apache.ignite.internal.processors.metric.impl.HitRateMetric;
 import org.apache.ignite.internal.processors.metric.impl.MetricUtils;
+import org.apache.ignite.internal.util.collection.ImmutableIntSet;
+import org.apache.ignite.internal.util.collection.IntSet;
 import org.apache.ignite.internal.util.tostring.GridToStringExclude;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.internal.util.typedef.internal.U;
@@ -1118,8 +1119,8 @@ public class CacheMetricsImpl implements CacheMetrics {
             else {
                 AffinityTopologyVersion topVer = cctx.affinity().affinityTopologyVersion();
 
-                Set<Integer> primaries = cctx.affinity().primaryPartitions(cctx.localNodeId(), topVer);
-                Set<Integer> backups = cctx.affinity().backupPartitions(cctx.localNodeId(), topVer);
+                IntSet primaries = ImmutableIntSet.wrap(cctx.affinity().primaryPartitions(cctx.localNodeId(), topVer));
+                IntSet backups = ImmutableIntSet.wrap(cctx.affinity().backupPartitions(cctx.localNodeId(), topVer));
 
                 if (cctx.isNear() && cache != null)
                     heapEntriesCnt = cache.nearSize();
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java
index f184e6e..57cca44 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java
@@ -102,6 +102,10 @@ import org.apache.ignite.internal.util.GridLongList;
 import org.apache.ignite.internal.util.GridSpinBusyLock;
 import org.apache.ignite.internal.util.GridStripedLock;
 import org.apache.ignite.internal.util.IgniteTree;
+import org.apache.ignite.internal.util.collection.ImmutableIntSet;
+import org.apache.ignite.internal.util.collection.IntMap;
+import org.apache.ignite.internal.util.collection.IntRWHashMap;
+import org.apache.ignite.internal.util.collection.IntSet;
 import org.apache.ignite.internal.util.lang.GridCloseableIterator;
 import org.apache.ignite.internal.util.lang.GridCursor;
 import org.apache.ignite.internal.util.lang.GridIterator;
@@ -111,9 +115,7 @@ import org.apache.ignite.internal.util.typedef.X;
 import org.apache.ignite.internal.util.typedef.internal.CU;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.lang.IgniteBiTuple;
-import org.apache.ignite.lang.IgniteClosure;
 import org.apache.ignite.lang.IgniteInClosure;
-import org.apache.ignite.lang.IgnitePredicate;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -412,27 +414,13 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
         else {
             final Iterator<GridDhtLocalPartition> it = grp.topology().currentLocalPartitions().iterator();
 
-            if (primary && backup) {
-                return F.iterator(it, new IgniteClosure<GridDhtLocalPartition, CacheDataStore>() {
-                    @Override public CacheDataStore apply(GridDhtLocalPartition part) {
-                        return part.dataStore();
-                    }
-                }, true);
-            }
+            if (primary && backup)
+                return F.iterator(it, GridDhtLocalPartition::dataStore, true);
 
-            final Set<Integer> parts = primary ? grp.affinity().primaryPartitions(ctx.localNodeId(), topVer) :
-                grp.affinity().backupPartitions(ctx.localNodeId(), topVer);
+            final IntSet parts = ImmutableIntSet.wrap(primary ? grp.affinity().primaryPartitions(ctx.localNodeId(), topVer) :
+                grp.affinity().backupPartitions(ctx.localNodeId(), topVer));
 
-            return F.iterator(it, new IgniteClosure<GridDhtLocalPartition, CacheDataStore>() {
-                    @Override public CacheDataStore apply(GridDhtLocalPartition part) {
-                        return part.dataStore();
-                    }
-                }, true,
-                new IgnitePredicate<GridDhtLocalPartition>() {
-                    @Override public boolean apply(GridDhtLocalPartition part) {
-                        return parts.contains(part.id());
-                    }
-                });
+            return F.iterator(it, GridDhtLocalPartition::dataStore, true, part -> parts.contains(part.id()));
         }
     }
 
@@ -1418,7 +1406,7 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
         private final AtomicLong storageSize = new AtomicLong();
 
         /** */
-        private final ConcurrentMap<Integer, AtomicLong> cacheSizes = new ConcurrentHashMap<>();
+        private final IntMap<AtomicLong> cacheSizes = new IntRWHashMap();
 
         /** Mvcc remove handler. */
         private final PageHandler<MvccUpdateDataRow, Boolean> mvccUpdateMarker = new MvccMarkUpdatedHandler();
@@ -1494,8 +1482,7 @@ public class IgniteCacheOffheapManagerImpl implements IgniteCacheOffheapManager
 
             Map<Integer, Long> res = new HashMap<>();
 
-            for (Map.Entry<Integer, AtomicLong> e : cacheSizes.entrySet())
-                res.put(e.getKey(), e.getValue().longValue());
+            cacheSizes.forEach((key, val) -> res.put(key, val.longValue()));
 
             return res;
         }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/topology/GridDhtLocalPartition.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/topology/GridDhtLocalPartition.java
index 5e637e9..a827f02fb 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/topology/GridDhtLocalPartition.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/topology/GridDhtLocalPartition.java
@@ -59,6 +59,8 @@ import org.apache.ignite.internal.processors.cache.transactions.TxCounters;
 import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
 import org.apache.ignite.internal.processors.query.GridQueryRowCacheCleaner;
 import org.apache.ignite.internal.util.GridLongList;
+import org.apache.ignite.internal.util.collection.IntMap;
+import org.apache.ignite.internal.util.collection.IntRWHashMap;
 import org.apache.ignite.internal.util.future.GridFutureAdapter;
 import org.apache.ignite.internal.util.lang.GridIterator;
 import org.apache.ignite.internal.util.tostring.GridToStringExclude;
@@ -145,7 +147,7 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
 
     /** */
     @GridToStringExclude
-    private final ConcurrentMap<Integer, CacheMapHolder> cacheMaps;
+    private final IntMap<CacheMapHolder> cacheMaps;
 
     /** */
     @GridToStringExclude
@@ -195,7 +197,7 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
 
         if (grp.sharedGroup()) {
             singleCacheEntryMap = null;
-            cacheMaps = new ConcurrentHashMap<>();
+            cacheMaps = new IntRWHashMap<>();
         }
         else {
             singleCacheEntryMap = new CacheMapHolder(grp.singleCacheContext(), createEntriesMap());
@@ -256,12 +258,11 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
     /** {@inheritDoc} */
     @Override public int internalSize() {
         if (grp.sharedGroup()) {
-            int size = 0;
+            final AtomicInteger size = new AtomicInteger(0);
 
-            for (CacheMapHolder hld : cacheMaps.values())
-                size += hld.map.size();
+            cacheMaps.forEach((key, hld) -> size.addAndGet(hld.map.size()));
 
-            return size;
+            return size.get();
         }
 
         return singleCacheEntryMap.map.size();
@@ -1129,10 +1130,8 @@ public class GridDhtLocalPartition extends GridCacheConcurrentMapImpl implements
 
         boolean rec = grp.eventRecordable(EVT_CACHE_REBALANCE_OBJECT_UNLOADED);
 
-        if (grp.sharedGroup()) {
-            for (CacheMapHolder hld : cacheMaps.values())
-                clear(hld.map, extras, rec);
-        }
+        if (grp.sharedGroup())
+            cacheMaps.forEach((key, hld) -> clear(hld.map, extras, rec));
         else
             clear(singleCacheEntryMap.map, extras, rec);
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/BitSetIntSet.java b/modules/core/src/main/java/org/apache/ignite/internal/util/collection/BitSetIntSet.java
similarity index 60%
rename from modules/core/src/main/java/org/apache/ignite/internal/util/BitSetIntSet.java
rename to modules/core/src/main/java/org/apache/ignite/internal/util/collection/BitSetIntSet.java
index 47c0198..12e8075 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/BitSetIntSet.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/collection/BitSetIntSet.java
@@ -14,21 +14,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.ignite.internal.util;
+package org.apache.ignite.internal.util.collection;
 
 import java.util.BitSet;
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
-import java.util.Set;
+import org.apache.ignite.internal.util.GridSerializableCollection;
 import org.jetbrains.annotations.NotNull;
 
+import static org.apache.ignite.internal.util.IgniteUtils.EMPTY_INTS;
+
 /**
  * Set of Integers implementation based on BitSet.
  *
  * Implementation doesn't support negative values and null, cause we can't distinct null from 0 bit in BitSet.
  */
-public class BitSetIntSet extends GridSerializableCollection<Integer> implements Set<Integer> {
+public class BitSetIntSet extends GridSerializableCollection<Integer> implements IntSet {
     /** */
     private static final long serialVersionUID = 0L;
 
@@ -44,13 +46,21 @@ public class BitSetIntSet extends GridSerializableCollection<Integer> implements
     }
 
     /**
-     *
      * @param initCap initial capacity.
      */
     public BitSetIntSet(int initCap) {
         bitSet = new BitSet(initCap);
     }
 
+    /**
+     * @param initCap initial capacity.
+     * @param coll initial collection.
+     */
+    public BitSetIntSet(int initCap, Collection<Integer> coll) {
+        bitSet = new BitSet(initCap);
+        addAll(coll);
+    }
+
     /** {@inheritDoc} */
     @Override public int size() {
         return size;
@@ -59,74 +69,91 @@ public class BitSetIntSet extends GridSerializableCollection<Integer> implements
     /** {@inheritDoc} */
     @Override public boolean contains(Object o) {
         if (o == null)
-            throw new UnsupportedOperationException("Null values are not supported!");
+            throw new NullPointerException("Null values are not supported!");
 
-        int val = (int)o;
+        return contains((int)o);
+    }
 
-        if (val < 0)
-            throw new UnsupportedOperationException("Negative values are not supported!");
+    /** {@inheritDoc} */
+    @Override public boolean contains(int element) {
+        if (element < 0)
+            return false;
 
-        return bitSet.get(val);
+        return bitSet.get(element);
     }
 
     /** {@inheritDoc} */
     @NotNull @Override public Iterator<Integer> iterator() {
         return new Iterator<Integer>() {
-            private int next = -1;
+            /** */
+            private int idx = -1;
+
+            /** */
+            private int nextIdx = -1;
 
             /** {@inheritDoc} */
             @Override public boolean hasNext() {
-                int nextBit = bitSet.nextSetBit(next + 1);
-
-                if (nextBit != -1) {
-                    next = nextBit;
-
-                    return true;
-                }
-                else
-                    return false;
+                return nextIdx == idx ? (nextIdx = bitSet.nextSetBit(idx + 1)) != -1 : nextIdx != -1;
             }
 
             /** {@inheritDoc} */
             @Override public Integer next() {
-                if (next == -1)
+                if (nextIdx == idx)
+                    nextIdx = bitSet.nextSetBit(idx + 1);
+
+                if (nextIdx == -1)
                     throw new NoSuchElementException();
 
-                return next;
+                idx = nextIdx;
+
+                return idx;
             }
         };
     }
 
-    /** Unsupported operation. */
+    /** {@inheritDoc} */
     @Override public boolean add(Integer integer) {
-        if (integer == null || integer < 0)
-            throw new UnsupportedOperationException("Negative or null values are not supported!");
+        if (integer == null)
+            throw new IllegalArgumentException("Negative or null values are not supported!");
 
-        boolean alreadySet = bitSet.get(integer);
+        return add((int)integer);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean add(int element) {
+        if (element < 0)
+            throw new IllegalArgumentException("Negative or null values are not supported!");
+
+        boolean alreadySet = bitSet.get(element);
 
         if (!alreadySet) {
-            bitSet.set(integer);
+            bitSet.set(element);
 
             size++;
-        }
 
-        return !alreadySet;
+            return true;
+        }
+        else
+            return false;
     }
 
-    /** Unsupported operation. */
+    /** {@inheritDoc} */
     @Override public boolean remove(Object o) {
         if (o == null)
-            throw new UnsupportedOperationException("Null values are not supported!");
+            return false;
 
-        int val = (int)o;
+        return remove((int)o);
+    }
 
-        if (val < 0)
-            throw new UnsupportedOperationException("Negative values are not supported!");
+    /** {@inheritDoc} */
+    @Override public boolean remove(int element) {
+        if (element < 0)
+            return false;
 
-        boolean alreadySet = bitSet.get(val);
+        boolean alreadySet = bitSet.get(element);
 
         if (alreadySet) {
-            bitSet.clear(val);
+            bitSet.clear(element);
 
             size--;
         }
@@ -135,6 +162,21 @@ public class BitSetIntSet extends GridSerializableCollection<Integer> implements
     }
 
     /** {@inheritDoc} */
+    @Override public int[] toIntArray() {
+        if (size == 0)
+            return EMPTY_INTS;
+
+        int[] arr = new int[size];
+
+        for (int i = 0, pos = -1; i < size; i++) {
+            pos = bitSet.nextSetBit(pos + 1);
+            arr[i] = pos;
+        }
+
+        return arr;
+    }
+
+    /** {@inheritDoc} */
     @Override public boolean containsAll(@NotNull Collection<?> c) {
         for (Object o : c) {
             if (!contains(o))
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/collection/ImmutableIntSet.java b/modules/core/src/main/java/org/apache/ignite/internal/util/collection/ImmutableIntSet.java
new file mode 100644
index 0000000..d5ad568
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/collection/ImmutableIntSet.java
@@ -0,0 +1,206 @@
+/*
+ * 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.ignite.internal.util.collection;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.jetbrains.annotations.NotNull;
+
+/** */
+public class ImmutableIntSet implements IntSet {
+    /** */
+    private static final ImmutableIntSet EMPTY_SET = new ImmutableIntSet(new BitSetIntSet(1));
+
+    /** Delegate. */
+    private final Set<Integer> delegate;
+
+    /**
+     * @param delegate Delegate.
+     */
+    public static ImmutableIntSet wrap(Set<Integer> delegate) {
+        return delegate instanceof ImmutableIntSet ? (ImmutableIntSet)delegate : new ImmutableIntSet(delegate);
+    }
+
+    /** */
+    public static ImmutableIntSet emptySet() {
+        return EMPTY_SET;
+    }
+
+    /**
+     * @param delegate Delegate.
+     */
+    public ImmutableIntSet(Set<Integer> delegate) {
+        this.delegate = delegate;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean contains(int element) {
+        if (delegate instanceof IntSet)
+            return ((IntSet)delegate).contains(element);
+        else
+            return delegate.contains(element);
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean containsAll(Collection<?> coll) {
+        return delegate.containsAll(coll);
+    }
+
+    /** {@inheritDoc} */
+    @Override public int size() {
+        return delegate.size();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isEmpty() {
+        return delegate.isEmpty();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean contains(Object o) {
+        return delegate.contains(o);
+    }
+
+    /** {@inheritDoc} */
+    @NotNull @Override public Object[] toArray() {
+        return delegate.toArray();
+    }
+
+    /** {@inheritDoc} */
+    @NotNull @Override public <T> T[] toArray(@NotNull T[] a) {
+        return delegate.toArray(a);
+    }
+
+    /** {@inheritDoc} */
+    @Override public int[] toIntArray() {
+        if (delegate instanceof IntSet)
+            return ((IntSet)delegate).toIntArray();
+        else
+            return U.toIntArray(delegate);
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return delegate.toString();
+    }
+
+    /** {@inheritDoc} */
+    @NotNull @Override public Iterator<Integer> iterator() {
+        return new Iterator<Integer>() {
+            /** */
+            private final Iterator<? extends Integer> iter = delegate.iterator();
+
+            /** {@inheritDoc} */
+            @Override public boolean hasNext() {
+                return iter.hasNext();
+            }
+
+            /** */
+            @Override public Integer next() {
+                return iter.next();
+            }
+
+            /** */
+            @Override public void remove() {
+                throw new UnsupportedOperationException();
+            }
+
+            /** */
+            @Override public void forEachRemaining(Consumer<? super Integer> act) {
+                iter.forEachRemaining(act);
+            }
+        };
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean add(int element) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean remove(int element) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean add(Integer integer) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean remove(Object o) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean addAll(@NotNull Collection<? extends Integer> c) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean retainAll(@NotNull Collection<?> c) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean removeAll(@NotNull Collection<?> c) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void clear() {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void forEach(Consumer<? super Integer> act) {
+        delegate.forEach(act);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean removeIf(Predicate<? super Integer> filter) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Spliterator<Integer> spliterator() {
+        return delegate.spliterator();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Stream<Integer> stream() {
+        return delegate.stream();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Stream<Integer> parallelStream() {
+        return delegate.parallelStream();
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/collection/IntHashMap.java b/modules/core/src/main/java/org/apache/ignite/internal/util/collection/IntHashMap.java
new file mode 100644
index 0000000..ba68056
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/collection/IntHashMap.java
@@ -0,0 +1,309 @@
+/*
+ * 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.ignite.internal.util.collection;
+
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * The simple map for primitive types base on Robin-hood hashing with backward shift.
+ */
+public class IntHashMap<V> implements IntMap<V> {
+    /** Initial capacity. */
+    public static final int INITIAL_CAPACITY = 8;
+
+    /** Maximum capacity. */
+    public static final int MAXIMUM_CAPACITY = 1 << 30;
+
+    /** Magic hash mixer. */
+    private static final int MAGIC_HASH_MIXER = 0x9E3779B9;
+
+    /** Array load percentage before resize. */
+    private static final float SCALE_LOAD_FACTOR = 0.7F;
+
+    /** Array load percentage before decrise size. */
+    private static final float COMPACT_LOAD_FACTOR = 0.2F;
+
+    /** Scale threshold. */
+    private int scaleThreshold;
+
+    /** Compact threshold. */
+    private int compactThreshold;
+
+    /** Entries. */
+    private Entry<V>[] entries;
+
+    /** Count of elements in Map. */
+    private int size;
+
+    /** Inner entry structure. */
+    private static class Entry<V> {
+        /** Key. */
+        public final int key;
+
+        /** Value. */
+        public final V val;
+
+        /** Default constructor. */
+        Entry(int key, V val) {
+            this.key = key;
+            this.val = val;
+        }
+
+        /** {@inheritDoc} */
+        @Override public String toString() {
+            return "{key=" + key + ", val=" + val + '}';
+        }
+    }
+
+    /** Default constructor. */
+    public IntHashMap() {
+        entries = (Entry<V>[])new Entry[INITIAL_CAPACITY];
+    }
+
+    /** Create map with preallocated array. */
+    public IntHashMap(int cap) {
+        int n = cap - 1;
+        n |= n >>> 1;
+        n |= n >>> 2;
+        n |= n >>> 4;
+        n |= n >>> 8;
+        n |= n >>> 16;
+
+        int entriesSize = (n < INITIAL_CAPACITY) ? INITIAL_CAPACITY : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
+
+        compactThreshold = (int)(COMPACT_LOAD_FACTOR * (entries.length >> 1));
+
+        scaleThreshold = (int)(entries.length * SCALE_LOAD_FACTOR);
+
+        entries = (Entry<V>[])new Entry[entriesSize];
+    }
+
+    /** {@inheritDoc} */
+    @Override public V get(int key) {
+        int idx = find(key);
+
+        return idx < 0 ? null : entries[idx].val;
+    }
+
+    /** {@inheritDoc} */
+    @Override public V put(int key, V val) {
+        return put0(new Entry<>(key, val));
+    }
+
+    /** {@inheritDoc} */
+    @Override public V remove(int key) {
+        if (entries.length != INITIAL_CAPACITY && compactThreshold > size)
+            resize(false);
+
+        int idx = find(key);
+
+        if (idx < 0)
+            return null;
+
+        size--;
+
+        V tmp = entries[idx].val;
+
+        // Backward shift
+        for (int i = 0; i < entries.length; i++) {
+            int curIdx = (idx + i) & (entries.length - 1);
+            int nextIdx = (idx + i + 1) & (entries.length - 1);
+
+            Entry<V> nextEntry = entries[nextIdx];
+
+            if (nextEntry == null || distance(nextIdx, nextEntry.key) == 0) {
+                entries[curIdx] = null;
+
+                return tmp;
+            }
+
+            entries[curIdx] = nextEntry;
+        }
+
+        throw new IllegalStateException("Unreachable state exception. Backward shift has a problem. " +
+            "Removing key: " + key + " map state: " + toString());
+    }
+
+    /** {@inheritDoc} */
+    @Override public V putIfAbsent(int key, V val) {
+        int idx = find(key);
+
+        if (idx < 0) {
+            put(key, val);
+
+            return null;
+        }
+        else
+            return entries[idx].val;
+    }
+
+    /** {@inheritDoc} */
+    @Override public <E extends Throwable> void forEach(EntryConsumer<V, E> act) throws E {
+        for (Entry<V> entry : entries)
+            if (entry != null)
+                act.accept(entry.key, entry.val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public int size() {
+        return size;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isEmpty() {
+        return size() == 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean containsKey(int key) {
+        return find(key) >= 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean containsValue(V val) {
+        return Arrays.stream(entries)
+            .filter(Objects::nonNull)
+            .anyMatch(entry -> Objects.equals(val, entry.val));
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        String strEntries = Arrays.stream(entries)
+            .filter(Objects::nonNull)
+            .map(Entry::toString)
+            .collect(Collectors.joining(","));
+
+        return "IntHashMap{size=" + size + ", entries=[" + strEntries + "]}";
+    }
+
+    /**
+     * Returns size of shift from ideal position computing via index(key).
+     *
+     * @param curIdx Current index.
+     * @param key Target key.
+     */
+    protected int distance(int curIdx, int key) {
+        int keyIdx = index(key);
+
+        return curIdx >= keyIdx ? curIdx - keyIdx : entries.length - keyIdx + curIdx;
+    }
+
+    /**
+     * Position in entites array.
+     *
+     * @param key Targert key.
+     */
+    protected int index(int key) {
+        return (entries.length - 1) & ((key ^ (key >>> 16)) * MAGIC_HASH_MIXER);
+    }
+
+    /**
+     * Does insert of entry.
+     */
+    private V put0(Entry<V> entry) {
+        if (size >= scaleThreshold)
+            resize(true);
+
+        Entry<V> savedEntry = entry;
+
+        int startKey = savedEntry.key;
+
+        for (int i = 0; i < entries.length; i++) {
+            int idx = (index(startKey) + i) & (entries.length - 1);
+
+            Entry<V> curEntry = entries[idx];
+
+            if (curEntry == null) {
+                entries[idx] = savedEntry;
+
+                size++;
+
+                return null;
+            }
+            else if (curEntry.key == savedEntry.key) {
+                entries[idx] = savedEntry;
+
+                return curEntry.val;
+            }
+
+            int curDist = distance(idx, curEntry.key);
+            int savedDist = distance(idx, savedEntry.key);
+
+            if (curDist < savedDist) {
+                entries[idx] = savedEntry;
+
+                savedEntry = curEntry;
+            }
+        }
+
+        throw new IllegalStateException("Unreachable state exception. Insertion position not found. " +
+            "Entry: " + entry + " map state: " + toString());
+    }
+
+    /**
+     * @param key Key.
+     * @return position of element or -1 if absent.
+     */
+    private int find(int key) {
+        int idx = index(key);
+
+        for (int keyDist = 0; keyDist < entries.length; keyDist++) {
+            int curIdx = (idx + keyDist) & (entries.length - 1);
+
+            Entry<V> entry = entries[curIdx];
+
+            if (entry == null)
+                return -1;
+            else if (entry.key == key)
+                return curIdx;
+
+            int entryDist = distance(curIdx, entry.key);
+
+            if (keyDist > entryDist)
+                return -1;
+        }
+
+        return -1;
+    }
+
+    /**
+     * Does resize of entities array.
+     */
+    private void resize(boolean increase) {
+        if (MAXIMUM_CAPACITY == entries.length)
+            throw new IllegalStateException("Maximum capacity: " + MAXIMUM_CAPACITY + " is reached.");
+
+        Entry<V>[] oldEntries = entries;
+
+        int newCap = increase ? entries.length << 1 : entries.length >> 1;
+
+        entries = (Entry<V>[])new Entry[newCap];
+
+        compactThreshold = (int)(COMPACT_LOAD_FACTOR * (entries.length >> 1));
+
+        scaleThreshold = (int)(entries.length * SCALE_LOAD_FACTOR);
+
+        size = 0;
+
+        for (Entry<V> entry : oldEntries)
+            if (entry != null)
+                put0(entry);
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/collection/IntMap.java b/modules/core/src/main/java/org/apache/ignite/internal/util/collection/IntMap.java
new file mode 100644
index 0000000..f1bbe51
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/collection/IntMap.java
@@ -0,0 +1,85 @@
+/*
+ * 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.ignite.internal.util.collection;
+
+/**
+ * The map for integer keys.
+ */
+public interface IntMap<V> {
+    /***
+     * The bridge for consuming all entries of the map.
+     */
+    public interface EntryConsumer<V, E extends Throwable> {
+        /**
+         * @param key entry key.
+         * @param val store value.
+         */
+        void accept(int key, V val) throws E;
+    }
+
+    /**
+     * Returns <tt>true</tt> if the map contains the key, otherwise <tt>false</tt>.
+     * @param key tests key value.
+     */
+    boolean containsKey(int key);
+
+    /**
+     * Returns <tt>true</tt> if the map contains one or more values, otherwise <tt>false</tt>.
+     * @param val value to be associated with the specified key.
+     */
+    boolean containsValue(V val);
+
+    /**
+     * Returns value associated with the key. if the map doesn't contain the key, returns null.
+     * @param key key with which the specified value is to be associated.
+     */
+    V get(int key);
+
+    /**
+     * Save the pair into the map. If a pair is present, returns old value and store new.
+     * @param key key with which the specified value is to be associated.
+     * @param val value to be associated with the specified key.
+     */
+    V put(int key, V val);
+
+    /**
+     * @param key key with which the specified value is to be associated.
+     */
+    V remove(int key);
+
+    /**
+     * Does put into the map if a pair isn't present, otherwise returns stored value.
+     * @param key key with which the specified value is to be associated..
+     * @param val value to be associated with the specified key..
+     */
+    V putIfAbsent(int key, V val);
+
+    /**
+     * This method work under a read lock, be careful with long operations inside.
+     * @param act Action.
+     */
+    <E extends Throwable> void forEach(EntryConsumer<V, E> act) throws E;
+
+    /** Returns count of elements. */
+    int size();
+
+    /**
+     * Returns <tt>true</tt> if this map contains no key-value mappings.
+     */
+    boolean isEmpty();
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/collection/IntRWHashMap.java b/modules/core/src/main/java/org/apache/ignite/internal/util/collection/IntRWHashMap.java
new file mode 100644
index 0000000..8d379bb
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/collection/IntRWHashMap.java
@@ -0,0 +1,141 @@
+/*
+ * 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.ignite.internal.util.collection;
+
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * Read-write lock wrapper for {@link IntMap}.
+ */
+public class IntRWHashMap<V> implements IntMap<V> {
+    /** RW Lock. */
+    private final ReadWriteLock lock = new ReentrantReadWriteLock();
+
+    /** Map delegate. */
+    private final IntHashMap<V> delegate;
+
+    /** Default constructor. */
+    public IntRWHashMap() {
+        delegate = new IntHashMap<>();
+    }
+
+    /** {@inheritDoc} */
+    @Override public V get(int key) {
+        lock.readLock().lock();
+        try {
+            return delegate.get(key);
+        }
+        finally {
+            lock.readLock().unlock();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public V put(int key, V val) {
+        lock.writeLock().lock();
+        try {
+            return delegate.put(key, val);
+        }
+        finally {
+            lock.writeLock().unlock();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public V remove(int key) {
+        lock.writeLock().lock();
+        try {
+            return delegate.remove(key);
+        }
+        finally {
+            lock.writeLock().unlock();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public V putIfAbsent(int key, V val) {
+        lock.writeLock().lock();
+        try {
+            return delegate.putIfAbsent(key, val);
+        }
+        finally {
+            lock.writeLock().unlock();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public <E extends Throwable> void forEach(EntryConsumer<V, E> act) throws E {
+        lock.readLock().lock();
+        try {
+            delegate.forEach(act);
+        }
+        finally {
+            lock.readLock().unlock();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public int size() {
+        lock.readLock().lock();
+        try {
+            return delegate.size();
+        }
+        finally {
+            lock.readLock().unlock();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean isEmpty() {
+        return size() == 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean containsKey(int key) {
+        lock.readLock().lock();
+        try {
+            return delegate.containsKey(key);
+        }
+        finally {
+            lock.readLock().unlock();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean containsValue(V val) {
+        lock.readLock().lock();
+        try {
+            return delegate.containsValue(val);
+        }
+        finally {
+            lock.readLock().unlock();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        lock.readLock().lock();
+        try {
+            return delegate.toString();
+        }
+        finally {
+            lock.readLock().unlock();
+        }
+    }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/collection/IntSet.java b/modules/core/src/main/java/org/apache/ignite/internal/util/collection/IntSet.java
new file mode 100644
index 0000000..caac3d3
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/collection/IntSet.java
@@ -0,0 +1,37 @@
+/*
+ * 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.ignite.internal.util.collection;
+
+import java.util.Set;
+
+/**
+ *
+ */
+public interface IntSet extends Set<Integer> {
+    /** Returns <tt>true</tt> if this set contains the specified element. */
+    boolean contains(int element);
+
+    /** Adds the specified element to this set. */
+    boolean add(int element);
+
+    /** Removes the specified element from this set. */
+    boolean remove(int element);
+
+    /** Returns array with primitive types **/
+    int[] toIntArray();
+}
diff --git a/modules/core/src/main/resources/META-INF/classnames.properties b/modules/core/src/main/resources/META-INF/classnames.properties
index 0f14356..bb2747b 100644
--- a/modules/core/src/main/resources/META-INF/classnames.properties
+++ b/modules/core/src/main/resources/META-INF/classnames.properties
@@ -1773,7 +1773,10 @@ org.apache.ignite.internal.transactions.IgniteTxSerializationCheckedException
 org.apache.ignite.internal.transactions.IgniteTxTimeoutCheckedException
 org.apache.ignite.internal.transactions.IgniteTxUnexpectedStateCheckedException
 org.apache.ignite.internal.transactions.TransactionCheckedException
-org.apache.ignite.internal.util.BitSetIntSet
+org.apache.ignite.internal.util.collection.BitSetIntSet
+org.apache.ignite.internal.util.collection.IntRWHashMap
+org.apache.ignite.internal.util.collection.ImmutableIntSet
+org.apache.ignite.internal.util.collection.IntSet
 org.apache.ignite.internal.util.F0$1
 org.apache.ignite.internal.util.F0$2
 org.apache.ignite.internal.util.F0$3
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/affinity/GridAffinityAssignmentV2Test.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/affinity/GridAffinityAssignmentV2Test.java
index f71a06b..d854304 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/affinity/GridAffinityAssignmentV2Test.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/affinity/GridAffinityAssignmentV2Test.java
@@ -29,7 +29,7 @@ import java.util.List;
 import java.util.Set;
 import java.util.UUID;
 import org.apache.ignite.cluster.ClusterNode;
-import org.apache.ignite.internal.util.BitSetIntSet;
+import org.apache.ignite.internal.util.collection.BitSetIntSet;
 import org.apache.ignite.lang.IgniteProductVersion;
 import org.apache.ignite.spi.discovery.DiscoveryMetricsProvider;
 import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryNode;
@@ -154,7 +154,7 @@ public class GridAffinityAssignmentV2Test {
 
         Set<Integer> unwrapped = (Set<Integer>)Whitebox.getInternalState(
             gridAffinityAssignment2.primaryPartitions(clusterNode1.id()),
-            "c"
+            "delegate"
         );
 
         if (AffinityAssignment.IGNITE_DISABLE_AFFINITY_MEMORY_OPTIMIZATION)
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/util/collection/AbstractBaseIntMapTest.java b/modules/core/src/test/java/org/apache/ignite/internal/util/collection/AbstractBaseIntMapTest.java
new file mode 100644
index 0000000..65b5521
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/util/collection/AbstractBaseIntMapTest.java
@@ -0,0 +1,248 @@
+/*
+ * 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.ignite.internal.util.collection;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicLong;
+import org.junit.Assert;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Common map tests.
+ */
+public abstract class AbstractBaseIntMapTest {
+    /**
+     * @return Returns particular implementation of IntMap.
+     */
+    protected abstract IntMap<String> instantiateMap();
+
+    /**
+     *
+     */
+    @Test
+    public void sizeOfMap() {
+        IntMap<String> map = instantiateMap();
+
+        assertTrue(map.isEmpty());
+        assertEquals(0, map.size());
+
+        for (int i = 1; i < 100_000; i++) {
+            map.put(i, value(i));
+            assertFalse(map.isEmpty());
+            assertEquals(i, map.size());
+        }
+
+        for (int i = 99_999; i > 0; i--) {
+            map.remove(i);
+            assertEquals(i-1, map.size());
+        }
+
+        assertTrue(map.isEmpty());
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void getEmpty() {
+        IntMap<String> map = instantiateMap();
+
+        assertNull(map.get(0));
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void putAndGet() {
+        IntMap<String> map = instantiateMap();
+
+        map.put(0, value(0));
+        map.put(239, value(239));
+        map.put(677, value(677));
+
+        assertEquals(value(0), map.get(0));
+        assertEquals(value(239), map.get(239));
+        assertEquals(value(677), map.get(677));
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void getAbsentKey() {
+        IntMap<String> map = instantiateMap();
+
+        map.put(0, value(0));
+        map.put(239, value(239));
+        map.put(677, value(677));
+
+        assertNull(map.get(32));
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void putPresentKey() {
+        IntMap<String> map = instantiateMap();
+
+        map.put(0, value(0));
+        String oldVal = map.put(0, value(1));
+
+        assertEquals(value(0), oldVal);
+        assertEquals(value(1), map.get(0));
+        assertEquals(1, map.size());
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void remove() {
+        IntMap<String> map = instantiateMap();
+
+        map.put(0, value(0));
+
+        assertEquals(value(0), map.remove(0));
+        assertEquals(0, map.size());
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void removeAbsentKey() {
+        IntMap<String> map = instantiateMap();
+
+        assertNull(map.remove(0));
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void putIfAbsent() {
+        IntMap<String> map = instantiateMap();
+
+        String newVal = map.putIfAbsent(1, value(1));
+
+        assertNull(newVal);
+
+        assertEquals(value(1), map.get(1));
+
+        String retry = map.putIfAbsent(1, value(2));
+
+        assertEquals(value(1), retry);
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void forEach() {
+        IntMap<String> map = instantiateMap();
+
+        for (int i = 1; i < 100_000; i++)
+            map.put(i, value(i));
+
+        final AtomicLong cntr = new AtomicLong(0);
+
+        map.forEach((key, value) -> cntr.addAndGet(key));
+
+        assertEquals(99_999L * 100_000L / 2, cntr.get());
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void contains() {
+        IntMap<String> map = instantiateMap();
+
+        for (int i = 1; i < 10_000; i++)
+            map.put(i, value(i));
+
+        for (int i = 1; i < 10_000; i++) {
+            assertTrue(map.containsKey(i));
+            assertTrue(map.containsValue(value(i)));
+        }
+
+        assertFalse(map.containsKey(0));
+        assertFalse(map.containsValue(value(0)));
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void compareWithReferenceImplementation() {
+        Map<Integer, String> originMap = new HashMap<>();
+        IntMap<String> testable = instantiateMap();
+
+        ThreadLocalRandom randomGen = ThreadLocalRandom.current();
+
+        for (int i = 0; i < 10_000_000; i++) {
+            int nextKey = randomGen.nextInt(0, 1_000_000);
+            int actId = randomGen.nextInt(0, 4);
+
+            if (actId == 0) {
+                String oPut = originMap.put(nextKey, value(nextKey));
+                String ePut = testable.put(nextKey, value(nextKey));
+
+                assertEquals(oPut, ePut);
+                assertEquals(originMap.containsKey(nextKey), testable.containsKey(nextKey));
+            }
+            else if (actId == 1) {
+                assertEquals(originMap.get(nextKey), testable.get(nextKey));
+                assertEquals(originMap.containsKey(nextKey), testable.containsKey(nextKey));
+            }
+            else if (actId == 2) {
+                String oPutAbs = originMap.putIfAbsent(nextKey, value(nextKey));
+                String ePutAbs = testable.putIfAbsent(nextKey, value(nextKey));
+
+                assertEquals(oPutAbs, ePutAbs);
+                assertEquals(originMap.containsKey(nextKey), testable.containsKey(nextKey));
+            }
+            else {
+                String oRmv = originMap.remove(nextKey);
+                String eRmv = testable.remove(nextKey);
+
+                assertEquals(oRmv, eRmv);
+                assertEquals(originMap.get(nextKey), testable.get(nextKey));
+                assertEquals(originMap.containsKey(nextKey), testable.containsKey(nextKey));
+            }
+
+            Assert.assertEquals(originMap.size(), testable.size());
+        }
+    }
+
+    /**
+     * @return Returns value by key.
+     */
+    protected String value(int key) {
+        return "Value-" + key;
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/util/BitSetIntSetTest.java b/modules/core/src/test/java/org/apache/ignite/internal/util/collection/BitSetIntSetTest.java
similarity index 62%
rename from modules/core/src/test/java/org/apache/ignite/internal/util/BitSetIntSetTest.java
rename to modules/core/src/test/java/org/apache/ignite/internal/util/collection/BitSetIntSetTest.java
index 0846f80..632e6c9 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/util/BitSetIntSetTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/util/collection/BitSetIntSetTest.java
@@ -15,15 +15,19 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.util;
+package org.apache.ignite.internal.util.collection;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
 import java.util.NoSuchElementException;
 import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
 import org.junit.Test;
 
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
 /**
  *
  */
@@ -41,7 +45,7 @@ public class BitSetIntSetTest extends GridCommonAbstractTest {
      *
      */
     private void sizeIsEmpty(int initCap) {
-        BitSetIntSet bitSetIntSet = initCap != 0 ? new BitSetIntSet(initCap) : new BitSetIntSet();
+        IntSet bitSetIntSet = initCap != 0 ? new BitSetIntSet(initCap) : new BitSetIntSet();
 
         assertEquals(0, bitSetIntSet.size());
         assertTrue(bitSetIntSet.isEmpty());
@@ -66,9 +70,55 @@ public class BitSetIntSetTest extends GridCommonAbstractTest {
         testIterator(1024);
     }
 
+    @Test(expected = NoSuchElementException.class)
+    public void shouldThrowExceptionIfHasNotNextElement() {
+        IntSet intSet = new BitSetIntSet(2);
+        intSet.add(1);
+        intSet.add(2);
+        Iterator<Integer> iterator = intSet.iterator();
+
+        iterator.next();
+        iterator.next();
+        iterator.next();
+    }
+
+    @Test
+    public void hasNextShouldBeIdempotent() {
+        IntSet intSet = new BitSetIntSet(3);
+        intSet.add(1);
+        intSet.add(2);
+        intSet.add(3);
+        Iterator<Integer> iter = intSet.iterator();
+
+        assertEquals(1, (int) iter.next());
+
+        iter.hasNext();
+        iter.hasNext();
+        iter.hasNext();
+        assertEquals(2, (int) iter.next());
+
+        iter.hasNext();
+        iter.hasNext();
+        assertEquals(3, (int) iter.next());
+    }
+
+    @Test
+    public void toIntArray() {
+        IntSet emptySet = new BitSetIntSet();
+        int[] emptyArr = emptySet.toIntArray();
+        assertThat(emptyArr.length, is(0));
+
+        IntSet withGapsSet = new BitSetIntSet(100, Arrays.asList(43, 23, 53));
+        int[] arr = withGapsSet.toIntArray();
+        assertThat(arr.length, is(3));
+        assertThat(arr[0], is(23));
+        assertThat(arr[1], is(43));
+        assertThat(arr[2], is(53));
+    }
+
     /** */
     private void testIterator(int initCap) {
-        BitSetIntSet bitSet = initCap != 0 ? new BitSetIntSet(initCap) : new BitSetIntSet();
+        IntSet bitSet = initCap != 0 ? new BitSetIntSet(initCap) : new BitSetIntSet();
 
         for (Integer ignored : bitSet)
             fail("BitSet is empty, shouldn't be invoked.");
@@ -142,22 +192,22 @@ public class BitSetIntSetTest extends GridCommonAbstractTest {
     /** */
     @Test
     public void testContains() {
-        BitSetIntSet bitSetInt = new BitSetIntSet();
-
-        bitSetInt.add(1);
-        bitSetInt.add(10);
-        bitSetInt.add(10);
-        bitSetInt.add(11);
-        bitSetInt.add(1025);
-
-        assertTrue(bitSetInt.contains(1));
-        assertFalse(bitSetInt.contains(2));
-        assertFalse(bitSetInt.contains(3));
-        assertFalse(bitSetInt.contains(4));
-        assertTrue(bitSetInt.contains(10));
-        assertTrue(bitSetInt.contains(11));
-        assertFalse(bitSetInt.contains(1024));
-        assertTrue(bitSetInt.contains(1025));
+        IntSet intSet = new BitSetIntSet();
+
+        intSet.add(Integer.valueOf(1));
+        intSet.add(10);
+        intSet.add(10);
+        intSet.add(11);
+        intSet.add(1025);
+
+        assertTrue(intSet.contains(1));
+        assertFalse(intSet.contains(2));
+        assertFalse(intSet.contains(3));
+        assertFalse(intSet.contains(4));
+        assertTrue(intSet.contains(10));
+        assertTrue(intSet.contains(11));
+        assertFalse(intSet.contains(1024));
+        assertTrue(intSet.contains(1025));
     }
 
     /**
@@ -165,20 +215,20 @@ public class BitSetIntSetTest extends GridCommonAbstractTest {
      */
     @Test
     public void testContainsAll() {
-        BitSetIntSet bitSetInt = new BitSetIntSet();
+        IntSet intSet = new BitSetIntSet();
 
-        bitSetInt.add(1);
-        bitSetInt.add(10);
-        bitSetInt.add(10);
-        bitSetInt.add(11);
-        bitSetInt.add(1025);
+        intSet.add(1);
+        intSet.add(10);
+        intSet.add(10);
+        intSet.add(11);
+        intSet.add(1025);
 
-        assertTrue(bitSetInt.containsAll(new ArrayList<Integer>() {{
+        assertTrue(intSet.containsAll(new ArrayList<Integer>() {{
             add(1);
             add(10);
         }}));
 
-        assertFalse(bitSetInt.containsAll(new ArrayList<Integer>() {{
+        assertFalse(intSet.containsAll(new ArrayList<Integer>() {{
             add(1);
             add(10);
             add(11);
@@ -186,7 +236,7 @@ public class BitSetIntSetTest extends GridCommonAbstractTest {
             add(1026);
         }}));
 
-        assertFalse(bitSetInt.containsAll(new ArrayList<Integer>() {{
+        assertFalse(intSet.containsAll(new ArrayList<Integer>() {{
             add(1);
             add(10);
             add(12);
@@ -198,22 +248,22 @@ public class BitSetIntSetTest extends GridCommonAbstractTest {
      */
     @Test
     public void testAddAllRemoveAllRetainAll() {
-        BitSetIntSet bitSetInt = new BitSetIntSet();
+        IntSet intSet = new BitSetIntSet();
 
-        bitSetInt.add(1);
-        bitSetInt.add(10);
-        bitSetInt.add(10);
-        bitSetInt.add(11);
-        bitSetInt.add(1025);
+        intSet.add(1);
+        intSet.add(10);
+        intSet.add(10);
+        intSet.add(11);
+        intSet.add(1025);
 
-        assertFalse(bitSetInt.addAll(new ArrayList<Integer>() {{
+        assertFalse(intSet.addAll(new ArrayList<Integer>() {{
             add(1);
             add(10);
         }}));
 
-        assertEquals(4, bitSetInt.size());
+        assertEquals(4, intSet.size());
 
-        assertTrue(bitSetInt.addAll(new ArrayList<Integer>() {{
+        assertTrue(intSet.addAll(new ArrayList<Integer>() {{
             add(1);
             add(10);
             add(11);
@@ -221,10 +271,10 @@ public class BitSetIntSetTest extends GridCommonAbstractTest {
             add(1026);
         }}));
 
-        assertEquals(5, bitSetInt.size());
+        assertEquals(5, intSet.size());
 
         try {
-            bitSetInt.retainAll(new ArrayList<Integer>() {{
+            intSet.retainAll(new ArrayList<Integer>() {{
                 add(10);
                 add(1025);
             }});
@@ -241,17 +291,17 @@ public class BitSetIntSetTest extends GridCommonAbstractTest {
      */
     @Test
     public void testToArray() {
-        BitSetIntSet bitSetInt = new BitSetIntSet();
+        IntSet intSet = new BitSetIntSet();
 
-        assertEquals(0, bitSetInt.toArray().length);
+        assertEquals(0, intSet.toArray().length);
 
-        bitSetInt.add(1);
-        bitSetInt.add(10);
-        bitSetInt.add(10);
-        bitSetInt.add(11);
-        bitSetInt.add(1025);
+        intSet.add(1);
+        intSet.add(10);
+        intSet.add(10);
+        intSet.add(11);
+        intSet.add(1025);
 
-        Object[] arr = bitSetInt.toArray();
+        Object[] arr = intSet.toArray();
 
         assertEquals(4, arr.length);
 
@@ -262,7 +312,7 @@ public class BitSetIntSetTest extends GridCommonAbstractTest {
 
         Integer[] input = new Integer[1];
 
-        Integer[] output = bitSetInt.toArray(input);
+        Integer[] output = intSet.toArray(input);
 
         assertNotSame(input, output);
 
@@ -275,7 +325,7 @@ public class BitSetIntSetTest extends GridCommonAbstractTest {
 
         input = new Integer[6];
 
-        output = bitSetInt.toArray(input);
+        output = intSet.toArray(input);
 
         assertSame(input, output);
 
@@ -294,54 +344,34 @@ public class BitSetIntSetTest extends GridCommonAbstractTest {
      */
     @Test
     public void testInvalidValues() {
-        BitSetIntSet bitSetInt = new BitSetIntSet();
+        IntSet intSet = new BitSetIntSet();
 
         try {
-            bitSetInt.add(null);
+            intSet.add(null);
             fail("add should fail here");
         }
-        catch (UnsupportedOperationException ignored) {
+        catch (IllegalArgumentException ignored) {
             // Ignored.
         }
 
         try {
-            bitSetInt.add(-1);
+            intSet.add(-1);
             fail("add should fail here");
         }
-        catch (UnsupportedOperationException ignored) {
+        catch (IllegalArgumentException ignored) {
             // Ignored.
         }
 
         try {
-            bitSetInt.contains(null);
+            intSet.contains(null);
             fail("contains should fail here");
         }
-        catch (UnsupportedOperationException ignored) {
+        catch (NullPointerException ignored) {
             // Ignored.
         }
 
-        try {
-            bitSetInt.contains(-1);
-            fail("contains should fail here");
-        }
-        catch (UnsupportedOperationException ignored) {
-            // Ignored.
-        }
-
-        try {
-            bitSetInt.remove(null);
-            fail("remove should fail here");
-        }
-        catch (UnsupportedOperationException ignored) {
-            // Ignored.
-        }
-
-        try {
-            bitSetInt.remove(-1);
-            fail("remove should fail here");
-        }
-        catch (UnsupportedOperationException ignored) {
-            // Ignored.
-        }
+        assertFalse(intSet.contains(-1));
+        assertFalse(intSet.remove(null));
+        assertFalse(intSet.remove(-1));
     }
 }
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/util/collection/ImmutableIntSetTest.java b/modules/core/src/test/java/org/apache/ignite/internal/util/collection/ImmutableIntSetTest.java
new file mode 100644
index 0000000..e323cb0
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/util/collection/ImmutableIntSetTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.ignite.internal.util.collection;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import org.junit.Test;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test for immutable int set wrapper
+ */
+public class ImmutableIntSetTest {
+    /** */
+    @Test
+    public void shouldWrapHashSet() {
+        IntSet immutableSet = ImmutableIntSet.wrap(new HashSet<>(Arrays.asList(2)));
+
+        assertTrue(immutableSet.contains(2));
+        assertFalse(immutableSet.contains(1));
+    }
+
+    /** */
+    @Test
+    public void emptySet() {
+        ImmutableIntSet emptySet = ImmutableIntSet.emptySet();
+
+        assertTrue(emptySet.isEmpty());
+        assertThat(emptySet.size(), is(0));
+    }
+
+    /** */
+    @Test
+    public void toIntArray() {
+        IntSet hashSet = ImmutableIntSet.wrap(new HashSet<>(Arrays.asList(2)));
+        int[] fromHash = hashSet.toIntArray();
+        assertThat(fromHash.length, is(1));
+        assertThat(fromHash[0], is(2));
+
+        IntSet bitSet = ImmutableIntSet.wrap(new BitSetIntSet(2, Arrays.asList(2)));
+        int[] fromBit = bitSet.toIntArray();
+        assertThat(fromBit.length, is(1));
+        assertThat(fromBit[0], is(2));
+    }
+
+    /** */
+    @Test
+    public void contains() {
+        IntSet immutableSet = ImmutableIntSet.wrap(new BitSetIntSet(2, Arrays.asList(2)));
+
+        assertThat(immutableSet.size(), is(1));
+        assertFalse(immutableSet.isEmpty());
+
+        assertTrue(immutableSet.contains(2));
+        assertFalse(immutableSet.contains(1));
+    }
+
+    /** */
+    @Test(expected = UnsupportedOperationException.class)
+    public void throwExceptionForAddOperation() {
+        IntSet immutableSet = ImmutableIntSet.wrap(new BitSetIntSet(4));
+
+        immutableSet.add(1);
+    }
+
+    /** */
+    @Test(expected = UnsupportedOperationException.class)
+    public void throwExceptionForRemoveOperation() {
+        IntSet immutableSet = ImmutableIntSet.wrap(new BitSetIntSet(2, Arrays.asList(2)));
+
+        immutableSet.remove(2);
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/util/collection/IntHashMapTest.java b/modules/core/src/test/java/org/apache/ignite/internal/util/collection/IntHashMapTest.java
new file mode 100644
index 0000000..0af34c9
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/util/collection/IntHashMapTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.ignite.internal.util.collection;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.junit.Assert;
+import org.junit.Test;
+
+import static org.apache.ignite.internal.util.collection.IntHashMap.INITIAL_CAPACITY;
+import static org.apache.ignite.internal.util.collection.IntHashMap.MAXIMUM_CAPACITY;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test for the specific implementation of IntMap.
+ */
+public class IntHashMapTest extends AbstractBaseIntMapTest {
+    /** {@inheritDoc} */
+    @Override protected IntMap<String> instantiateMap() {
+        return new IntHashMap<>();
+    }
+
+    /** */
+    @Test
+    public void removeBackShift() {
+        HashMap<Integer, Integer> bijection = new HashMap<>();
+        bijection.put(1, 14);
+        bijection.put(2, 14);
+        bijection.put(3, 14);
+        bijection.put(4, 14);
+        bijection.put(5, 14);
+
+        IntMap<String> directPositionMap = bijectionHashFunctionMap(bijection);
+
+        directPositionMap.put(1, value(1));
+        directPositionMap.put(2, value(2));
+        directPositionMap.put(3, value(3));
+        directPositionMap.put(4, value(4));
+        directPositionMap.put(5, value(5));
+
+        directPositionMap.remove(1);
+
+        Assert.assertEquals(4, directPositionMap.size());
+    }
+
+    /** */
+    @Test
+    public void distance() {
+        HashMap<Integer, Integer> bijection = new HashMap<>();
+        bijection.put(1, 14);
+        bijection.put(2, 14);
+        bijection.put(3, 14);
+        bijection.put(4, 14);
+        bijection.put(5, 14);
+        bijection.put(6, 14);
+        bijection.put(7, 14);
+        bijection.put(8, 14);
+        bijection.put(9, 14);
+
+        IntHashMap<String> directPositionMap = (IntHashMap<String>)bijectionHashFunctionMap(bijection);
+
+        directPositionMap.put(1, value(1));
+        directPositionMap.put(2, value(2));
+        directPositionMap.put(3, value(3));
+        directPositionMap.put(4, value(4));
+        directPositionMap.put(5, value(5));
+        directPositionMap.put(6, value(6));
+        directPositionMap.put(7, value(7));
+        directPositionMap.put(8, value(8));
+        directPositionMap.put(9, value(9));
+
+        assertEquals(0, directPositionMap.distance(14, 1));
+        assertEquals(1, directPositionMap.distance(15, 1));
+        assertEquals(2, directPositionMap.distance(0, 1));
+        assertEquals(3, directPositionMap.distance(1, 1));
+        assertEquals(4, directPositionMap.distance(2, 1));
+        assertEquals(5, directPositionMap.distance(3, 1));
+        assertEquals(15, directPositionMap.distance(13, 1));
+    }
+
+    /**
+     *
+     */
+    @Test
+    public void shouldAllocateMapWithInitialCapacity() {
+        assertEquals(INITIAL_CAPACITY, realCapacityForInitialSize(1));
+        assertEquals(16, realCapacityForInitialSize(9));
+        assertEquals(128, realCapacityForInitialSize(99));
+        assertEquals(256, realCapacityForInitialSize(155));
+        assertEquals(MAXIMUM_CAPACITY, realCapacityForInitialSize(Integer.MAX_VALUE));
+    }
+
+    /**
+     * @param initSize Initial size.
+     */
+    private int realCapacityForInitialSize(int initSize) {
+        return ((Object[]) U.field(new IntHashMap<String>(initSize), "entries")).length;
+    }
+
+    /**
+     * @param bijection Bijection.
+     */
+    private IntMap<String> bijectionHashFunctionMap(Map<Integer, Integer> bijection) {
+        return new IntHashMap<String>() {
+            @Override protected int index(int key) {
+                return bijection.get(key);
+            }
+        };
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/util/collection/IntRWHashMapTest.java b/modules/core/src/test/java/org/apache/ignite/internal/util/collection/IntRWHashMapTest.java
new file mode 100644
index 0000000..16b6d56
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/util/collection/IntRWHashMapTest.java
@@ -0,0 +1,28 @@
+/*
+ * 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.ignite.internal.util.collection;
+
+/**
+ * Base scenarios for read-write map.
+ */
+public class IntRWHashMapTest extends AbstractBaseIntMapTest {
+    /** {@inheritDoc} */
+    @Override protected IntMap<String> instantiateMap() {
+        return new IntRWHashMap<>();
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java
index 3d5bfe2..d6b9b99 100644
--- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java
+++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteBasicTestSuite.java
@@ -87,7 +87,8 @@ import org.apache.ignite.internal.processors.metastorage.persistence.Distributed
 import org.apache.ignite.internal.processors.odbc.OdbcConfigurationValidationSelfTest;
 import org.apache.ignite.internal.processors.odbc.OdbcEscapeSequenceSelfTest;
 import org.apache.ignite.internal.product.GridProductVersionSelfTest;
-import org.apache.ignite.internal.util.BitSetIntSetTest;
+import org.apache.ignite.internal.util.collection.BitSetIntSetTest;
+import org.apache.ignite.internal.util.collection.ImmutableIntSetTest;
 import org.apache.ignite.internal.util.GridCleanerTest;
 import org.apache.ignite.internal.util.nio.IgniteExceptionInNioWorkerSelfTest;
 import org.apache.ignite.marshaller.DynamicProxySerializationMultiJvmSelfTest;
@@ -198,6 +199,7 @@ import org.junit.runners.Suite;
     DataRegionMetricsSelfTest.class,
     SwapPathConstructionSelfTest.class,
     BitSetIntSetTest.class,
+    ImmutableIntSetTest.class,
 
     IgniteMarshallerCacheFSRestoreTest.class,
     IgniteMarshallerCacheClassNameConflictTest.class,