Bug 559187 - Reduce memory consumption and improve performance

Make use of primitive values and Eclipse Collections in several places

Change-Id: Iae66d0b122e98d8bdfd0ae62c778348966e4e3b8
Signed-off-by: Dirk Fauth <dirk.fauth@googlemail.com>
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/ColumnGroupHeaderLayerTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/ColumnGroupHeaderLayerTest.java
index 4a4139e..324c25b 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/ColumnGroupHeaderLayerTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/ColumnGroupHeaderLayerTest.java
@@ -225,6 +225,7 @@
         assertEquals(0, group1.getVisibleStartPosition());
         assertEquals(4, group1.getOriginalSpan());
         assertEquals(4, group1.getVisibleSpan());
+        assertEquals(3, group1.getGroupEndPosition(this.selectionLayer));
 
         Group group2 = this.columnGroupHeaderLayer.getGroupModel().getGroupByPosition(4);
         assertEquals(4, group2.getStartIndex());
@@ -232,6 +233,7 @@
         assertEquals(4, group2.getVisibleStartPosition());
         assertEquals(4, group2.getOriginalSpan());
         assertEquals(4, group2.getVisibleSpan());
+        assertEquals(7, group2.getGroupEndPosition(this.selectionLayer));
 
         Group group3 = this.columnGroupHeaderLayer.getGroupModel().getGroupByPosition(8);
         assertEquals(8, group3.getStartIndex());
@@ -239,6 +241,7 @@
         assertEquals(8, group3.getVisibleStartPosition());
         assertEquals(3, group3.getOriginalSpan());
         assertEquals(3, group3.getVisibleSpan());
+        assertEquals(10, group3.getGroupEndPosition(this.selectionLayer));
 
         Group group4 = this.columnGroupHeaderLayer.getGroupModel().getGroupByPosition(11);
         assertEquals(11, group4.getStartIndex());
@@ -246,6 +249,7 @@
         assertEquals(11, group4.getVisibleStartPosition());
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
+        assertEquals(13, group4.getGroupEndPosition(this.selectionLayer));
     }
 
     @Test
@@ -1144,6 +1148,7 @@
             assertEquals(-1, group2.getVisibleStartPosition());
             assertEquals(4, group2.getOriginalSpan());
             assertEquals(0, group2.getVisibleSpan());
+            assertEquals(-1, group2.getGroupEndPosition(this.selectionLayer));
         } else {
             fail("Column not hidden");
         }
@@ -1404,6 +1409,17 @@
         // collapse group with no static indexes
         this.columnGroupHeaderLayer.collapseGroup(0);
 
+        int[] hiddenColumnIndexes = this.columnGroupExpandCollapseLayer.getHiddenColumnIndexesArray();
+        assertEquals(3, hiddenColumnIndexes.length);
+        assertEquals(1, hiddenColumnIndexes[0]);
+        assertEquals(2, hiddenColumnIndexes[1]);
+        assertEquals(3, hiddenColumnIndexes[2]);
+
+        assertTrue(this.columnGroupExpandCollapseLayer.isColumnIndexHidden(1));
+        assertTrue(this.columnGroupExpandCollapseLayer.isColumnIndexHidden(2));
+        assertTrue(this.columnGroupExpandCollapseLayer.isColumnIndexHidden(3));
+        assertTrue(this.columnGroupExpandCollapseLayer.hasHiddenColumns());
+
         assertEquals(11, this.columnGroupExpandCollapseLayer.getColumnCount());
 
         Group group = this.columnGroupHeaderLayer.getGroupByPosition(0);
@@ -1423,6 +1439,14 @@
         // expand group with no static indexes
         this.columnGroupHeaderLayer.expandGroup(0);
 
+        hiddenColumnIndexes = this.columnGroupExpandCollapseLayer.getHiddenColumnIndexesArray();
+        assertEquals(0, hiddenColumnIndexes.length);
+
+        assertFalse(this.columnGroupExpandCollapseLayer.isColumnIndexHidden(1));
+        assertFalse(this.columnGroupExpandCollapseLayer.isColumnIndexHidden(2));
+        assertFalse(this.columnGroupExpandCollapseLayer.isColumnIndexHidden(3));
+        assertFalse(this.columnGroupExpandCollapseLayer.hasHiddenColumns());
+
         assertEquals(14, this.columnGroupExpandCollapseLayer.getColumnCount());
 
         group = this.columnGroupHeaderLayer.getGroupByPosition(0);
@@ -8387,10 +8411,10 @@
         assertEquals(3, group.getVisibleSpan());
         assertFalse(group.isCollapsed());
 
-        assertEquals(3, group.getMembers().size());
-        assertTrue(group.getMembers().contains(5));
-        assertTrue(group.getMembers().contains(6));
-        assertTrue(group.getMembers().contains(7));
+        assertEquals(3, group.getMembers().length);
+        assertTrue(group.hasMember(5));
+        assertTrue(group.hasMember(6));
+        assertTrue(group.hasMember(7));
     }
 
     @Test
@@ -8470,11 +8494,11 @@
         // expand again
         this.columnGroupHeaderLayer.expandGroup(4);
 
-        assertEquals(4, group.getMembers().size());
-        assertTrue(group.getMembers().contains(4));
-        assertTrue(group.getMembers().contains(5));
-        assertTrue(group.getMembers().contains(6));
-        assertTrue(group.getMembers().contains(7));
+        assertEquals(4, group.getMembers().length);
+        assertTrue(group.hasMember(4));
+        assertTrue(group.hasMember(5));
+        assertTrue(group.hasMember(6));
+        assertTrue(group.hasMember(7));
 
         verifyCleanState();
     }
@@ -8558,11 +8582,11 @@
         // expand again
         this.columnGroupHeaderLayer.expandGroup(4);
 
-        assertEquals(4, group.getMembers().size());
-        assertTrue(group.getMembers().contains(4));
-        assertTrue(group.getMembers().contains(5));
-        assertTrue(group.getMembers().contains(6));
-        assertTrue(group.getMembers().contains(7));
+        assertEquals(4, group.getMembers().length);
+        assertTrue(group.hasMember(4));
+        assertTrue(group.hasMember(5));
+        assertTrue(group.hasMember(6));
+        assertTrue(group.hasMember(7));
 
         verifyCleanState();
     }
@@ -8606,11 +8630,11 @@
         assertEquals(4, group.getVisibleSpan());
         assertFalse(group.isCollapsed());
 
-        assertEquals(4, group.getMembers().size());
-        assertTrue(group.getMembers().contains(5));
-        assertTrue(group.getMembers().contains(6));
-        assertTrue(group.getMembers().contains(7));
-        assertTrue(group.getMembers().contains(4));
+        assertEquals(4, group.getMembers().length);
+        assertTrue(group.hasMember(5));
+        assertTrue(group.hasMember(6));
+        assertTrue(group.hasMember(7));
+        assertTrue(group.hasMember(4));
     }
 
     @Test
@@ -8792,11 +8816,11 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
 
-        assertEquals(4, group.getMembers().size());
-        assertTrue(group.getMembers().contains(4));
-        assertTrue(group.getMembers().contains(5));
-        assertTrue(group.getMembers().contains(6));
-        assertTrue(group.getMembers().contains(7));
+        assertEquals(4, group.getMembers().length);
+        assertTrue(group.hasMember(4));
+        assertTrue(group.hasMember(5));
+        assertTrue(group.hasMember(6));
+        assertTrue(group.hasMember(7));
     }
 
     @Test
@@ -8925,17 +8949,17 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
 
-        assertEquals(5, group1.getMembers().size());
-        assertTrue(group1.getMembers().contains(0));
-        assertTrue(group1.getMembers().contains(1));
-        assertTrue(group1.getMembers().contains(2));
-        assertTrue(group1.getMembers().contains(3));
-        assertTrue(group1.getMembers().contains(4));
+        assertEquals(5, group1.getMembers().length);
+        assertTrue(group1.hasMember(0));
+        assertTrue(group1.hasMember(1));
+        assertTrue(group1.hasMember(2));
+        assertTrue(group1.hasMember(3));
+        assertTrue(group1.hasMember(4));
 
-        assertEquals(3, group2.getMembers().size());
-        assertTrue(group2.getMembers().contains(5));
-        assertTrue(group2.getMembers().contains(6));
-        assertTrue(group2.getMembers().contains(7));
+        assertEquals(3, group2.getMembers().length);
+        assertTrue(group2.hasMember(5));
+        assertTrue(group2.hasMember(6));
+        assertTrue(group2.hasMember(7));
     }
 
     @Test
@@ -9065,17 +9089,17 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
 
-        assertEquals(5, group1.getMembers().size());
-        assertTrue(group1.getMembers().contains(0));
-        assertTrue(group1.getMembers().contains(1));
-        assertTrue(group1.getMembers().contains(2));
-        assertTrue(group1.getMembers().contains(3));
-        assertTrue(group1.getMembers().contains(4));
+        assertEquals(5, group1.getMembers().length);
+        assertTrue(group1.hasMember(0));
+        assertTrue(group1.hasMember(1));
+        assertTrue(group1.hasMember(2));
+        assertTrue(group1.hasMember(3));
+        assertTrue(group1.hasMember(4));
 
-        assertEquals(3, group2.getMembers().size());
-        assertTrue(group2.getMembers().contains(5));
-        assertTrue(group2.getMembers().contains(6));
-        assertTrue(group2.getMembers().contains(7));
+        assertEquals(3, group2.getMembers().length);
+        assertTrue(group2.hasMember(5));
+        assertTrue(group2.hasMember(6));
+        assertTrue(group2.hasMember(7));
     }
 
     @Test
@@ -9201,16 +9225,16 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
 
-        assertEquals(4, group3.getMembers().size());
-        assertTrue(group3.getMembers().contains(8));
-        assertTrue(group3.getMembers().contains(9));
-        assertTrue(group3.getMembers().contains(10));
-        assertTrue(group3.getMembers().contains(4));
+        assertEquals(4, group3.getMembers().length);
+        assertTrue(group3.hasMember(8));
+        assertTrue(group3.hasMember(9));
+        assertTrue(group3.hasMember(10));
+        assertTrue(group3.hasMember(4));
 
-        assertEquals(3, group2.getMembers().size());
-        assertTrue(group2.getMembers().contains(5));
-        assertTrue(group2.getMembers().contains(6));
-        assertTrue(group2.getMembers().contains(7));
+        assertEquals(3, group2.getMembers().length);
+        assertTrue(group2.hasMember(5));
+        assertTrue(group2.hasMember(6));
+        assertTrue(group2.hasMember(7));
     }
 
     @Test
@@ -9336,16 +9360,16 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
 
-        assertEquals(4, group3.getMembers().size());
-        assertTrue(group3.getMembers().contains(8));
-        assertTrue(group3.getMembers().contains(9));
-        assertTrue(group3.getMembers().contains(10));
-        assertTrue(group3.getMembers().contains(4));
+        assertEquals(4, group3.getMembers().length);
+        assertTrue(group3.hasMember(8));
+        assertTrue(group3.hasMember(9));
+        assertTrue(group3.hasMember(10));
+        assertTrue(group3.hasMember(4));
 
-        assertEquals(3, group2.getMembers().size());
-        assertTrue(group2.getMembers().contains(5));
-        assertTrue(group2.getMembers().contains(6));
-        assertTrue(group2.getMembers().contains(7));
+        assertEquals(3, group2.getMembers().length);
+        assertTrue(group2.hasMember(5));
+        assertTrue(group2.hasMember(6));
+        assertTrue(group2.hasMember(7));
     }
 
     @Test
@@ -9435,11 +9459,11 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
 
-        assertEquals(4, group2.getMembers().size());
-        assertTrue(group2.getMembers().contains(4));
-        assertTrue(group2.getMembers().contains(5));
-        assertTrue(group2.getMembers().contains(6));
-        assertTrue(group2.getMembers().contains(7));
+        assertEquals(4, group2.getMembers().length);
+        assertTrue(group2.hasMember(4));
+        assertTrue(group2.hasMember(5));
+        assertTrue(group2.hasMember(6));
+        assertTrue(group2.hasMember(7));
     }
 
     @Test
@@ -9530,11 +9554,11 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
 
-        assertEquals(4, group2.getMembers().size());
-        assertTrue(group2.getMembers().contains(4));
-        assertTrue(group2.getMembers().contains(5));
-        assertTrue(group2.getMembers().contains(6));
-        assertTrue(group2.getMembers().contains(7));
+        assertEquals(4, group2.getMembers().length);
+        assertTrue(group2.hasMember(4));
+        assertTrue(group2.hasMember(5));
+        assertTrue(group2.hasMember(6));
+        assertTrue(group2.hasMember(7));
     }
 
     @Test
@@ -9682,13 +9706,13 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
 
-        assertEquals(3, group2.getMembers().size());
-        assertTrue(group2.getMembers().contains(4));
-        assertTrue(group2.getMembers().contains(5));
-        assertTrue(group2.getMembers().contains(7));
+        assertEquals(3, group2.getMembers().length);
+        assertTrue(group2.hasMember(4));
+        assertTrue(group2.hasMember(5));
+        assertTrue(group2.hasMember(7));
 
-        assertEquals(1, group2.getStaticIndexes().size());
-        assertTrue(group2.getStaticIndexes().contains(5));
+        assertEquals(1, group2.getStaticIndexes().length);
+        assertTrue(group2.containsStaticIndex(5));
     }
 
     @Test
@@ -9838,13 +9862,13 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
 
-        assertEquals(3, group2.getMembers().size());
-        assertTrue(group2.getMembers().contains(4));
-        assertTrue(group2.getMembers().contains(5));
-        assertTrue(group2.getMembers().contains(7));
+        assertEquals(3, group2.getMembers().length);
+        assertTrue(group2.hasMember(4));
+        assertTrue(group2.hasMember(5));
+        assertTrue(group2.hasMember(7));
 
-        assertEquals(1, group2.getStaticIndexes().size());
-        assertTrue(group2.getStaticIndexes().contains(5));
+        assertEquals(1, group2.getStaticIndexes().length);
+        assertTrue(group2.containsStaticIndex(5));
     }
 
     @Test
@@ -10475,11 +10499,11 @@
         assertEquals(4, group2.getOriginalSpan());
         assertEquals(4, group2.getVisibleSpan());
 
-        assertEquals(4, group2.getMembers().size());
-        assertTrue(group2.getMembers().contains(4));
-        assertTrue(group2.getMembers().contains(5));
-        assertTrue(group2.getMembers().contains(6));
-        assertTrue(group2.getMembers().contains(7));
+        assertEquals(4, group2.getMembers().length);
+        assertTrue(group2.hasMember(4));
+        assertTrue(group2.hasMember(5));
+        assertTrue(group2.hasMember(6));
+        assertTrue(group2.hasMember(7));
     }
 
     @Test
@@ -10705,12 +10729,12 @@
         assertEquals(4, group.getVisibleSpan());
         assertFalse(group.isCollapsed());
 
-        Collection<Integer> members = group.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(0));
-        assertTrue(members.contains(2));
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(6));
+        int[] members = group.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(2, members[1]);
+        assertEquals(4, members[2]);
+        assertEquals(6, members[3]);
 
         assertEquals(0, this.columnGroupHeaderLayer.getColumnIndexByPosition(0));
         assertEquals(2, this.columnGroupHeaderLayer.getColumnIndexByPosition(1));
@@ -10756,12 +10780,12 @@
         assertEquals(4, group.getVisibleSpan());
         assertFalse(group.isCollapsed());
 
-        Collection<Integer> members = group.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(0));
-        assertTrue(members.contains(1));
-        assertTrue(members.contains(2));
-        assertTrue(members.contains(3));
+        int[] members = group.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(1, members[1]);
+        assertEquals(2, members[2]);
+        assertEquals(3, members[3]);
 
         ILayerCell cell = this.columnGroupHeaderLayer.getCellByPosition(0, 0);
         assertEquals(0, cell.getOriginColumnPosition());
@@ -10820,11 +10844,11 @@
         assertEquals(3, group.getVisibleSpan());
         assertFalse(group.isCollapsed());
 
-        Collection<Integer> members = group.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(0));
-        assertTrue(members.contains(1));
-        assertTrue(members.contains(2));
+        int[] members = group.getMembers();
+        assertEquals(3, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(1, members[1]);
+        assertEquals(2, members[2]);
 
         assertNull(groupModel.getGroupByPosition(3));
 
@@ -10861,12 +10885,12 @@
         assertEquals(4, group1.getVisibleSpan());
         assertFalse(group1.isCollapsed());
 
-        Collection<Integer> members = group1.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(0));
-        assertTrue(members.contains(1));
-        assertTrue(members.contains(2));
-        assertTrue(members.contains(3));
+        int[] members = group1.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(1, members[1]);
+        assertEquals(2, members[2]);
+        assertEquals(3, members[3]);
 
         Group group2 = groupModel.getGroupByPosition(5);
         assertEquals(5, group2.getStartIndex());
@@ -10877,10 +10901,10 @@
         assertFalse(group2.isCollapsed());
 
         members = group2.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        assertEquals(3, members.length);
+        assertEquals(5, members[0]);
+        assertEquals(6, members[1]);
+        assertEquals(7, members[2]);
 
         assertNull(groupModel.getGroupByPosition(4));
 
@@ -10941,11 +10965,11 @@
         assertEquals(3, group.getVisibleSpan());
         assertFalse(group.isCollapsed());
 
-        Collection<Integer> members = group.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(0));
-        assertTrue(members.contains(1));
-        assertTrue(members.contains(3));
+        int[] members = group.getMembers();
+        assertEquals(3, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(1, members[1]);
+        assertEquals(3, members[2]);
 
         assertNull(groupModel.getGroupByPosition(3));
 
@@ -10995,12 +11019,12 @@
         assertEquals(4, group1.getVisibleSpan());
         assertFalse(group1.isCollapsed());
 
-        Collection<Integer> members = group1.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(0));
-        assertTrue(members.contains(1));
-        assertTrue(members.contains(2));
-        assertTrue(members.contains(3));
+        int[] members = group1.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(1, members[1]);
+        assertEquals(2, members[2]);
+        assertEquals(3, members[3]);
 
         assertNull(groupModel.getGroupByPosition(4));
         assertNull(groupModel.getGroupByPosition(5));
@@ -11014,9 +11038,9 @@
         assertFalse(group2.isCollapsed());
 
         members = group2.getMembers();
-        assertEquals(2, members.size());
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        assertEquals(2, members.length);
+        assertEquals(6, members[0]);
+        assertEquals(7, members[1]);
 
         ILayerCell cell = this.columnGroupHeaderLayer.getCellByPosition(0, 0);
         assertEquals(0, cell.getOriginColumnPosition());
@@ -11088,10 +11112,10 @@
         assertEquals(2, group1.getVisibleSpan());
         assertFalse(group1.isCollapsed());
 
-        Collection<Integer> members = group1.getMembers();
-        assertEquals(2, members.size());
-        assertTrue(members.contains(0));
-        assertTrue(members.contains(1));
+        int[] members = group1.getMembers();
+        assertEquals(2, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(1, members[1]);
 
         assertNull(groupModel.getGroupByPosition(2));
         assertNull(groupModel.getGroupByPosition(3));
@@ -11105,11 +11129,11 @@
         assertFalse(group2.isCollapsed());
 
         members = group2.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
 
         ILayerCell cell = this.columnGroupHeaderLayer.getCellByPosition(0, 0);
         assertEquals(0, cell.getOriginColumnPosition());
@@ -11181,10 +11205,10 @@
         assertEquals(2, group1.getVisibleSpan());
         assertFalse(group1.isCollapsed());
 
-        Collection<Integer> members = group1.getMembers();
-        assertEquals(2, members.size());
-        assertTrue(members.contains(0));
-        assertTrue(members.contains(3));
+        int[] members = group1.getMembers();
+        assertEquals(2, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(3, members[1]);
 
         assertNull(groupModel.getGroupByPosition(2));
         assertNull(groupModel.getGroupByPosition(3));
@@ -11198,11 +11222,11 @@
         assertFalse(group2.isCollapsed());
 
         members = group2.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
 
         ILayerCell cell = this.columnGroupHeaderLayer.getCellByPosition(0, 0);
         assertEquals(0, cell.getOriginColumnPosition());
@@ -11276,10 +11300,10 @@
         assertEquals(2, group1.getVisibleSpan());
         assertFalse(group1.isCollapsed());
 
-        Collection<Integer> members = group1.getMembers();
-        assertEquals(2, members.size());
-        assertTrue(members.contains(1));
-        assertTrue(members.contains(2));
+        int[] members = group1.getMembers();
+        assertEquals(2, members.length);
+        assertEquals(1, members[0]);
+        assertEquals(2, members[1]);
 
         assertNull(groupModel.getGroupByPosition(3));
 
@@ -11292,11 +11316,11 @@
         assertFalse(group2.isCollapsed());
 
         members = group2.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
 
         ILayerCell cell = this.columnGroupHeaderLayer.getCellByPosition(0, 0);
         assertEquals(0, cell.getOriginColumnPosition());
@@ -11488,6 +11512,20 @@
     }
 
     @Test
+    public void shouldRemoveAllFromColumnGroup() {
+        Group group = this.groupModel.getGroupByPosition(0);
+        group.removeMembers(0, 1, 2, 3);
+
+        assertEquals(0, group.getMembers().length);
+        assertFalse(group.hasMember(0));
+        assertFalse(group.hasMember(1));
+        assertFalse(group.hasMember(2));
+        assertFalse(group.hasMember(3));
+
+        assertEquals(-1, group.getGroupEndPosition(this.selectionLayer));
+    }
+
+    @Test
     public void shouldNotRemoveUnbreakableColumnGroup() {
         this.columnGroupHeaderLayer.setGroupUnbreakable(4, true);
 
@@ -12189,7 +12227,7 @@
         this.gridLayer.doCommand(new MultiColumnHideCommand(this.gridLayer, 9));
 
         // reorder first and second column to second group end
-        this.gridLayer.doCommand(new MultiColumnReorderCommand(this.gridLayer, Arrays.asList(1, 2), 9));
+        this.gridLayer.doCommand(new MultiColumnReorderCommand(this.gridLayer, new int[] { 1, 2 }, 9));
 
         ILayerCell cell = this.columnGroupHeaderLayer.getCellByPosition(0, 0);
         assertEquals(0, cell.getOriginColumnPosition());
@@ -12395,7 +12433,7 @@
         assertEquals(2, group.getVisibleSpan());
 
         // reorder first position between first and second group
-        this.gridLayer.doCommand(new MultiColumnReorderCommand(this.gridLayer, Arrays.asList(1), 4));
+        this.gridLayer.doCommand(new MultiColumnReorderCommand(this.gridLayer, new int[] { 1 }, 4));
 
         assertEquals(1, group.getStartIndex());
         assertEquals(1, group.getVisibleStartIndex());
@@ -12603,7 +12641,7 @@
         assertEquals(3, group3.getVisibleSpan());
 
         // reorder first position between second and third group
-        this.gridLayer.doCommand(new MultiColumnReorderCommand(this.gridLayer, Arrays.asList(1), 8));
+        this.gridLayer.doCommand(new MultiColumnReorderCommand(this.gridLayer, new int[] { 1 }, 8));
 
         assertEquals(1, group1.getStartIndex());
         assertEquals(1, group1.getVisibleStartIndex());
@@ -12754,7 +12792,7 @@
         assertEquals(2, group.getVisibleSpan());
 
         // reorder fourth position to first position
-        this.gridLayer.doCommand(new MultiColumnReorderCommand(this.gridLayer, Arrays.asList(3), 1));
+        this.gridLayer.doCommand(new MultiColumnReorderCommand(this.gridLayer, new int[] { 3 }, 1));
 
         assertEquals(0, group.getStartIndex());
         assertEquals(1, group.getVisibleStartIndex());
@@ -12916,7 +12954,7 @@
         assertEquals(4, group2.getVisibleSpan());
 
         // reorder first position in second group to first position in table
-        this.gridLayer.doCommand(new MultiColumnReorderCommand(this.gridLayer, Arrays.asList(4), 1));
+        this.gridLayer.doCommand(new MultiColumnReorderCommand(this.gridLayer, new int[] { 4 }, 1));
 
         assertEquals(0, group1.getStartIndex());
         assertEquals(1, group1.getVisibleStartIndex());
@@ -13582,7 +13620,7 @@
         this.columnGroupHeaderLayer.collapseGroup(11);
 
         // reorder first position to table end
-        this.gridLayer.doCommand(new MultiColumnReorderCommand(this.gridLayer, Arrays.asList(1), 13));
+        this.gridLayer.doCommand(new MultiColumnReorderCommand(this.gridLayer, new int[] { 1 }, 13));
 
         assertEquals(1, group1.getStartIndex());
         assertEquals(1, group1.getVisibleStartIndex());
@@ -13860,7 +13898,7 @@
         this.columnGroupHeaderLayer.collapseGroup(0);
 
         // reorder first position to table end
-        this.gridLayer.doCommand(new MultiColumnReorderCommand(this.gridLayer, Arrays.asList(1), 11));
+        this.gridLayer.doCommand(new MultiColumnReorderCommand(this.gridLayer, new int[] { 1 }, 11));
 
         assertEquals(1, group1.getStartIndex());
         assertEquals(1, group1.getVisibleStartIndex());
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/ColumnGroupUtilsTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/ColumnGroupUtilsTest.java
index 2a039b6..5233238 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/ColumnGroupUtilsTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/ColumnGroupUtilsTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2019 Dirk Fauth.
+ * Copyright (c) 2019, 2020 Dirk Fauth.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -34,6 +34,7 @@
 import org.eclipse.nebula.widgets.nattable.grid.layer.GridLayer;
 import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer;
 import org.eclipse.nebula.widgets.nattable.group.ColumnGroupUtils;
+import org.eclipse.nebula.widgets.nattable.group.performance.GroupModel.Group;
 import org.eclipse.nebula.widgets.nattable.hideshow.ColumnHideShowLayer;
 import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
 import org.eclipse.nebula.widgets.nattable.layer.ILayer;
@@ -451,4 +452,18 @@
         // assertTrue(ColumnGroupUtils.isBetweenTwoGroups(this.columnGroupHeaderLayer,
         // 1, 13, true, MoveDirectionEnum.RIGHT));
     }
+
+    @Test
+    public void shouldTestIsGroupReordered() {
+        Group group = this.columnGroupHeaderLayer.getGroupByPosition(0);
+
+        // test all group positions
+        assertTrue(ColumnGroupUtils.isGroupReordered(group, new int[] { 0, 1, 2, 3 }));
+
+        // test less group positions
+        assertFalse(ColumnGroupUtils.isGroupReordered(group, new int[] { 1, 2, 3 }));
+
+        // test more group positions
+        assertTrue(ColumnGroupUtils.isGroupReordered(group, new int[] { 0, 1, 2, 3, 4, 5 }));
+    }
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/GroupModelTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/GroupModelTest.java
index f9861a3..22755bd 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/GroupModelTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/GroupModelTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2019 Dirk Fauth.
+ * Copyright (c) 2019, 2020 Dirk Fauth.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -16,7 +16,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
-import java.util.Collection;
+import java.util.Arrays;
 import java.util.Properties;
 
 import org.eclipse.nebula.widgets.nattable.group.performance.GroupModel.Group;
@@ -162,22 +162,22 @@
     @Test
     public void shouldFillInternalMemberIndexes() {
         Group group1 = this.model.getGroupByPosition(0);
-        assertEquals(4, group1.getMembers().size());
-        assertTrue(group1.getMembers().contains(0));
-        assertTrue(group1.getMembers().contains(1));
-        assertTrue(group1.getMembers().contains(2));
-        assertTrue(group1.getMembers().contains(3));
+        assertEquals(4, group1.getMembers().length);
+        assertTrue(group1.hasMember(0));
+        assertTrue(group1.hasMember(1));
+        assertTrue(group1.hasMember(2));
+        assertTrue(group1.hasMember(3));
 
         Group group2 = this.model.getGroupByPosition(5);
-        assertEquals(3, group2.getMembers().size());
-        assertTrue(group2.getMembers().contains(5));
-        assertTrue(group2.getMembers().contains(6));
-        assertTrue(group2.getMembers().contains(7));
+        assertEquals(3, group2.getMembers().length);
+        assertTrue(group2.hasMember(5));
+        assertTrue(group2.hasMember(6));
+        assertTrue(group2.hasMember(7));
 
         Group group3 = this.model.getGroupByPosition(12);
-        assertEquals(2, group3.getMembers().size());
-        assertTrue(group3.getMembers().contains(12));
-        assertTrue(group3.getMembers().contains(13));
+        assertEquals(2, group3.getMembers().length);
+        assertTrue(group3.hasMember(12));
+        assertTrue(group3.hasMember(13));
     }
 
     @Test
@@ -191,10 +191,10 @@
         assertEquals(3, group.getVisibleSpan());
         assertTrue(this.model.isPartOfAGroup(7));
 
-        assertEquals(3, group.getMembers().size());
-        assertTrue(group.getMembers().contains(5));
-        assertTrue(group.getMembers().contains(6));
-        assertTrue(group.getMembers().contains(7));
+        assertEquals(3, group.getMembers().length);
+        assertTrue(group.hasMember(5));
+        assertTrue(group.hasMember(6));
+        assertTrue(group.hasMember(7));
 
         this.model.removePositionsFromGroup(6);
 
@@ -205,9 +205,9 @@
         assertEquals(2, group.getVisibleSpan());
         assertFalse(this.model.isPartOfAGroup(7));
 
-        assertEquals(2, group.getMembers().size());
-        assertTrue(group.getMembers().contains(5));
-        assertTrue(group.getMembers().contains(6));
+        assertEquals(2, group.getMembers().length);
+        assertTrue(group.hasMember(5));
+        assertTrue(group.hasMember(6));
     }
 
     @Test
@@ -221,10 +221,10 @@
         assertEquals(3, group.getVisibleSpan());
         assertTrue(this.model.isPartOfAGroup(7));
 
-        assertEquals(3, group.getMembers().size());
-        assertTrue(group.getMembers().contains(5));
-        assertTrue(group.getMembers().contains(6));
-        assertTrue(group.getMembers().contains(7));
+        assertEquals(3, group.getMembers().length);
+        assertTrue(group.hasMember(5));
+        assertTrue(group.hasMember(6));
+        assertTrue(group.hasMember(7));
 
         this.model.removePositionsFromGroup(7);
 
@@ -235,9 +235,9 @@
         assertEquals(2, group.getVisibleSpan());
         assertFalse(this.model.isPartOfAGroup(7));
 
-        assertEquals(2, group.getMembers().size());
-        assertTrue(group.getMembers().contains(5));
-        assertTrue(group.getMembers().contains(6));
+        assertEquals(2, group.getMembers().length);
+        assertTrue(group.hasMember(5));
+        assertTrue(group.hasMember(6));
     }
 
     @Test
@@ -251,10 +251,10 @@
         assertEquals(3, group.getVisibleSpan());
         assertTrue(this.model.isPartOfAGroup(7));
 
-        assertEquals(3, group.getMembers().size());
-        assertTrue(group.getMembers().contains(5));
-        assertTrue(group.getMembers().contains(6));
-        assertTrue(group.getMembers().contains(7));
+        assertEquals(3, group.getMembers().length);
+        assertTrue(group.hasMember(5));
+        assertTrue(group.hasMember(6));
+        assertTrue(group.hasMember(7));
 
         this.model.removePositionsFromGroup(5);
 
@@ -266,9 +266,9 @@
         assertFalse(this.model.isPartOfAGroup(5));
         assertTrue(this.model.isPartOfAGroup(7));
 
-        assertEquals(2, group.getMembers().size());
-        assertTrue(group.getMembers().contains(6));
-        assertTrue(group.getMembers().contains(7));
+        assertEquals(2, group.getMembers().length);
+        assertTrue(group.hasMember(6));
+        assertTrue(group.hasMember(7));
     }
 
     @Test
@@ -285,10 +285,10 @@
         assertEquals(3, group.getVisibleSpan());
         assertTrue(this.model.isPartOfAGroup(7));
 
-        assertEquals(3, group.getMembers().size());
-        assertTrue(group.getMembers().contains(5));
-        assertTrue(group.getMembers().contains(6));
-        assertTrue(group.getMembers().contains(7));
+        assertEquals(3, group.getMembers().length);
+        assertTrue(group.hasMember(5));
+        assertTrue(group.hasMember(6));
+        assertTrue(group.hasMember(7));
 
         this.model.removePositionsFromGroup(7);
 
@@ -299,10 +299,10 @@
         assertEquals(3, group.getVisibleSpan());
         assertTrue(this.model.isPartOfAGroup(7));
 
-        assertEquals(3, group.getMembers().size());
-        assertTrue(group.getMembers().contains(5));
-        assertTrue(group.getMembers().contains(6));
-        assertTrue(group.getMembers().contains(7));
+        assertEquals(3, group.getMembers().length);
+        assertTrue(group.hasMember(5));
+        assertTrue(group.hasMember(6));
+        assertTrue(group.hasMember(7));
     }
 
     @Test
@@ -406,21 +406,21 @@
         assertEquals(0, group.getStartIndex());
         assertEquals(4, group.getOriginalSpan());
 
-        assertEquals(4, group.getMembers().size());
-        assertTrue(group.getMembers().contains(0));
-        assertTrue(group.getMembers().contains(1));
-        assertTrue(group.getMembers().contains(2));
-        assertTrue(group.getMembers().contains(3));
+        assertEquals(4, group.getMembers().length);
+        assertTrue(group.hasMember(0));
+        assertTrue(group.hasMember(1));
+        assertTrue(group.hasMember(2));
+        assertTrue(group.hasMember(3));
 
         this.model.removePositionsFromGroup(group, 2);
 
         assertEquals(0, group.getStartIndex());
         assertEquals(3, group.getOriginalSpan());
 
-        assertEquals(3, group.getMembers().size());
-        assertTrue(group.getMembers().contains(0));
-        assertTrue(group.getMembers().contains(1));
-        assertTrue(group.getMembers().contains(2));
+        assertEquals(3, group.getMembers().length);
+        assertTrue(group.hasMember(0));
+        assertTrue(group.hasMember(1));
+        assertTrue(group.hasMember(2));
     }
 
     @Test
@@ -430,20 +430,20 @@
         assertEquals(0, group.getStartIndex());
         assertEquals(4, group.getOriginalSpan());
 
-        assertEquals(4, group.getMembers().size());
-        assertTrue(group.getMembers().contains(0));
-        assertTrue(group.getMembers().contains(1));
-        assertTrue(group.getMembers().contains(2));
-        assertTrue(group.getMembers().contains(3));
+        assertEquals(4, group.getMembers().length);
+        assertTrue(group.hasMember(0));
+        assertTrue(group.hasMember(1));
+        assertTrue(group.hasMember(2));
+        assertTrue(group.hasMember(3));
 
         this.model.removePositionsFromGroup(group, 0, 3);
 
         assertEquals(1, group.getStartIndex());
         assertEquals(2, group.getOriginalSpan());
 
-        assertEquals(2, group.getMembers().size());
-        assertTrue(group.getMembers().contains(1));
-        assertTrue(group.getMembers().contains(2));
+        assertEquals(2, group.getMembers().length);
+        assertTrue(group.hasMember(1));
+        assertTrue(group.hasMember(2));
     }
 
     @Test
@@ -453,20 +453,20 @@
         assertEquals(0, group.getStartIndex());
         assertEquals(4, group.getOriginalSpan());
 
-        assertEquals(4, group.getMembers().size());
-        assertTrue(group.getMembers().contains(0));
-        assertTrue(group.getMembers().contains(1));
-        assertTrue(group.getMembers().contains(2));
-        assertTrue(group.getMembers().contains(3));
+        assertEquals(4, group.getMembers().length);
+        assertTrue(group.hasMember(0));
+        assertTrue(group.hasMember(1));
+        assertTrue(group.hasMember(2));
+        assertTrue(group.hasMember(3));
 
         this.model.removePositionsFromGroup(0, 3);
 
         assertEquals(1, group.getStartIndex());
         assertEquals(2, group.getOriginalSpan());
 
-        assertEquals(2, group.getMembers().size());
-        assertTrue(group.getMembers().contains(1));
-        assertTrue(group.getMembers().contains(2));
+        assertEquals(2, group.getMembers().length);
+        assertTrue(group.hasMember(1));
+        assertTrue(group.hasMember(2));
     }
 
     @Test
@@ -476,11 +476,11 @@
         assertEquals(0, group.getStartIndex());
         assertEquals(4, group.getOriginalSpan());
 
-        assertEquals(4, group.getMembers().size());
-        assertTrue(group.getMembers().contains(0));
-        assertTrue(group.getMembers().contains(1));
-        assertTrue(group.getMembers().contains(2));
-        assertTrue(group.getMembers().contains(3));
+        assertEquals(4, group.getMembers().length);
+        assertTrue(group.hasMember(0));
+        assertTrue(group.hasMember(1));
+        assertTrue(group.hasMember(2));
+        assertTrue(group.hasMember(3));
 
         this.model.removePositionsFromGroup(0, 1, 2, 3);
 
@@ -490,7 +490,7 @@
         assertEquals(0, group.getOriginalSpan());
         assertEquals(0, group.getVisibleSpan());
 
-        assertEquals(0, group.getMembers().size());
+        assertEquals(0, group.getMembers().length);
 
         assertNull(this.model.getGroupByPosition(0));
         assertNull(this.model.getGroupByPosition(1));
@@ -508,11 +508,11 @@
         assertEquals(0, group.getStartIndex());
         assertEquals(4, group.getOriginalSpan());
 
-        assertEquals(4, group.getMembers().size());
-        assertTrue(group.getMembers().contains(0));
-        assertTrue(group.getMembers().contains(1));
-        assertTrue(group.getMembers().contains(2));
-        assertTrue(group.getMembers().contains(3));
+        assertEquals(4, group.getMembers().length);
+        assertTrue(group.hasMember(0));
+        assertTrue(group.hasMember(1));
+        assertTrue(group.hasMember(2));
+        assertTrue(group.hasMember(3));
 
         this.model.removePositionsFromGroup(group, 0, 1, 2, 3);
 
@@ -522,7 +522,7 @@
         assertEquals(0, group.getOriginalSpan());
         assertEquals(0, group.getVisibleSpan());
 
-        assertEquals(0, group.getMembers().size());
+        assertEquals(0, group.getMembers().length);
 
         assertNull(this.model.getGroupByPosition(0));
         assertNull(this.model.getGroupByPosition(1));
@@ -571,10 +571,10 @@
         assertEquals(3, group.getOriginalSpan());
         assertEquals(3, group.getVisibleSpan());
 
-        assertEquals(3, group.getMembers().size());
-        assertTrue(group.getMembers().contains(5));
-        assertTrue(group.getMembers().contains(6));
-        assertTrue(group.getMembers().contains(7));
+        assertEquals(3, group.getMembers().length);
+        assertTrue(group.hasMember(5));
+        assertTrue(group.hasMember(6));
+        assertTrue(group.hasMember(7));
 
         this.model.addPositionsToGroup(group, 8);
 
@@ -584,11 +584,11 @@
         assertEquals(4, group.getOriginalSpan());
         assertEquals(4, group.getVisibleSpan());
 
-        assertEquals(4, group.getMembers().size());
-        assertTrue(group.getMembers().contains(5));
-        assertTrue(group.getMembers().contains(6));
-        assertTrue(group.getMembers().contains(7));
-        assertTrue(group.getMembers().contains(8));
+        assertEquals(4, group.getMembers().length);
+        assertTrue(group.hasMember(5));
+        assertTrue(group.hasMember(6));
+        assertTrue(group.hasMember(7));
+        assertTrue(group.hasMember(8));
     }
 
     @Test
@@ -601,10 +601,10 @@
         assertEquals(3, group.getOriginalSpan());
         assertEquals(3, group.getVisibleSpan());
 
-        assertEquals(3, group.getMembers().size());
-        assertTrue(group.getMembers().contains(5));
-        assertTrue(group.getMembers().contains(6));
-        assertTrue(group.getMembers().contains(7));
+        assertEquals(3, group.getMembers().length);
+        assertTrue(group.hasMember(5));
+        assertTrue(group.hasMember(6));
+        assertTrue(group.hasMember(7));
 
         this.model.addPositionsToGroup(group, 8, 9, 10);
 
@@ -614,13 +614,13 @@
         assertEquals(6, group.getOriginalSpan());
         assertEquals(6, group.getVisibleSpan());
 
-        assertEquals(6, group.getMembers().size());
-        assertTrue(group.getMembers().contains(5));
-        assertTrue(group.getMembers().contains(6));
-        assertTrue(group.getMembers().contains(7));
-        assertTrue(group.getMembers().contains(8));
-        assertTrue(group.getMembers().contains(9));
-        assertTrue(group.getMembers().contains(10));
+        assertEquals(6, group.getMembers().length);
+        assertTrue(group.hasMember(5));
+        assertTrue(group.hasMember(6));
+        assertTrue(group.hasMember(7));
+        assertTrue(group.hasMember(8));
+        assertTrue(group.hasMember(9));
+        assertTrue(group.hasMember(10));
     }
 
     @Test
@@ -671,9 +671,9 @@
         assertEquals(2, group.getOriginalSpan());
         assertEquals(2, group.getVisibleSpan());
 
-        assertEquals(2, group.getMembers().size());
-        assertTrue(group.getMembers().contains(12));
-        assertTrue(group.getMembers().contains(13));
+        assertEquals(2, group.getMembers().length);
+        assertTrue(group.hasMember(12));
+        assertTrue(group.hasMember(13));
 
         this.model.addPositionsToGroup(group, 11);
 
@@ -683,10 +683,10 @@
         assertEquals(3, group.getOriginalSpan());
         assertEquals(3, group.getVisibleSpan());
 
-        assertEquals(3, group.getMembers().size());
-        assertTrue(group.getMembers().contains(11));
-        assertTrue(group.getMembers().contains(12));
-        assertTrue(group.getMembers().contains(13));
+        assertEquals(3, group.getMembers().length);
+        assertTrue(group.hasMember(11));
+        assertTrue(group.hasMember(12));
+        assertTrue(group.hasMember(13));
     }
 
     @Test
@@ -699,9 +699,9 @@
         assertEquals(2, group.getOriginalSpan());
         assertEquals(2, group.getVisibleSpan());
 
-        assertEquals(2, group.getMembers().size());
-        assertTrue(group.getMembers().contains(12));
-        assertTrue(group.getMembers().contains(13));
+        assertEquals(2, group.getMembers().length);
+        assertTrue(group.hasMember(12));
+        assertTrue(group.hasMember(13));
 
         this.model.addPositionsToGroup(group, 11, 10);
 
@@ -711,11 +711,11 @@
         assertEquals(4, group.getOriginalSpan());
         assertEquals(4, group.getVisibleSpan());
 
-        assertEquals(4, group.getMembers().size());
-        assertTrue(group.getMembers().contains(10));
-        assertTrue(group.getMembers().contains(11));
-        assertTrue(group.getMembers().contains(12));
-        assertTrue(group.getMembers().contains(13));
+        assertEquals(4, group.getMembers().length);
+        assertTrue(group.hasMember(10));
+        assertTrue(group.hasMember(11));
+        assertTrue(group.hasMember(12));
+        assertTrue(group.hasMember(13));
     }
 
     @Test
@@ -812,8 +812,8 @@
         Group group2 = this.model.getGroupByPosition(5);
         this.model.addStaticIndexesToGroup(group2, 5, 6);
 
-        assertEquals(2, group1.getStaticIndexes().size());
-        assertEquals(2, group2.getStaticIndexes().size());
+        assertEquals(2, group1.getStaticIndexes().length);
+        assertEquals(2, group2.getStaticIndexes().length);
 
         assertTrue(this.model.isStatic(1));
         assertTrue(this.model.isStatic(2));
@@ -827,7 +827,7 @@
         Group group = this.model.getGroupByPosition(0);
         this.model.addStaticIndexesToGroup(group, 4);
 
-        assertEquals(0, group.getStaticIndexes().size());
+        assertEquals(0, group.getStaticIndexes().length);
 
         assertFalse(this.model.isStatic(4));
     }
@@ -837,7 +837,7 @@
         Group group = this.model.getGroupByPosition(0);
         this.model.addStaticIndexesToGroup(group, 3, 4);
 
-        assertEquals(1, group.getStaticIndexes().size());
+        assertEquals(1, group.getStaticIndexes().length);
 
         assertTrue(this.model.isStatic(3));
         assertFalse(this.model.isStatic(4));
@@ -849,7 +849,7 @@
         // set last two columns as static
         this.model.addStaticIndexesToGroup(group, 2, 3);
 
-        assertEquals(2, group.getStaticIndexes().size());
+        assertEquals(2, group.getStaticIndexes().length);
 
         assertTrue(this.model.isStatic(2));
         assertTrue(this.model.isStatic(3));
@@ -863,13 +863,13 @@
         assertEquals(3, group.getOriginalSpan());
         assertEquals(3, group.getVisibleSpan());
 
-        Collection<Integer> members = group.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(Integer.valueOf(0)));
-        assertTrue(members.contains(Integer.valueOf(1)));
-        assertTrue(members.contains(Integer.valueOf(2)));
+        int[] members = group.getMembers();
+        assertEquals(3, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(1, members[1]);
+        assertEquals(2, members[2]);
 
-        assertEquals(1, group.getStaticIndexes().size());
+        assertEquals(1, group.getStaticIndexes().length);
 
         assertTrue(this.model.isStatic(2));
         assertFalse(this.model.isStatic(3));
@@ -881,7 +881,7 @@
         // set first and last column as static
         this.model.addStaticIndexesToGroup(group, 0, 3);
 
-        assertEquals(2, group.getStaticIndexes().size());
+        assertEquals(2, group.getStaticIndexes().length);
 
         assertTrue(this.model.isStatic(0));
         assertFalse(this.model.isStatic(1));
@@ -897,13 +897,13 @@
         assertEquals(3, group.getOriginalSpan());
         assertEquals(3, group.getVisibleSpan());
 
-        Collection<Integer> members = group.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(Integer.valueOf(1)));
-        assertTrue(members.contains(Integer.valueOf(2)));
-        assertTrue(members.contains(Integer.valueOf(3)));
+        int[] members = group.getMembers();
+        assertEquals(3, members.length);
+        assertEquals(1, members[0]);
+        assertEquals(2, members[1]);
+        assertEquals(3, members[2]);
 
-        assertEquals(1, group.getStaticIndexes().size());
+        assertEquals(1, group.getStaticIndexes().length);
 
         assertFalse(this.model.isStatic(0));
         assertFalse(this.model.isStatic(1));
@@ -917,7 +917,7 @@
         // set last two columns as static
         this.model.addStaticIndexesToGroup(group, 2, 3);
 
-        assertEquals(2, group.getStaticIndexes().size());
+        assertEquals(2, group.getStaticIndexes().length);
 
         assertTrue(this.model.isStatic(2));
         assertTrue(this.model.isStatic(3));
@@ -931,13 +931,13 @@
         assertEquals(3, group.getOriginalSpan());
         assertEquals(3, group.getVisibleSpan());
 
-        Collection<Integer> members = group.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(Integer.valueOf(0)));
-        assertTrue(members.contains(Integer.valueOf(1)));
-        assertTrue(members.contains(Integer.valueOf(2)));
+        int[] members = group.getMembers();
+        assertEquals(3, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(1, members[1]);
+        assertEquals(2, members[2]);
 
-        assertEquals(1, group.getStaticIndexes().size());
+        assertEquals(1, group.getStaticIndexes().length);
 
         assertTrue(this.model.isStatic(2));
         assertFalse(this.model.isStatic(3));
@@ -949,7 +949,7 @@
         // set first and last column as static
         this.model.addStaticIndexesToGroup(group, 0, 3);
 
-        assertEquals(2, group.getStaticIndexes().size());
+        assertEquals(2, group.getStaticIndexes().length);
 
         assertTrue(this.model.isStatic(0));
         assertFalse(this.model.isStatic(1));
@@ -965,13 +965,13 @@
         assertEquals(3, group.getOriginalSpan());
         assertEquals(3, group.getVisibleSpan());
 
-        Collection<Integer> members = group.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(Integer.valueOf(1)));
-        assertTrue(members.contains(Integer.valueOf(2)));
-        assertTrue(members.contains(Integer.valueOf(3)));
+        int[] members = group.getMembers();
+        assertEquals(3, members.length);
+        assertEquals(1, members[0]);
+        assertEquals(2, members[1]);
+        assertEquals(3, members[2]);
 
-        assertEquals(1, group.getStaticIndexes().size());
+        assertEquals(1, group.getStaticIndexes().length);
 
         assertFalse(this.model.isStatic(0));
         assertFalse(this.model.isStatic(1));
@@ -981,40 +981,40 @@
 
     @Test
     public void shouldReturnVisiblePositionCollection() {
-        Collection<Integer> positions1 = this.model.getGroupByPosition(0).getVisiblePositions();
-        Collection<Integer> positions2 = this.model.getGroupByPosition(5).getVisiblePositions();
-        Collection<Integer> positions3 = this.model.getGroupByPosition(12).getVisiblePositions();
+        int[] positions1 = this.model.getGroupByPosition(0).getVisiblePositions();
+        int[] positions2 = this.model.getGroupByPosition(5).getVisiblePositions();
+        int[] positions3 = this.model.getGroupByPosition(12).getVisiblePositions();
 
-        assertTrue(positions1.contains(Integer.valueOf(0)));
-        assertTrue(positions1.contains(Integer.valueOf(1)));
-        assertTrue(positions1.contains(Integer.valueOf(2)));
-        assertTrue(positions1.contains(Integer.valueOf(3)));
+        assertTrue(Arrays.stream(positions1).anyMatch(x -> x == 0));
+        assertTrue(Arrays.stream(positions1).anyMatch(x -> x == 1));
+        assertTrue(Arrays.stream(positions1).anyMatch(x -> x == 2));
+        assertTrue(Arrays.stream(positions1).anyMatch(x -> x == 3));
 
-        assertTrue(positions2.contains(Integer.valueOf(5)));
-        assertTrue(positions2.contains(Integer.valueOf(6)));
-        assertTrue(positions2.contains(Integer.valueOf(7)));
+        assertTrue(Arrays.stream(positions2).anyMatch(x -> x == 5));
+        assertTrue(Arrays.stream(positions2).anyMatch(x -> x == 6));
+        assertTrue(Arrays.stream(positions2).anyMatch(x -> x == 7));
 
-        assertTrue(positions3.contains(Integer.valueOf(12)));
-        assertTrue(positions3.contains(Integer.valueOf(13)));
+        assertTrue(Arrays.stream(positions3).anyMatch(x -> x == 12));
+        assertTrue(Arrays.stream(positions3).anyMatch(x -> x == 13));
     }
 
     @Test
     public void shouldReturnVisibleIndexCollection() {
-        Collection<Integer> indexes1 = this.model.getGroupByPosition(0).getVisibleIndexes();
-        Collection<Integer> indexes2 = this.model.getGroupByPosition(5).getVisibleIndexes();
-        Collection<Integer> indexes3 = this.model.getGroupByPosition(12).getVisibleIndexes();
+        int[] indexes1 = this.model.getGroupByPosition(0).getVisibleIndexes();
+        int[] indexes2 = this.model.getGroupByPosition(5).getVisibleIndexes();
+        int[] indexes3 = this.model.getGroupByPosition(12).getVisibleIndexes();
 
-        assertTrue(indexes1.contains(Integer.valueOf(0)));
-        assertTrue(indexes1.contains(Integer.valueOf(1)));
-        assertTrue(indexes1.contains(Integer.valueOf(2)));
-        assertTrue(indexes1.contains(Integer.valueOf(3)));
+        assertTrue(Arrays.stream(indexes1).anyMatch(x -> x == 0));
+        assertTrue(Arrays.stream(indexes1).anyMatch(x -> x == 1));
+        assertTrue(Arrays.stream(indexes1).anyMatch(x -> x == 2));
+        assertTrue(Arrays.stream(indexes1).anyMatch(x -> x == 3));
 
-        assertTrue(indexes2.contains(Integer.valueOf(5)));
-        assertTrue(indexes2.contains(Integer.valueOf(6)));
-        assertTrue(indexes2.contains(Integer.valueOf(7)));
+        assertTrue(Arrays.stream(indexes2).anyMatch(x -> x == 5));
+        assertTrue(Arrays.stream(indexes2).anyMatch(x -> x == 6));
+        assertTrue(Arrays.stream(indexes2).anyMatch(x -> x == 7));
 
-        assertTrue(indexes3.contains(Integer.valueOf(12)));
-        assertTrue(indexes3.contains(Integer.valueOf(13)));
+        assertTrue(Arrays.stream(indexes3).anyMatch(x -> x == 12));
+        assertTrue(Arrays.stream(indexes3).anyMatch(x -> x == 13));
     }
 
     @Test
@@ -1137,7 +1137,7 @@
 
         assertEquals(1, properties.size());
         assertEquals(
-                "testGroupName=0:0:0:4:4:expanded:uncollapseable:breakable:1,2,|"
+                "testGroupName=0:0:0:4:4:expanded:uncollapseable:breakable:1,2|"
                         + "testGroupName2=5:6:5:3:2:expanded:collapseable:unbreakable|"
                         + "testGroupName3=12:12:12:2:2:collapsed:collapseable:breakable|",
                 properties.getProperty("prefix.groupModel"));
@@ -1172,9 +1172,9 @@
         assertFalse(group1.isCollapsed());
         assertFalse(group1.isCollapseable());
         assertFalse(group1.isUnbreakable());
-        assertEquals(2, group1.getStaticIndexes().size());
-        assertTrue(group1.getStaticIndexes().contains(1));
-        assertTrue(group1.getStaticIndexes().contains(2));
+        assertEquals(2, group1.getStaticIndexes().length);
+        assertTrue(group1.containsStaticIndex(1));
+        assertTrue(group1.containsStaticIndex(2));
 
         assertEquals("testGroupName2", group2.getName());
         assertEquals(5, group2.getStartIndex());
@@ -1185,7 +1185,7 @@
         assertFalse(group2.isCollapsed());
         assertTrue(group2.isCollapseable());
         assertTrue(group2.isUnbreakable());
-        assertEquals(0, group2.getStaticIndexes().size());
+        assertEquals(0, group2.getStaticIndexes().length);
 
         assertEquals("testGroupName3", group3.getName());
         assertEquals(12, group3.getStartIndex());
@@ -1196,6 +1196,6 @@
         assertTrue(group3.isCollapsed());
         assertTrue(group3.isCollapseable());
         assertFalse(group3.isUnbreakable());
-        assertEquals(0, group3.getStaticIndexes().size());
+        assertEquals(0, group3.getStaticIndexes().length);
     }
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/RowGroupHeaderLayerTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/RowGroupHeaderLayerTest.java
index aaf96a3..6629e7d 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/RowGroupHeaderLayerTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/RowGroupHeaderLayerTest.java
@@ -1406,6 +1406,17 @@
         // collapse group with no static indexes
         this.rowGroupHeaderLayer.collapseGroup(0);
 
+        int[] hiddenRowIndexes = this.rowGroupExpandCollapseLayer.getHiddenRowIndexesArray();
+        assertEquals(3, hiddenRowIndexes.length);
+        assertEquals(1, hiddenRowIndexes[0]);
+        assertEquals(2, hiddenRowIndexes[1]);
+        assertEquals(3, hiddenRowIndexes[2]);
+
+        assertTrue(this.rowGroupExpandCollapseLayer.isRowIndexHidden(1));
+        assertTrue(this.rowGroupExpandCollapseLayer.isRowIndexHidden(2));
+        assertTrue(this.rowGroupExpandCollapseLayer.isRowIndexHidden(3));
+        assertTrue(this.rowGroupExpandCollapseLayer.hasHiddenRows());
+
         assertEquals(11, this.rowGroupExpandCollapseLayer.getRowCount());
 
         Group group = this.rowGroupHeaderLayer.getGroupByPosition(0);
@@ -1425,6 +1436,14 @@
         // expand group with no static indexes
         this.rowGroupHeaderLayer.expandGroup(0);
 
+        hiddenRowIndexes = this.rowGroupExpandCollapseLayer.getHiddenRowIndexesArray();
+        assertEquals(0, hiddenRowIndexes.length);
+
+        assertFalse(this.rowGroupExpandCollapseLayer.isRowIndexHidden(1));
+        assertFalse(this.rowGroupExpandCollapseLayer.isRowIndexHidden(2));
+        assertFalse(this.rowGroupExpandCollapseLayer.isRowIndexHidden(3));
+        assertFalse(this.rowGroupExpandCollapseLayer.hasHiddenRows());
+
         assertEquals(14, this.rowGroupExpandCollapseLayer.getRowCount());
 
         group = this.rowGroupHeaderLayer.getGroupByPosition(0);
@@ -8402,10 +8421,10 @@
         assertEquals(3, group.getVisibleSpan());
         assertFalse(group.isCollapsed());
 
-        assertEquals(3, group.getMembers().size());
-        assertTrue(group.getMembers().contains(5));
-        assertTrue(group.getMembers().contains(6));
-        assertTrue(group.getMembers().contains(7));
+        assertEquals(3, group.getMembers().length);
+        assertTrue(group.hasMember(5));
+        assertTrue(group.hasMember(6));
+        assertTrue(group.hasMember(7));
     }
 
     @Test
@@ -8485,11 +8504,11 @@
         // expand again
         this.rowGroupHeaderLayer.expandGroup(4);
 
-        assertEquals(4, group.getMembers().size());
-        assertTrue(group.getMembers().contains(4));
-        assertTrue(group.getMembers().contains(5));
-        assertTrue(group.getMembers().contains(6));
-        assertTrue(group.getMembers().contains(7));
+        assertEquals(4, group.getMembers().length);
+        assertTrue(group.hasMember(4));
+        assertTrue(group.hasMember(5));
+        assertTrue(group.hasMember(6));
+        assertTrue(group.hasMember(7));
 
         verifyCleanState();
     }
@@ -8573,11 +8592,11 @@
         // expand again
         this.rowGroupHeaderLayer.expandGroup(4);
 
-        assertEquals(4, group.getMembers().size());
-        assertTrue(group.getMembers().contains(4));
-        assertTrue(group.getMembers().contains(5));
-        assertTrue(group.getMembers().contains(6));
-        assertTrue(group.getMembers().contains(7));
+        assertEquals(4, group.getMembers().length);
+        assertTrue(group.hasMember(4));
+        assertTrue(group.hasMember(5));
+        assertTrue(group.hasMember(6));
+        assertTrue(group.hasMember(7));
 
         verifyCleanState();
     }
@@ -8621,11 +8640,11 @@
         assertEquals(4, group.getVisibleSpan());
         assertFalse(group.isCollapsed());
 
-        assertEquals(4, group.getMembers().size());
-        assertTrue(group.getMembers().contains(5));
-        assertTrue(group.getMembers().contains(6));
-        assertTrue(group.getMembers().contains(7));
-        assertTrue(group.getMembers().contains(4));
+        assertEquals(4, group.getMembers().length);
+        assertTrue(group.hasMember(5));
+        assertTrue(group.hasMember(6));
+        assertTrue(group.hasMember(7));
+        assertTrue(group.hasMember(4));
     }
 
     @Test
@@ -8791,11 +8810,11 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
 
-        assertEquals(4, group.getMembers().size());
-        assertTrue(group.getMembers().contains(4));
-        assertTrue(group.getMembers().contains(5));
-        assertTrue(group.getMembers().contains(6));
-        assertTrue(group.getMembers().contains(7));
+        assertEquals(4, group.getMembers().length);
+        assertTrue(group.hasMember(4));
+        assertTrue(group.hasMember(5));
+        assertTrue(group.hasMember(6));
+        assertTrue(group.hasMember(7));
     }
 
     @Test
@@ -8924,17 +8943,17 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
 
-        assertEquals(5, group1.getMembers().size());
-        assertTrue(group1.getMembers().contains(0));
-        assertTrue(group1.getMembers().contains(1));
-        assertTrue(group1.getMembers().contains(2));
-        assertTrue(group1.getMembers().contains(3));
-        assertTrue(group1.getMembers().contains(4));
+        assertEquals(5, group1.getMembers().length);
+        assertTrue(group1.hasMember(0));
+        assertTrue(group1.hasMember(1));
+        assertTrue(group1.hasMember(2));
+        assertTrue(group1.hasMember(3));
+        assertTrue(group1.hasMember(4));
 
-        assertEquals(3, group2.getMembers().size());
-        assertTrue(group2.getMembers().contains(5));
-        assertTrue(group2.getMembers().contains(6));
-        assertTrue(group2.getMembers().contains(7));
+        assertEquals(3, group2.getMembers().length);
+        assertTrue(group2.hasMember(5));
+        assertTrue(group2.hasMember(6));
+        assertTrue(group2.hasMember(7));
     }
 
     @Test
@@ -9064,17 +9083,17 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
 
-        assertEquals(5, group1.getMembers().size());
-        assertTrue(group1.getMembers().contains(0));
-        assertTrue(group1.getMembers().contains(1));
-        assertTrue(group1.getMembers().contains(2));
-        assertTrue(group1.getMembers().contains(3));
-        assertTrue(group1.getMembers().contains(4));
+        assertEquals(5, group1.getMembers().length);
+        assertTrue(group1.hasMember(0));
+        assertTrue(group1.hasMember(1));
+        assertTrue(group1.hasMember(2));
+        assertTrue(group1.hasMember(3));
+        assertTrue(group1.hasMember(4));
 
-        assertEquals(3, group2.getMembers().size());
-        assertTrue(group2.getMembers().contains(5));
-        assertTrue(group2.getMembers().contains(6));
-        assertTrue(group2.getMembers().contains(7));
+        assertEquals(3, group2.getMembers().length);
+        assertTrue(group2.hasMember(5));
+        assertTrue(group2.hasMember(6));
+        assertTrue(group2.hasMember(7));
     }
 
     @Test
@@ -9200,16 +9219,16 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
 
-        assertEquals(4, group3.getMembers().size());
-        assertTrue(group3.getMembers().contains(8));
-        assertTrue(group3.getMembers().contains(9));
-        assertTrue(group3.getMembers().contains(10));
-        assertTrue(group3.getMembers().contains(4));
+        assertEquals(4, group3.getMembers().length);
+        assertTrue(group3.hasMember(8));
+        assertTrue(group3.hasMember(9));
+        assertTrue(group3.hasMember(10));
+        assertTrue(group3.hasMember(4));
 
-        assertEquals(3, group2.getMembers().size());
-        assertTrue(group2.getMembers().contains(5));
-        assertTrue(group2.getMembers().contains(6));
-        assertTrue(group2.getMembers().contains(7));
+        assertEquals(3, group2.getMembers().length);
+        assertTrue(group2.hasMember(5));
+        assertTrue(group2.hasMember(6));
+        assertTrue(group2.hasMember(7));
     }
 
     @Test
@@ -9335,16 +9354,16 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
 
-        assertEquals(4, group3.getMembers().size());
-        assertTrue(group3.getMembers().contains(8));
-        assertTrue(group3.getMembers().contains(9));
-        assertTrue(group3.getMembers().contains(10));
-        assertTrue(group3.getMembers().contains(4));
+        assertEquals(4, group3.getMembers().length);
+        assertTrue(group3.hasMember(8));
+        assertTrue(group3.hasMember(9));
+        assertTrue(group3.hasMember(10));
+        assertTrue(group3.hasMember(4));
 
-        assertEquals(3, group2.getMembers().size());
-        assertTrue(group2.getMembers().contains(5));
-        assertTrue(group2.getMembers().contains(6));
-        assertTrue(group2.getMembers().contains(7));
+        assertEquals(3, group2.getMembers().length);
+        assertTrue(group2.hasMember(5));
+        assertTrue(group2.hasMember(6));
+        assertTrue(group2.hasMember(7));
     }
 
     @Test
@@ -9434,11 +9453,11 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
 
-        assertEquals(4, group2.getMembers().size());
-        assertTrue(group2.getMembers().contains(4));
-        assertTrue(group2.getMembers().contains(5));
-        assertTrue(group2.getMembers().contains(6));
-        assertTrue(group2.getMembers().contains(7));
+        assertEquals(4, group2.getMembers().length);
+        assertTrue(group2.hasMember(4));
+        assertTrue(group2.hasMember(5));
+        assertTrue(group2.hasMember(6));
+        assertTrue(group2.hasMember(7));
     }
 
     @Test
@@ -9529,11 +9548,11 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
 
-        assertEquals(4, group2.getMembers().size());
-        assertTrue(group2.getMembers().contains(4));
-        assertTrue(group2.getMembers().contains(5));
-        assertTrue(group2.getMembers().contains(6));
-        assertTrue(group2.getMembers().contains(7));
+        assertEquals(4, group2.getMembers().length);
+        assertTrue(group2.hasMember(4));
+        assertTrue(group2.hasMember(5));
+        assertTrue(group2.hasMember(6));
+        assertTrue(group2.hasMember(7));
     }
 
     @Test
@@ -9680,13 +9699,13 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
 
-        assertEquals(3, group2.getMembers().size());
-        assertTrue(group2.getMembers().contains(4));
-        assertTrue(group2.getMembers().contains(5));
-        assertTrue(group2.getMembers().contains(7));
+        assertEquals(3, group2.getMembers().length);
+        assertTrue(group2.hasMember(4));
+        assertTrue(group2.hasMember(5));
+        assertTrue(group2.hasMember(7));
 
-        assertEquals(1, group2.getStaticIndexes().size());
-        assertTrue(group2.getStaticIndexes().contains(5));
+        assertEquals(1, group2.getStaticIndexes().length);
+        assertTrue(group2.containsStaticIndex(5));
     }
 
     @Test
@@ -9836,13 +9855,13 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
 
-        assertEquals(3, group2.getMembers().size());
-        assertTrue(group2.getMembers().contains(4));
-        assertTrue(group2.getMembers().contains(5));
-        assertTrue(group2.getMembers().contains(7));
+        assertEquals(3, group2.getMembers().length);
+        assertTrue(group2.hasMember(4));
+        assertTrue(group2.hasMember(5));
+        assertTrue(group2.hasMember(7));
 
-        assertEquals(1, group2.getStaticIndexes().size());
-        assertTrue(group2.getStaticIndexes().contains(5));
+        assertEquals(1, group2.getStaticIndexes().length);
+        assertTrue(group2.containsStaticIndex(5));
     }
 
     @Test
@@ -10473,11 +10492,11 @@
         assertEquals(4, group2.getOriginalSpan());
         assertEquals(4, group2.getVisibleSpan());
 
-        assertEquals(4, group2.getMembers().size());
-        assertTrue(group2.getMembers().contains(4));
-        assertTrue(group2.getMembers().contains(5));
-        assertTrue(group2.getMembers().contains(6));
-        assertTrue(group2.getMembers().contains(7));
+        assertEquals(4, group2.getMembers().length);
+        assertTrue(group2.hasMember(4));
+        assertTrue(group2.hasMember(5));
+        assertTrue(group2.hasMember(6));
+        assertTrue(group2.hasMember(7));
     }
 
     @Test
@@ -10703,12 +10722,12 @@
         assertEquals(4, group.getVisibleSpan());
         assertFalse(group.isCollapsed());
 
-        Collection<Integer> members = group.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(0));
-        assertTrue(members.contains(2));
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(6));
+        int[] members = group.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(2, members[1]);
+        assertEquals(4, members[2]);
+        assertEquals(6, members[3]);
 
         assertEquals(0, this.rowGroupHeaderLayer.getRowIndexByPosition(0));
         assertEquals(2, this.rowGroupHeaderLayer.getRowIndexByPosition(1));
@@ -10754,12 +10773,12 @@
         assertEquals(4, group.getVisibleSpan());
         assertFalse(group.isCollapsed());
 
-        Collection<Integer> members = group.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(0));
-        assertTrue(members.contains(1));
-        assertTrue(members.contains(2));
-        assertTrue(members.contains(3));
+        int[] members = group.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(1, members[1]);
+        assertEquals(2, members[2]);
+        assertEquals(3, members[3]);
 
         ILayerCell cell = this.rowGroupHeaderLayer.getCellByPosition(0, 0);
         assertEquals(0, cell.getOriginRowPosition());
@@ -10818,11 +10837,11 @@
         assertEquals(3, group.getVisibleSpan());
         assertFalse(group.isCollapsed());
 
-        Collection<Integer> members = group.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(0));
-        assertTrue(members.contains(1));
-        assertTrue(members.contains(2));
+        int[] members = group.getMembers();
+        assertEquals(3, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(1, members[1]);
+        assertEquals(2, members[2]);
 
         assertNull(groupModel.getGroupByPosition(3));
 
@@ -10859,12 +10878,12 @@
         assertEquals(4, group1.getVisibleSpan());
         assertFalse(group1.isCollapsed());
 
-        Collection<Integer> members = group1.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(0));
-        assertTrue(members.contains(1));
-        assertTrue(members.contains(2));
-        assertTrue(members.contains(3));
+        int[] members = group1.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(1, members[1]);
+        assertEquals(2, members[2]);
+        assertEquals(3, members[3]);
 
         Group group2 = groupModel.getGroupByPosition(5);
         assertEquals(5, group2.getStartIndex());
@@ -10875,10 +10894,10 @@
         assertFalse(group2.isCollapsed());
 
         members = group2.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        assertEquals(3, members.length);
+        assertEquals(5, members[0]);
+        assertEquals(6, members[1]);
+        assertEquals(7, members[2]);
 
         assertNull(groupModel.getGroupByPosition(4));
 
@@ -10939,11 +10958,11 @@
         assertEquals(3, group.getVisibleSpan());
         assertFalse(group.isCollapsed());
 
-        Collection<Integer> members = group.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(0));
-        assertTrue(members.contains(1));
-        assertTrue(members.contains(3));
+        int[] members = group.getMembers();
+        assertEquals(3, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(1, members[1]);
+        assertEquals(3, members[2]);
 
         assertNull(groupModel.getGroupByPosition(3));
 
@@ -10993,12 +11012,12 @@
         assertEquals(4, group1.getVisibleSpan());
         assertFalse(group1.isCollapsed());
 
-        Collection<Integer> members = group1.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(0));
-        assertTrue(members.contains(1));
-        assertTrue(members.contains(2));
-        assertTrue(members.contains(3));
+        int[] members = group1.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(1, members[1]);
+        assertEquals(2, members[2]);
+        assertEquals(3, members[3]);
 
         assertNull(groupModel.getGroupByPosition(4));
         assertNull(groupModel.getGroupByPosition(5));
@@ -11012,9 +11031,9 @@
         assertFalse(group2.isCollapsed());
 
         members = group2.getMembers();
-        assertEquals(2, members.size());
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        assertEquals(2, members.length);
+        assertEquals(6, members[0]);
+        assertEquals(7, members[1]);
 
         ILayerCell cell = this.rowGroupHeaderLayer.getCellByPosition(0, 0);
         assertEquals(0, cell.getOriginRowPosition());
@@ -11086,10 +11105,10 @@
         assertEquals(2, group1.getVisibleSpan());
         assertFalse(group1.isCollapsed());
 
-        Collection<Integer> members = group1.getMembers();
-        assertEquals(2, members.size());
-        assertTrue(members.contains(0));
-        assertTrue(members.contains(1));
+        int[] members = group1.getMembers();
+        assertEquals(2, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(1, members[1]);
 
         assertNull(groupModel.getGroupByPosition(2));
         assertNull(groupModel.getGroupByPosition(3));
@@ -11103,11 +11122,11 @@
         assertFalse(group2.isCollapsed());
 
         members = group2.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
 
         ILayerCell cell = this.rowGroupHeaderLayer.getCellByPosition(0, 0);
         assertEquals(0, cell.getOriginRowPosition());
@@ -11179,10 +11198,10 @@
         assertEquals(2, group1.getVisibleSpan());
         assertFalse(group1.isCollapsed());
 
-        Collection<Integer> members = group1.getMembers();
-        assertEquals(2, members.size());
-        assertTrue(members.contains(0));
-        assertTrue(members.contains(3));
+        int[] members = group1.getMembers();
+        assertEquals(2, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(3, members[1]);
 
         assertNull(groupModel.getGroupByPosition(2));
         assertNull(groupModel.getGroupByPosition(3));
@@ -11196,11 +11215,11 @@
         assertFalse(group2.isCollapsed());
 
         members = group2.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
 
         ILayerCell cell = this.rowGroupHeaderLayer.getCellByPosition(0, 0);
         assertEquals(0, cell.getOriginRowPosition());
@@ -11274,10 +11293,10 @@
         assertEquals(2, group1.getVisibleSpan());
         assertFalse(group1.isCollapsed());
 
-        Collection<Integer> members = group1.getMembers();
-        assertEquals(2, members.size());
-        assertTrue(members.contains(1));
-        assertTrue(members.contains(2));
+        int[] members = group1.getMembers();
+        assertEquals(2, members.length);
+        assertEquals(1, members[0]);
+        assertEquals(2, members[1]);
 
         assertNull(groupModel.getGroupByPosition(3));
 
@@ -11290,11 +11309,11 @@
         assertFalse(group2.isCollapsed());
 
         members = group2.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
 
         ILayerCell cell = this.rowGroupHeaderLayer.getCellByPosition(0, 0);
         assertEquals(0, cell.getOriginRowPosition());
@@ -12187,7 +12206,7 @@
         this.gridLayer.doCommand(new MultiRowHideCommand(this.gridLayer, 9));
 
         // reorder first and second row to second group end
-        this.gridLayer.doCommand(new MultiRowReorderCommand(this.gridLayer, Arrays.asList(1, 2), 9));
+        this.gridLayer.doCommand(new MultiRowReorderCommand(this.gridLayer, new int[] { 1, 2 }, 9));
 
         ILayerCell cell = this.rowGroupHeaderLayer.getCellByPosition(0, 0);
         assertEquals(0, cell.getOriginRowPosition());
@@ -12393,7 +12412,7 @@
         assertEquals(2, group.getVisibleSpan());
 
         // reorder first position between first and second group
-        this.gridLayer.doCommand(new MultiRowReorderCommand(this.gridLayer, Arrays.asList(1), 4));
+        this.gridLayer.doCommand(new MultiRowReorderCommand(this.gridLayer, new int[] { 1 }, 4));
 
         assertEquals(1, group.getStartIndex());
         assertEquals(1, group.getVisibleStartIndex());
@@ -12601,7 +12620,7 @@
         assertEquals(3, group3.getVisibleSpan());
 
         // reorder first position between second and third group
-        this.gridLayer.doCommand(new MultiRowReorderCommand(this.gridLayer, Arrays.asList(1), 8));
+        this.gridLayer.doCommand(new MultiRowReorderCommand(this.gridLayer, new int[] { 1 }, 8));
 
         assertEquals(1, group1.getStartIndex());
         assertEquals(1, group1.getVisibleStartIndex());
@@ -12752,7 +12771,7 @@
         assertEquals(2, group.getVisibleSpan());
 
         // reorder fourth position to first position
-        this.gridLayer.doCommand(new MultiRowReorderCommand(this.gridLayer, Arrays.asList(3), 1));
+        this.gridLayer.doCommand(new MultiRowReorderCommand(this.gridLayer, new int[] { 3 }, 1));
 
         assertEquals(0, group.getStartIndex());
         assertEquals(1, group.getVisibleStartIndex());
@@ -12914,7 +12933,7 @@
         assertEquals(4, group2.getVisibleSpan());
 
         // reorder first position in second group to first position in table
-        this.gridLayer.doCommand(new MultiRowReorderCommand(this.gridLayer, Arrays.asList(4), 1));
+        this.gridLayer.doCommand(new MultiRowReorderCommand(this.gridLayer, new int[] { 4 }, 1));
 
         assertEquals(0, group1.getStartIndex());
         assertEquals(1, group1.getVisibleStartIndex());
@@ -13580,7 +13599,7 @@
         this.rowGroupHeaderLayer.collapseGroup(11);
 
         // reorder first position to table end
-        this.gridLayer.doCommand(new MultiRowReorderCommand(this.gridLayer, Arrays.asList(1), 13));
+        this.gridLayer.doCommand(new MultiRowReorderCommand(this.gridLayer, new int[] { 1 }, 13));
 
         assertEquals(1, group1.getStartIndex());
         assertEquals(1, group1.getVisibleStartIndex());
@@ -13858,7 +13877,7 @@
         this.rowGroupHeaderLayer.collapseGroup(0);
 
         // reorder first position to table end
-        this.gridLayer.doCommand(new MultiRowReorderCommand(this.gridLayer, Arrays.asList(1), 11));
+        this.gridLayer.doCommand(new MultiRowReorderCommand(this.gridLayer, new int[] { 1 }, 11));
 
         assertEquals(1, group1.getStartIndex());
         assertEquals(1, group1.getVisibleStartIndex());
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/RowGroupUtilsTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/RowGroupUtilsTest.java
index 9d685b3..1fe84f7 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/RowGroupUtilsTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/RowGroupUtilsTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2019 Dirk Fauth.
+ * Copyright (c) 2019, 2020 Dirk Fauth.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -34,6 +34,7 @@
 import org.eclipse.nebula.widgets.nattable.grid.layer.GridLayer;
 import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer;
 import org.eclipse.nebula.widgets.nattable.group.RowGroupUtils;
+import org.eclipse.nebula.widgets.nattable.group.performance.GroupModel.Group;
 import org.eclipse.nebula.widgets.nattable.hideshow.ColumnHideShowLayer;
 import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
 import org.eclipse.nebula.widgets.nattable.layer.ILayer;
@@ -451,4 +452,18 @@
         // assertTrue(RowGroupUtils.isBetweenTwoGroups(this.columnGroupHeaderLayer,
         // 1, 13, true, MoveDirectionEnum.RIGHT));
     }
+
+    @Test
+    public void shouldTestIsGroupReordered() {
+        Group group = this.rowGroupHeaderLayer.getGroupByPosition(0);
+
+        // test all group positions
+        assertTrue(RowGroupUtils.isGroupReordered(group, new int[] { 0, 1, 2, 3 }));
+
+        // test less group positions
+        assertFalse(RowGroupUtils.isGroupReordered(group, new int[] { 1, 2, 3 }));
+
+        // test more group positions
+        assertTrue(RowGroupUtils.isGroupReordered(group, new int[] { 0, 1, 2, 3, 4, 5 }));
+    }
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/TwoLevelColumnGroupHeaderLayerTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/TwoLevelColumnGroupHeaderLayerTest.java
index 9a92480..4398e26 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/TwoLevelColumnGroupHeaderLayerTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/TwoLevelColumnGroupHeaderLayerTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2019 Dirk Fauth.
+ * Copyright (c) 2019, 2020 Dirk Fauth.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -17,7 +17,6 @@
 import static org.junit.Assert.assertTrue;
 
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -306,12 +305,12 @@
         assertEquals(0, group1.getVisibleStartPosition());
         assertEquals(4, group1.getOriginalSpan());
         assertEquals(4, group1.getVisibleSpan());
-        Collection<Integer> members = group1.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(0));
-        assertTrue(members.contains(1));
-        assertTrue(members.contains(2));
-        assertTrue(members.contains(3));
+        int[] members = group1.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(1, members[1]);
+        assertEquals(2, members[2]);
+        assertEquals(3, members[3]);
 
         Group group2 = this.columnGroupHeaderLayer.getGroupModel(0).getGroupByPosition(4);
         assertEquals(4, group2.getStartIndex());
@@ -320,11 +319,11 @@
         assertEquals(4, group2.getOriginalSpan());
         assertEquals(4, group2.getVisibleSpan());
         members = group2.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
 
         Group group3 = this.columnGroupHeaderLayer.getGroupModel(0).getGroupByPosition(8);
         assertEquals(8, group3.getStartIndex());
@@ -333,10 +332,10 @@
         assertEquals(3, group3.getOriginalSpan());
         assertEquals(3, group3.getVisibleSpan());
         members = group3.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(8));
-        assertTrue(members.contains(9));
-        assertTrue(members.contains(10));
+        assertEquals(3, members.length);
+        assertEquals(8, members[0]);
+        assertEquals(9, members[1]);
+        assertEquals(10, members[2]);
 
         Group group4 = this.columnGroupHeaderLayer.getGroupModel(0).getGroupByPosition(11);
         assertEquals(11, group4.getStartIndex());
@@ -345,10 +344,10 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
         members = group4.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(11));
-        assertTrue(members.contains(12));
-        assertTrue(members.contains(13));
+        assertEquals(3, members.length);
+        assertEquals(11, members[0]);
+        assertEquals(12, members[1]);
+        assertEquals(13, members[2]);
 
         Group group11 = this.columnGroupHeaderLayer.getGroupModel(1).getGroupByPosition(4);
         assertEquals(4, group11.getStartIndex());
@@ -357,14 +356,14 @@
         assertEquals(7, group11.getOriginalSpan());
         assertEquals(7, group11.getVisibleSpan());
         members = group11.getMembers();
-        assertEquals(7, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
-        assertTrue(members.contains(8));
-        assertTrue(members.contains(9));
-        assertTrue(members.contains(10));
+        assertEquals(7, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
+        assertEquals(8, members[4]);
+        assertEquals(9, members[5]);
+        assertEquals(10, members[6]);
     }
 
     @Test
@@ -598,12 +597,12 @@
         assertEquals(0, group1.getVisibleStartPosition());
         assertEquals(4, group1.getOriginalSpan());
         assertEquals(4, group1.getVisibleSpan());
-        Collection<Integer> members = group1.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        int[] members = group1.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
 
         Group group2 = this.columnGroupHeaderLayer.getGroupModel(0).getGroupByPosition(4);
         assertEquals(8, group2.getStartIndex());
@@ -612,10 +611,10 @@
         assertEquals(3, group2.getOriginalSpan());
         assertEquals(3, group2.getVisibleSpan());
         members = group2.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(8));
-        assertTrue(members.contains(9));
-        assertTrue(members.contains(10));
+        assertEquals(3, members.length);
+        assertEquals(8, members[0]);
+        assertEquals(9, members[1]);
+        assertEquals(10, members[2]);
 
         Group group3 = this.columnGroupHeaderLayer.getGroupModel(0).getGroupByPosition(7);
         assertEquals(0, group3.getStartIndex());
@@ -624,11 +623,11 @@
         assertEquals(4, group3.getOriginalSpan());
         assertEquals(4, group3.getVisibleSpan());
         members = group3.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(0));
-        assertTrue(members.contains(1));
-        assertTrue(members.contains(2));
-        assertTrue(members.contains(3));
+        assertEquals(4, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(1, members[1]);
+        assertEquals(2, members[2]);
+        assertEquals(3, members[3]);
 
         Group group4 = this.columnGroupHeaderLayer.getGroupModel(0).getGroupByPosition(11);
         assertEquals(11, group4.getStartIndex());
@@ -637,10 +636,10 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
         members = group4.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(11));
-        assertTrue(members.contains(12));
-        assertTrue(members.contains(13));
+        assertEquals(3, members.length);
+        assertEquals(11, members[0]);
+        assertEquals(12, members[1]);
+        assertEquals(13, members[2]);
 
         Group group11 = this.columnGroupHeaderLayer.getGroupModel(1).getGroupByPosition(0);
         assertEquals(4, group11.getStartIndex());
@@ -649,14 +648,14 @@
         assertEquals(7, group11.getOriginalSpan());
         assertEquals(7, group11.getVisibleSpan());
         members = group11.getMembers();
-        assertEquals(7, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
-        assertTrue(members.contains(8));
-        assertTrue(members.contains(9));
-        assertTrue(members.contains(10));
+        assertEquals(7, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
+        assertEquals(8, members[4]);
+        assertEquals(9, members[5]);
+        assertEquals(10, members[6]);
     }
 
     @Test
@@ -747,12 +746,12 @@
         assertEquals(0, group1.getVisibleStartPosition());
         assertEquals(4, group1.getOriginalSpan());
         assertEquals(4, group1.getVisibleSpan());
-        Collection<Integer> members = group1.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        int[] members = group1.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
 
         Group group2 = this.columnGroupHeaderLayer.getGroupModel(0).getGroupByPosition(4);
         assertEquals(8, group2.getStartIndex());
@@ -761,10 +760,10 @@
         assertEquals(3, group2.getOriginalSpan());
         assertEquals(3, group2.getVisibleSpan());
         members = group2.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(8));
-        assertTrue(members.contains(9));
-        assertTrue(members.contains(10));
+        assertEquals(3, members.length);
+        assertEquals(8, members[0]);
+        assertEquals(9, members[1]);
+        assertEquals(10, members[2]);
 
         Group group3 = this.columnGroupHeaderLayer.getGroupModel(0).getGroupByPosition(7);
         assertEquals(0, group3.getStartIndex());
@@ -773,11 +772,11 @@
         assertEquals(4, group3.getOriginalSpan());
         assertEquals(4, group3.getVisibleSpan());
         members = group3.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(0));
-        assertTrue(members.contains(1));
-        assertTrue(members.contains(2));
-        assertTrue(members.contains(3));
+        assertEquals(4, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(1, members[1]);
+        assertEquals(2, members[2]);
+        assertEquals(3, members[3]);
 
         Group group4 = this.columnGroupHeaderLayer.getGroupModel(0).getGroupByPosition(11);
         assertEquals(11, group4.getStartIndex());
@@ -786,10 +785,10 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
         members = group4.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(11));
-        assertTrue(members.contains(12));
-        assertTrue(members.contains(13));
+        assertEquals(3, members.length);
+        assertEquals(11, members[0]);
+        assertEquals(12, members[1]);
+        assertEquals(13, members[2]);
 
         Group group11 = this.columnGroupHeaderLayer.getGroupModel(1).getGroupByPosition(0);
         assertEquals(4, group11.getStartIndex());
@@ -798,14 +797,14 @@
         assertEquals(7, group11.getOriginalSpan());
         assertEquals(7, group11.getVisibleSpan());
         members = group11.getMembers();
-        assertEquals(7, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
-        assertTrue(members.contains(8));
-        assertTrue(members.contains(9));
-        assertTrue(members.contains(10));
+        assertEquals(7, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
+        assertEquals(8, members[4]);
+        assertEquals(9, members[5]);
+        assertEquals(10, members[6]);
     }
 
     @Test
@@ -922,12 +921,12 @@
         assertEquals(0, group1.getVisibleStartPosition());
         assertEquals(4, group1.getOriginalSpan());
         assertEquals(4, group1.getVisibleSpan());
-        Collection<Integer> members = group1.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(0));
-        assertTrue(members.contains(1));
-        assertTrue(members.contains(2));
-        assertTrue(members.contains(3));
+        int[] members = group1.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(1, members[1]);
+        assertEquals(2, members[2]);
+        assertEquals(3, members[3]);
 
         Group group2 = this.columnGroupHeaderLayer.getGroupModel(0).getGroupByPosition(4);
         assertEquals(11, group2.getStartIndex());
@@ -936,10 +935,10 @@
         assertEquals(3, group2.getOriginalSpan());
         assertEquals(3, group2.getVisibleSpan());
         members = group2.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(11));
-        assertTrue(members.contains(12));
-        assertTrue(members.contains(13));
+        assertEquals(3, members.length);
+        assertEquals(11, members[0]);
+        assertEquals(12, members[1]);
+        assertEquals(13, members[2]);
 
         Group group3 = this.columnGroupHeaderLayer.getGroupModel(0).getGroupByPosition(7);
         assertEquals(4, group3.getStartIndex());
@@ -948,11 +947,11 @@
         assertEquals(4, group3.getOriginalSpan());
         assertEquals(4, group3.getVisibleSpan());
         members = group3.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
 
         Group group4 = this.columnGroupHeaderLayer.getGroupModel(0).getGroupByPosition(11);
         assertEquals(8, group4.getStartIndex());
@@ -961,10 +960,10 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
         members = group4.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(8));
-        assertTrue(members.contains(9));
-        assertTrue(members.contains(10));
+        assertEquals(3, members.length);
+        assertEquals(8, members[0]);
+        assertEquals(9, members[1]);
+        assertEquals(10, members[2]);
 
         Group group11 = this.columnGroupHeaderLayer.getGroupModel(1).getGroupByPosition(7);
         assertEquals(4, group11.getStartIndex());
@@ -973,14 +972,14 @@
         assertEquals(7, group11.getOriginalSpan());
         assertEquals(7, group11.getVisibleSpan());
         members = group11.getMembers();
-        assertEquals(7, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
-        assertTrue(members.contains(8));
-        assertTrue(members.contains(9));
-        assertTrue(members.contains(10));
+        assertEquals(7, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
+        assertEquals(8, members[4]);
+        assertEquals(9, members[5]);
+        assertEquals(10, members[6]);
     }
 
     @Test
@@ -1098,12 +1097,12 @@
         assertEquals(0, group1.getVisibleStartPosition());
         assertEquals(4, group1.getOriginalSpan());
         assertEquals(4, group1.getVisibleSpan());
-        Collection<Integer> members = group1.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(0));
-        assertTrue(members.contains(1));
-        assertTrue(members.contains(2));
-        assertTrue(members.contains(3));
+        int[] members = group1.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(1, members[1]);
+        assertEquals(2, members[2]);
+        assertEquals(3, members[3]);
 
         Group group2 = this.columnGroupHeaderLayer.getGroupModel(0).getGroupByPosition(4);
         assertEquals(11, group2.getStartIndex());
@@ -1112,10 +1111,10 @@
         assertEquals(3, group2.getOriginalSpan());
         assertEquals(3, group2.getVisibleSpan());
         members = group2.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(11));
-        assertTrue(members.contains(12));
-        assertTrue(members.contains(13));
+        assertEquals(3, members.length);
+        assertEquals(11, members[0]);
+        assertEquals(12, members[1]);
+        assertEquals(13, members[2]);
 
         Group group3 = this.columnGroupHeaderLayer.getGroupModel(0).getGroupByPosition(7);
         assertEquals(4, group3.getStartIndex());
@@ -1124,11 +1123,11 @@
         assertEquals(4, group3.getOriginalSpan());
         assertEquals(4, group3.getVisibleSpan());
         members = group3.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
 
         Group group4 = this.columnGroupHeaderLayer.getGroupModel(0).getGroupByPosition(11);
         assertEquals(8, group4.getStartIndex());
@@ -1137,10 +1136,10 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
         members = group4.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(8));
-        assertTrue(members.contains(9));
-        assertTrue(members.contains(10));
+        assertEquals(3, members.length);
+        assertEquals(8, members[0]);
+        assertEquals(9, members[1]);
+        assertEquals(10, members[2]);
 
         Group group11 = this.columnGroupHeaderLayer.getGroupModel(1).getGroupByPosition(7);
         assertEquals(4, group11.getStartIndex());
@@ -1149,14 +1148,14 @@
         assertEquals(7, group11.getOriginalSpan());
         assertEquals(7, group11.getVisibleSpan());
         members = group11.getMembers();
-        assertEquals(7, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
-        assertTrue(members.contains(8));
-        assertTrue(members.contains(9));
-        assertTrue(members.contains(10));
+        assertEquals(7, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
+        assertEquals(8, members[4]);
+        assertEquals(9, members[5]);
+        assertEquals(10, members[6]);
     }
 
     @Test
@@ -1192,12 +1191,12 @@
         assertEquals(4, group.getVisibleStartPosition());
         assertEquals(4, group.getOriginalSpan());
         assertEquals(4, group.getVisibleSpan());
-        Collection<Integer> members = group.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        int[] members = group.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
     }
 
     @Test
@@ -1234,12 +1233,12 @@
         assertEquals(4, group.getVisibleStartPosition());
         assertEquals(4, group.getOriginalSpan());
         assertEquals(4, group.getVisibleSpan());
-        Collection<Integer> members = group.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        int[] members = group.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
     }
 
     @Test
@@ -1274,12 +1273,12 @@
         assertEquals(2, group.getVisibleStartPosition());
         assertEquals(4, group.getOriginalSpan());
         assertEquals(4, group.getVisibleSpan());
-        Collection<Integer> members = group.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        int[] members = group.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
 
         assertNull(this.columnGroupHeaderLayer.getGroupByPosition(0, 6));
         assertNull(this.columnGroupHeaderLayer.getGroupByPosition(0, 7));
@@ -1316,12 +1315,12 @@
         assertEquals(2, group.getVisibleStartPosition());
         assertEquals(4, group.getOriginalSpan());
         assertEquals(4, group.getVisibleSpan());
-        Collection<Integer> members = group.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        int[] members = group.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
 
         assertNull(this.columnGroupHeaderLayer.getGroupByPosition(0, 6));
         assertNull(this.columnGroupHeaderLayer.getGroupByPosition(0, 7));
@@ -1360,12 +1359,12 @@
         assertEquals(4, group.getVisibleStartPosition());
         assertEquals(4, group.getOriginalSpan());
         assertEquals(4, group.getVisibleSpan());
-        Collection<Integer> members = group.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        int[] members = group.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
     }
 
     @Test
@@ -1402,12 +1401,12 @@
         assertEquals(4, group.getVisibleStartPosition());
         assertEquals(4, group.getOriginalSpan());
         assertEquals(4, group.getVisibleSpan());
-        Collection<Integer> members = group.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        int[] members = group.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
     }
 
     @Test
@@ -1441,9 +1440,9 @@
         assertEquals(2, group.getVisibleStartPosition());
         assertEquals(1, group.getOriginalSpan());
         assertEquals(1, group.getVisibleSpan());
-        Collection<Integer> members = group.getMembers();
-        assertEquals(1, members.size());
-        assertTrue(members.contains(4));
+        int[] members = group.getMembers();
+        assertEquals(1, members.length);
+        assertEquals(4, members[0]);
 
         assertNull(this.columnGroupHeaderLayer.getGroupByPosition(0, 3));
         assertNull(this.columnGroupHeaderLayer.getGroupByPosition(0, 4));
@@ -2024,6 +2023,22 @@
         // hide first column in third group and last column in second group
         this.gridLayer.doCommand(new MultiColumnHideCommand(this.gridLayer, 8, 9));
 
+        Group group2 = this.columnGroupHeaderLayer.getGroupModel(1).getGroupByPosition(4);
+        assertEquals(4, group2.getStartIndex());
+        assertEquals(4, group2.getVisibleStartIndex());
+        assertEquals(4, group2.getVisibleStartPosition());
+        assertEquals(4, group2.getOriginalSpan());
+        assertEquals(3, group2.getVisibleSpan());
+        assertEquals("Address", group2.getName());
+
+        Group group3 = this.columnGroupHeaderLayer.getGroupModel(1).getGroupByPosition(7);
+        assertEquals(8, group3.getStartIndex());
+        assertEquals(9, group3.getVisibleStartIndex());
+        assertEquals(7, group3.getVisibleStartPosition());
+        assertEquals(3, group3.getOriginalSpan());
+        assertEquals(2, group3.getVisibleSpan());
+        assertEquals("Facts", group3.getName());
+
         // try to reorder group 1 between 2 and 3
         this.gridLayer.doCommand(new ColumnGroupReorderCommand(this.gridLayer, 1, 1, 8));
 
@@ -2035,7 +2050,7 @@
         assertEquals(3, group1.getVisibleSpan());
         assertEquals("Address", group1.getName());
 
-        Group group2 = this.columnGroupHeaderLayer.getGroupModel(1).getGroupByPosition(3);
+        group2 = this.columnGroupHeaderLayer.getGroupModel(1).getGroupByPosition(3);
         assertEquals(0, group2.getStartIndex());
         assertEquals(0, group2.getVisibleStartIndex());
         assertEquals(3, group2.getVisibleStartPosition());
@@ -2043,7 +2058,7 @@
         assertEquals(4, group2.getVisibleSpan());
         assertEquals("Person", group2.getName());
 
-        Group group3 = this.columnGroupHeaderLayer.getGroupModel(1).getGroupByPosition(7);
+        group3 = this.columnGroupHeaderLayer.getGroupModel(1).getGroupByPosition(7);
         assertEquals(8, group3.getStartIndex());
         assertEquals(9, group3.getVisibleStartIndex());
         assertEquals(7, group3.getVisibleStartPosition());
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/TwoLevelRowGroupHeaderLayerTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/TwoLevelRowGroupHeaderLayerTest.java
index ac16907..a4d6eb0 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/TwoLevelRowGroupHeaderLayerTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/group/performance/TwoLevelRowGroupHeaderLayerTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2019 Dirk Fauth.
+ * Copyright (c) 2019, 2020 Dirk Fauth.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -17,7 +17,6 @@
 import static org.junit.Assert.assertTrue;
 
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -308,12 +307,12 @@
         assertEquals(0, group1.getVisibleStartPosition());
         assertEquals(4, group1.getOriginalSpan());
         assertEquals(4, group1.getVisibleSpan());
-        Collection<Integer> members = group1.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(0));
-        assertTrue(members.contains(1));
-        assertTrue(members.contains(2));
-        assertTrue(members.contains(3));
+        int[] members = group1.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(1, members[1]);
+        assertEquals(2, members[2]);
+        assertEquals(3, members[3]);
 
         Group group2 = this.rowGroupHeaderLayer.getGroupModel(0).getGroupByPosition(4);
         assertEquals(4, group2.getStartIndex());
@@ -322,11 +321,11 @@
         assertEquals(4, group2.getOriginalSpan());
         assertEquals(4, group2.getVisibleSpan());
         members = group2.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
 
         Group group3 = this.rowGroupHeaderLayer.getGroupModel(0).getGroupByPosition(8);
         assertEquals(8, group3.getStartIndex());
@@ -335,10 +334,10 @@
         assertEquals(3, group3.getOriginalSpan());
         assertEquals(3, group3.getVisibleSpan());
         members = group3.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(8));
-        assertTrue(members.contains(9));
-        assertTrue(members.contains(10));
+        assertEquals(3, members.length);
+        assertEquals(8, members[0]);
+        assertEquals(9, members[1]);
+        assertEquals(10, members[2]);
 
         Group group4 = this.rowGroupHeaderLayer.getGroupModel(0).getGroupByPosition(11);
         assertEquals(11, group4.getStartIndex());
@@ -347,10 +346,10 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
         members = group4.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(11));
-        assertTrue(members.contains(12));
-        assertTrue(members.contains(13));
+        assertEquals(3, members.length);
+        assertEquals(11, members[0]);
+        assertEquals(12, members[1]);
+        assertEquals(13, members[2]);
 
         Group group11 = this.rowGroupHeaderLayer.getGroupModel(1).getGroupByPosition(4);
         assertEquals(4, group11.getStartIndex());
@@ -359,14 +358,14 @@
         assertEquals(7, group11.getOriginalSpan());
         assertEquals(7, group11.getVisibleSpan());
         members = group11.getMembers();
-        assertEquals(7, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
-        assertTrue(members.contains(8));
-        assertTrue(members.contains(9));
-        assertTrue(members.contains(10));
+        assertEquals(7, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
+        assertEquals(8, members[4]);
+        assertEquals(9, members[5]);
+        assertEquals(10, members[6]);
     }
 
     @Test
@@ -600,12 +599,12 @@
         assertEquals(0, group1.getVisibleStartPosition());
         assertEquals(4, group1.getOriginalSpan());
         assertEquals(4, group1.getVisibleSpan());
-        Collection<Integer> members = group1.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        int[] members = group1.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
 
         Group group2 = this.rowGroupHeaderLayer.getGroupModel(0).getGroupByPosition(4);
         assertEquals(8, group2.getStartIndex());
@@ -614,10 +613,10 @@
         assertEquals(3, group2.getOriginalSpan());
         assertEquals(3, group2.getVisibleSpan());
         members = group2.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(8));
-        assertTrue(members.contains(9));
-        assertTrue(members.contains(10));
+        assertEquals(3, members.length);
+        assertEquals(8, members[0]);
+        assertEquals(9, members[1]);
+        assertEquals(10, members[2]);
 
         Group group3 = this.rowGroupHeaderLayer.getGroupModel(0).getGroupByPosition(7);
         assertEquals(0, group3.getStartIndex());
@@ -626,11 +625,11 @@
         assertEquals(4, group3.getOriginalSpan());
         assertEquals(4, group3.getVisibleSpan());
         members = group3.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(0));
-        assertTrue(members.contains(1));
-        assertTrue(members.contains(2));
-        assertTrue(members.contains(3));
+        assertEquals(4, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(1, members[1]);
+        assertEquals(2, members[2]);
+        assertEquals(3, members[3]);
 
         Group group4 = this.rowGroupHeaderLayer.getGroupModel(0).getGroupByPosition(11);
         assertEquals(11, group4.getStartIndex());
@@ -639,10 +638,10 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
         members = group4.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(11));
-        assertTrue(members.contains(12));
-        assertTrue(members.contains(13));
+        assertEquals(3, members.length);
+        assertEquals(11, members[0]);
+        assertEquals(12, members[1]);
+        assertEquals(13, members[2]);
 
         Group group11 = this.rowGroupHeaderLayer.getGroupModel(1).getGroupByPosition(0);
         assertEquals(4, group11.getStartIndex());
@@ -651,14 +650,14 @@
         assertEquals(7, group11.getOriginalSpan());
         assertEquals(7, group11.getVisibleSpan());
         members = group11.getMembers();
-        assertEquals(7, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
-        assertTrue(members.contains(8));
-        assertTrue(members.contains(9));
-        assertTrue(members.contains(10));
+        assertEquals(7, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
+        assertEquals(8, members[4]);
+        assertEquals(9, members[5]);
+        assertEquals(10, members[6]);
     }
 
     @Test
@@ -749,12 +748,12 @@
         assertEquals(0, group1.getVisibleStartPosition());
         assertEquals(4, group1.getOriginalSpan());
         assertEquals(4, group1.getVisibleSpan());
-        Collection<Integer> members = group1.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        int[] members = group1.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
 
         Group group2 = this.rowGroupHeaderLayer.getGroupModel(0).getGroupByPosition(4);
         assertEquals(8, group2.getStartIndex());
@@ -763,10 +762,10 @@
         assertEquals(3, group2.getOriginalSpan());
         assertEquals(3, group2.getVisibleSpan());
         members = group2.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(8));
-        assertTrue(members.contains(9));
-        assertTrue(members.contains(10));
+        assertEquals(3, members.length);
+        assertEquals(8, members[0]);
+        assertEquals(9, members[1]);
+        assertEquals(10, members[2]);
 
         Group group3 = this.rowGroupHeaderLayer.getGroupModel(0).getGroupByPosition(7);
         assertEquals(0, group3.getStartIndex());
@@ -775,11 +774,11 @@
         assertEquals(4, group3.getOriginalSpan());
         assertEquals(4, group3.getVisibleSpan());
         members = group3.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(0));
-        assertTrue(members.contains(1));
-        assertTrue(members.contains(2));
-        assertTrue(members.contains(3));
+        assertEquals(4, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(1, members[1]);
+        assertEquals(2, members[2]);
+        assertEquals(3, members[3]);
 
         Group group4 = this.rowGroupHeaderLayer.getGroupModel(0).getGroupByPosition(11);
         assertEquals(11, group4.getStartIndex());
@@ -788,10 +787,10 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
         members = group4.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(11));
-        assertTrue(members.contains(12));
-        assertTrue(members.contains(13));
+        assertEquals(3, members.length);
+        assertEquals(11, members[0]);
+        assertEquals(12, members[1]);
+        assertEquals(13, members[2]);
 
         Group group11 = this.rowGroupHeaderLayer.getGroupModel(1).getGroupByPosition(0);
         assertEquals(4, group11.getStartIndex());
@@ -800,14 +799,14 @@
         assertEquals(7, group11.getOriginalSpan());
         assertEquals(7, group11.getVisibleSpan());
         members = group11.getMembers();
-        assertEquals(7, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
-        assertTrue(members.contains(8));
-        assertTrue(members.contains(9));
-        assertTrue(members.contains(10));
+        assertEquals(7, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
+        assertEquals(8, members[4]);
+        assertEquals(9, members[5]);
+        assertEquals(10, members[6]);
     }
 
     @Test
@@ -924,12 +923,12 @@
         assertEquals(0, group1.getVisibleStartPosition());
         assertEquals(4, group1.getOriginalSpan());
         assertEquals(4, group1.getVisibleSpan());
-        Collection<Integer> members = group1.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(0));
-        assertTrue(members.contains(1));
-        assertTrue(members.contains(2));
-        assertTrue(members.contains(3));
+        int[] members = group1.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(1, members[1]);
+        assertEquals(2, members[2]);
+        assertEquals(3, members[3]);
 
         Group group2 = this.rowGroupHeaderLayer.getGroupModel(0).getGroupByPosition(4);
         assertEquals(11, group2.getStartIndex());
@@ -938,10 +937,10 @@
         assertEquals(3, group2.getOriginalSpan());
         assertEquals(3, group2.getVisibleSpan());
         members = group2.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(11));
-        assertTrue(members.contains(12));
-        assertTrue(members.contains(13));
+        assertEquals(3, members.length);
+        assertEquals(11, members[0]);
+        assertEquals(12, members[1]);
+        assertEquals(13, members[2]);
 
         Group group3 = this.rowGroupHeaderLayer.getGroupModel(0).getGroupByPosition(7);
         assertEquals(4, group3.getStartIndex());
@@ -950,11 +949,11 @@
         assertEquals(4, group3.getOriginalSpan());
         assertEquals(4, group3.getVisibleSpan());
         members = group3.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
 
         Group group4 = this.rowGroupHeaderLayer.getGroupModel(0).getGroupByPosition(11);
         assertEquals(8, group4.getStartIndex());
@@ -963,10 +962,10 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
         members = group4.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(8));
-        assertTrue(members.contains(9));
-        assertTrue(members.contains(10));
+        assertEquals(3, members.length);
+        assertEquals(8, members[0]);
+        assertEquals(9, members[1]);
+        assertEquals(10, members[2]);
 
         Group group11 = this.rowGroupHeaderLayer.getGroupModel(1).getGroupByPosition(7);
         assertEquals(4, group11.getStartIndex());
@@ -975,14 +974,14 @@
         assertEquals(7, group11.getOriginalSpan());
         assertEquals(7, group11.getVisibleSpan());
         members = group11.getMembers();
-        assertEquals(7, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
-        assertTrue(members.contains(8));
-        assertTrue(members.contains(9));
-        assertTrue(members.contains(10));
+        assertEquals(7, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
+        assertEquals(8, members[4]);
+        assertEquals(9, members[5]);
+        assertEquals(10, members[6]);
     }
 
     @Test
@@ -1100,12 +1099,12 @@
         assertEquals(0, group1.getVisibleStartPosition());
         assertEquals(4, group1.getOriginalSpan());
         assertEquals(4, group1.getVisibleSpan());
-        Collection<Integer> members = group1.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(0));
-        assertTrue(members.contains(1));
-        assertTrue(members.contains(2));
-        assertTrue(members.contains(3));
+        int[] members = group1.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(0, members[0]);
+        assertEquals(1, members[1]);
+        assertEquals(2, members[2]);
+        assertEquals(3, members[3]);
 
         Group group2 = this.rowGroupHeaderLayer.getGroupModel(0).getGroupByPosition(4);
         assertEquals(11, group2.getStartIndex());
@@ -1114,10 +1113,10 @@
         assertEquals(3, group2.getOriginalSpan());
         assertEquals(3, group2.getVisibleSpan());
         members = group2.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(11));
-        assertTrue(members.contains(12));
-        assertTrue(members.contains(13));
+        assertEquals(3, members.length);
+        assertEquals(11, members[0]);
+        assertEquals(12, members[1]);
+        assertEquals(13, members[2]);
 
         Group group3 = this.rowGroupHeaderLayer.getGroupModel(0).getGroupByPosition(7);
         assertEquals(4, group3.getStartIndex());
@@ -1126,11 +1125,11 @@
         assertEquals(4, group3.getOriginalSpan());
         assertEquals(4, group3.getVisibleSpan());
         members = group3.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
 
         Group group4 = this.rowGroupHeaderLayer.getGroupModel(0).getGroupByPosition(11);
         assertEquals(8, group4.getStartIndex());
@@ -1139,10 +1138,10 @@
         assertEquals(3, group4.getOriginalSpan());
         assertEquals(3, group4.getVisibleSpan());
         members = group4.getMembers();
-        assertEquals(3, members.size());
-        assertTrue(members.contains(8));
-        assertTrue(members.contains(9));
-        assertTrue(members.contains(10));
+        assertEquals(3, members.length);
+        assertEquals(8, members[0]);
+        assertEquals(9, members[1]);
+        assertEquals(10, members[2]);
 
         Group group11 = this.rowGroupHeaderLayer.getGroupModel(1).getGroupByPosition(7);
         assertEquals(4, group11.getStartIndex());
@@ -1151,14 +1150,14 @@
         assertEquals(7, group11.getOriginalSpan());
         assertEquals(7, group11.getVisibleSpan());
         members = group11.getMembers();
-        assertEquals(7, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
-        assertTrue(members.contains(8));
-        assertTrue(members.contains(9));
-        assertTrue(members.contains(10));
+        assertEquals(7, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
+        assertEquals(8, members[4]);
+        assertEquals(9, members[5]);
+        assertEquals(10, members[6]);
     }
 
     @Test
@@ -1194,12 +1193,12 @@
         assertEquals(4, group.getVisibleStartPosition());
         assertEquals(4, group.getOriginalSpan());
         assertEquals(4, group.getVisibleSpan());
-        Collection<Integer> members = group.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        int[] members = group.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
     }
 
     @Test
@@ -1236,12 +1235,12 @@
         assertEquals(4, group.getVisibleStartPosition());
         assertEquals(4, group.getOriginalSpan());
         assertEquals(4, group.getVisibleSpan());
-        Collection<Integer> members = group.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        int[] members = group.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
     }
 
     @Test
@@ -1276,12 +1275,12 @@
         assertEquals(2, group.getVisibleStartPosition());
         assertEquals(4, group.getOriginalSpan());
         assertEquals(4, group.getVisibleSpan());
-        Collection<Integer> members = group.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        int[] members = group.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
 
         assertNull(this.rowGroupHeaderLayer.getGroupByPosition(0, 6));
         assertNull(this.rowGroupHeaderLayer.getGroupByPosition(0, 7));
@@ -1318,12 +1317,12 @@
         assertEquals(2, group.getVisibleStartPosition());
         assertEquals(4, group.getOriginalSpan());
         assertEquals(4, group.getVisibleSpan());
-        Collection<Integer> members = group.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        int[] members = group.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
 
         assertNull(this.rowGroupHeaderLayer.getGroupByPosition(0, 6));
         assertNull(this.rowGroupHeaderLayer.getGroupByPosition(0, 7));
@@ -1362,12 +1361,12 @@
         assertEquals(4, group.getVisibleStartPosition());
         assertEquals(4, group.getOriginalSpan());
         assertEquals(4, group.getVisibleSpan());
-        Collection<Integer> members = group.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        int[] members = group.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
     }
 
     @Test
@@ -1404,12 +1403,12 @@
         assertEquals(4, group.getVisibleStartPosition());
         assertEquals(4, group.getOriginalSpan());
         assertEquals(4, group.getVisibleSpan());
-        Collection<Integer> members = group.getMembers();
-        assertEquals(4, members.size());
-        assertTrue(members.contains(4));
-        assertTrue(members.contains(5));
-        assertTrue(members.contains(6));
-        assertTrue(members.contains(7));
+        int[] members = group.getMembers();
+        assertEquals(4, members.length);
+        assertEquals(4, members[0]);
+        assertEquals(5, members[1]);
+        assertEquals(6, members[2]);
+        assertEquals(7, members[3]);
     }
 
     @Test
@@ -1443,9 +1442,9 @@
         assertEquals(2, group.getVisibleStartPosition());
         assertEquals(1, group.getOriginalSpan());
         assertEquals(1, group.getVisibleSpan());
-        Collection<Integer> members = group.getMembers();
-        assertEquals(1, members.size());
-        assertTrue(members.contains(4));
+        int[] members = group.getMembers();
+        assertEquals(1, members.length);
+        assertEquals(4, members[0]);
 
         assertNull(this.rowGroupHeaderLayer.getGroupByPosition(0, 3));
         assertNull(this.rowGroupHeaderLayer.getGroupByPosition(0, 4));
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/ColumnHideShowLayerPersistenceTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/ColumnHideShowLayerPersistenceTest.java
index bf7fa18..e6f7121 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/ColumnHideShowLayerPersistenceTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/ColumnHideShowLayerPersistenceTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2016 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -50,7 +50,7 @@
         this.layer.saveState("prefix", properties);
 
         assertEquals(1, properties.size());
-        assertEquals("3,5,6,", properties.getProperty("prefix" + ColumnHideShowLayer.PERSISTENCE_KEY_HIDDEN_COLUMN_INDEXES));
+        assertEquals("3,5,6", properties.getProperty("prefix" + ColumnHideShowLayer.PERSISTENCE_KEY_HIDDEN_COLUMN_INDEXES));
     }
 
     @Test
@@ -82,7 +82,7 @@
         this.layer.saveState("prefix", properties);
 
         assertEquals(1, properties.size());
-        assertEquals("1,", properties.getProperty("prefix" + ColumnHideShowLayer.PERSISTENCE_KEY_HIDDEN_COLUMN_INDEXES));
+        assertEquals("1", properties.getProperty("prefix" + ColumnHideShowLayer.PERSISTENCE_KEY_HIDDEN_COLUMN_INDEXES));
 
         this.layer.showColumnIndexes(Arrays.asList(new Integer[] { 1 }));
 
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/ColumnHideShowLayerTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/ColumnHideShowLayerTest.java
index 2bf49be..25b9061 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/ColumnHideShowLayerTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/ColumnHideShowLayerTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2019 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -69,17 +69,16 @@
 
     @Test
     public void hideAllColumns() {
-        this.columnHideShowLayer.hideColumnPositions(Arrays.asList(0, 1, 2));
+        this.columnHideShowLayer.hideColumnPositions(0, 1, 2);
 
         assertEquals(0, this.columnHideShowLayer.getColumnCount());
     }
 
     @Test
     public void hideAllColumns2() {
-        List<Integer> columnPositions = Arrays.asList(0);
-        this.columnHideShowLayer.hideColumnPositions(columnPositions);
-        this.columnHideShowLayer.hideColumnPositions(columnPositions);
-        this.columnHideShowLayer.hideColumnPositions(columnPositions);
+        this.columnHideShowLayer.hideColumnPositions(0);
+        this.columnHideShowLayer.hideColumnPositions(0);
+        this.columnHideShowLayer.hideColumnPositions(0);
         assertEquals(0, this.columnHideShowLayer.getColumnCount());
     }
 
@@ -152,6 +151,25 @@
     }
 
     @Test
+    public void showColumnIndexesPrimitive() {
+        this.columnHideShowLayer = new ColumnHideShowLayerFixture(
+                new DataLayerFixture(10, 10, 100, 20));
+
+        assertEquals(10, this.columnHideShowLayer.getColumnCount());
+
+        this.columnHideShowLayer.hideColumnPositions(3, 4, 5);
+        assertEquals(7, this.columnHideShowLayer.getColumnCount());
+        assertEquals(-1, this.columnHideShowLayer.getColumnPositionByIndex(3));
+        assertEquals(-1, this.columnHideShowLayer.getColumnPositionByIndex(4));
+
+        this.columnHideShowLayer.showColumnIndexes(3, 4);
+        assertEquals(9, this.columnHideShowLayer.getColumnCount());
+        assertEquals(3, this.columnHideShowLayer.getColumnPositionByIndex(3));
+        assertEquals(4, this.columnHideShowLayer.getColumnPositionByIndex(4));
+
+    }
+
+    @Test
     public void showColumnPositions() {
         // Column reorder fixture index positions: 4 1 0 2 3
         // Columns positions hidden: 2 4 (index 0 3)
@@ -364,4 +382,32 @@
         assertEquals(-1, this.columnHideShowLayer.localToUnderlyingColumnPosition(5));
     }
 
+    @Test
+    public void shouldReturnHiddenColumnIndexes() {
+        // test initialization hides column indexes 0 and 3
+        // happens due to reordering and hiding
+
+        Collection<Integer> hiddenColumnIndexes = this.columnHideShowLayer.getHiddenColumnIndexes();
+        assertEquals(2, hiddenColumnIndexes.size());
+        assertTrue(hiddenColumnIndexes.contains(Integer.valueOf(0)));
+        assertTrue(hiddenColumnIndexes.contains(Integer.valueOf(3)));
+
+        int[] hiddenColumnIndexesArray = this.columnHideShowLayer.getHiddenColumnIndexesArray();
+        assertEquals(2, hiddenColumnIndexesArray.length);
+        assertEquals(0, hiddenColumnIndexesArray[0]);
+        assertEquals(3, hiddenColumnIndexesArray[1]);
+
+        assertTrue(this.columnHideShowLayer.hasHiddenColumns());
+
+        this.columnHideShowLayer.showAllColumns();
+
+        hiddenColumnIndexes = this.columnHideShowLayer.getHiddenColumnIndexes();
+        assertEquals(0, hiddenColumnIndexes.size());
+
+        hiddenColumnIndexesArray = this.columnHideShowLayer.getHiddenColumnIndexesArray();
+        assertEquals(0, hiddenColumnIndexesArray.length);
+
+        assertFalse(this.columnHideShowLayer.hasHiddenColumns());
+    }
+
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/RowHideShowLayerPersistenceTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/RowHideShowLayerPersistenceTest.java
index fc58ea7..d30796e 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/RowHideShowLayerPersistenceTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/RowHideShowLayerPersistenceTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2013 Dirk Fauth and others.
+ * Copyright (c) 2013, 2020 Dirk Fauth and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -10,12 +10,13 @@
  *******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.hideshow;
 
+import static org.junit.Assert.assertEquals;
+
 import java.util.Arrays;
 import java.util.Properties;
 
 import org.eclipse.nebula.widgets.nattable.grid.data.DummyBodyDataProvider;
 import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -48,9 +49,9 @@
         Properties properties = new Properties();
         this.layer.saveState("prefix", properties);
 
-        Assert.assertEquals(1, properties.size());
-        Assert.assertEquals(
-                "3,5,6,",
+        assertEquals(1, properties.size());
+        assertEquals(
+                "3,5,6",
                 properties.getProperty("prefix"
                         + RowHideShowLayer.PERSISTENCE_KEY_HIDDEN_ROW_INDEXES));
     }
@@ -65,14 +66,14 @@
 
         this.layer.loadState("prefix", properties);
 
-        Assert.assertEquals(7, this.layer.getRowCount());
+        assertEquals(7, this.layer.getRowCount());
 
-        Assert.assertEquals(0, this.layer.getRowIndexByPosition(0));
-        Assert.assertEquals(2, this.layer.getRowIndexByPosition(1));
-        Assert.assertEquals(4, this.layer.getRowIndexByPosition(2));
-        Assert.assertEquals(6, this.layer.getRowIndexByPosition(3));
-        Assert.assertEquals(7, this.layer.getRowIndexByPosition(4));
-        Assert.assertEquals(8, this.layer.getRowIndexByPosition(5));
-        Assert.assertEquals(9, this.layer.getRowIndexByPosition(6));
+        assertEquals(0, this.layer.getRowIndexByPosition(0));
+        assertEquals(2, this.layer.getRowIndexByPosition(1));
+        assertEquals(4, this.layer.getRowIndexByPosition(2));
+        assertEquals(6, this.layer.getRowIndexByPosition(3));
+        assertEquals(7, this.layer.getRowIndexByPosition(4));
+        assertEquals(8, this.layer.getRowIndexByPosition(5));
+        assertEquals(9, this.layer.getRowIndexByPosition(6));
     }
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/RowHideShowLayerTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/RowHideShowLayerTest.java
index 1c12073..47ad513 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/RowHideShowLayerTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/RowHideShowLayerTest.java
@@ -1,12 +1,12 @@
 /*******************************************************************************
- * Copyright (c) 2013, 2019 Dirk Fauth and others.
+ * Copyright (c) 2013, 2020 Dirk Fauth and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
- *    Dirk Fauth <dirk.fauth@gmail.com> - initial API and implementation
+ *    Dirk Fauth <dirk.fauth@googlemail.com> - initial API and implementation
  *******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.hideshow;
 
@@ -15,6 +15,7 @@
 import static org.junit.Assert.assertTrue;
 
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.List;
 
 import org.eclipse.nebula.widgets.nattable.hideshow.indicator.HideIndicatorConstants;
@@ -69,19 +70,18 @@
 
     @Test
     public void hideAllRows() {
-        this.rowHideShowLayer.hideRowPositions(Arrays.asList(0, 1, 2, 3, 4));
+        this.rowHideShowLayer.hideRowPositions(0, 1, 2, 3, 4);
 
         assertEquals(0, this.rowHideShowLayer.getRowCount());
     }
 
     @Test
     public void hideAllRows2() {
-        List<Integer> rowPositions = Arrays.asList(0);
-        this.rowHideShowLayer.hideRowPositions(rowPositions);
-        this.rowHideShowLayer.hideRowPositions(rowPositions);
-        this.rowHideShowLayer.hideRowPositions(rowPositions);
-        this.rowHideShowLayer.hideRowPositions(rowPositions);
-        this.rowHideShowLayer.hideRowPositions(rowPositions);
+        this.rowHideShowLayer.hideRowPositions(0);
+        this.rowHideShowLayer.hideRowPositions(0);
+        this.rowHideShowLayer.hideRowPositions(0);
+        this.rowHideShowLayer.hideRowPositions(0);
+        this.rowHideShowLayer.hideRowPositions(0);
         assertEquals(0, this.rowHideShowLayer.getRowCount());
     }
 
@@ -148,6 +148,24 @@
         assertEquals(9, this.rowHideShowLayer.getRowCount());
         assertEquals(3, this.rowHideShowLayer.getRowPositionByIndex(3));
         assertEquals(4, this.rowHideShowLayer.getRowPositionByIndex(4));
+    }
+
+    @Test
+    public void showRowIndexesPrimitive() {
+        this.rowHideShowLayer = new RowHideShowLayerFixture(
+                new DataLayerFixture(2, 10, 100, 20));
+
+        assertEquals(10, this.rowHideShowLayer.getRowCount());
+
+        this.rowHideShowLayer.hideRowPositions(3, 4, 5);
+        assertEquals(7, this.rowHideShowLayer.getRowCount());
+        assertEquals(-1, this.rowHideShowLayer.getRowPositionByIndex(3));
+        assertEquals(-1, this.rowHideShowLayer.getRowPositionByIndex(4));
+
+        this.rowHideShowLayer.showRowIndexes(3, 4);
+        assertEquals(9, this.rowHideShowLayer.getRowCount());
+        assertEquals(3, this.rowHideShowLayer.getRowPositionByIndex(3));
+        assertEquals(4, this.rowHideShowLayer.getRowPositionByIndex(4));
 
     }
 
@@ -361,4 +379,32 @@
         assertEquals(6, this.rowHideShowLayer.localToUnderlyingRowPosition(6));
         assertEquals(-1, this.rowHideShowLayer.localToUnderlyingRowPosition(7));
     }
+
+    @Test
+    public void shouldReturnHiddenRowIndexes() {
+        // test initialization hides row indexes 0 and 3
+        // happens due to reordering and hiding
+
+        Collection<Integer> hiddenRowIndexes = this.rowHideShowLayer.getHiddenRowIndexes();
+        assertEquals(2, hiddenRowIndexes.size());
+        assertTrue(hiddenRowIndexes.contains(Integer.valueOf(0)));
+        assertTrue(hiddenRowIndexes.contains(Integer.valueOf(3)));
+
+        int[] hiddenRowIndexesArray = this.rowHideShowLayer.getHiddenRowIndexesArray();
+        assertEquals(2, hiddenRowIndexesArray.length);
+        assertEquals(0, hiddenRowIndexesArray[0]);
+        assertEquals(3, hiddenRowIndexesArray[1]);
+
+        assertTrue(this.rowHideShowLayer.hasHiddenRows());
+
+        this.rowHideShowLayer.showAllRows();
+
+        hiddenRowIndexes = this.rowHideShowLayer.getHiddenRowIndexes();
+        assertEquals(0, hiddenRowIndexes.size());
+
+        hiddenRowIndexesArray = this.rowHideShowLayer.getHiddenRowIndexesArray();
+        assertEquals(0, hiddenRowIndexesArray.length);
+
+        assertFalse(this.rowHideShowLayer.hasHiddenRows());
+    }
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/RowIdHideShowLayerTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/RowIdHideShowLayerTest.java
index 214fbfd..8553b34 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/RowIdHideShowLayerTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/RowIdHideShowLayerTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2018 Dirk Fauth and others.
+ * Copyright (c) 2018, 2020 Dirk Fauth and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -16,6 +16,7 @@
 
 import java.io.Serializable;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
@@ -61,7 +62,7 @@
 
     @Test
     public void getRowIndexHideRow() {
-        this.rowHideShowLayer.hideRowPositions(Arrays.asList(1));
+        this.rowHideShowLayer.hideRowPositions(1);
 
         assertEquals(0, this.rowHideShowLayer.getRowIndexByPosition(0));
         assertEquals(2, this.rowHideShowLayer.getRowIndexByPosition(1));
@@ -72,7 +73,7 @@
 
     @Test
     public void getRowPositionForASingleHiddenRow() {
-        this.rowHideShowLayer.hideRowPositions(Arrays.asList(2, 4));
+        this.rowHideShowLayer.hideRowPositions(2, 4);
 
         assertEquals(0, this.rowHideShowLayer.getRowPositionByIndex(0));
         assertEquals(1, this.rowHideShowLayer.getRowPositionByIndex(1));
@@ -84,19 +85,18 @@
 
     @Test
     public void hideAllRows() {
-        this.rowHideShowLayer.hideRowPositions(Arrays.asList(0, 1, 2, 3, 4));
+        this.rowHideShowLayer.hideRowPositions(0, 1, 2, 3, 4);
 
         assertEquals(0, this.rowHideShowLayer.getRowCount());
     }
 
     @Test
     public void hideAllRows2() {
-        List<Integer> rowPositions = Arrays.asList(0);
-        this.rowHideShowLayer.hideRowPositions(rowPositions);
-        this.rowHideShowLayer.hideRowPositions(rowPositions);
-        this.rowHideShowLayer.hideRowPositions(rowPositions);
-        this.rowHideShowLayer.hideRowPositions(rowPositions);
-        this.rowHideShowLayer.hideRowPositions(rowPositions);
+        this.rowHideShowLayer.hideRowPositions(0);
+        this.rowHideShowLayer.hideRowPositions(0);
+        this.rowHideShowLayer.hideRowPositions(0);
+        this.rowHideShowLayer.hideRowPositions(0);
+        this.rowHideShowLayer.hideRowPositions(0);
         assertEquals(0, this.rowHideShowLayer.getRowCount());
     }
 
@@ -104,10 +104,8 @@
     public void showARowByIndex() {
         assertEquals(5, this.rowHideShowLayer.getRowCount());
 
-        List<Integer> rowPositions = Arrays.asList(2);
-        this.rowHideShowLayer.hideRowPositions(rowPositions); // index = 2
-        rowPositions = Arrays.asList(0);
-        this.rowHideShowLayer.hideRowPositions(rowPositions); // index = 4
+        this.rowHideShowLayer.hideRowPositions(Arrays.asList(2)); // index = 2
+        this.rowHideShowLayer.hideRowPositions(Arrays.asList(0)); // index = 4
         assertEquals(3, this.rowHideShowLayer.getRowCount());
         assertEquals(1, this.rowHideShowLayer.getRowIndexByPosition(0));
         assertEquals(-1, this.rowHideShowLayer.getRowIndexByPosition(3));
@@ -127,6 +125,30 @@
     }
 
     @Test
+    public void showARowByIndexPrimitive() {
+        assertEquals(5, this.rowHideShowLayer.getRowCount());
+
+        this.rowHideShowLayer.hideRowPositions(2); // index = 2
+        this.rowHideShowLayer.hideRowPositions(0); // index = 4
+        assertEquals(3, this.rowHideShowLayer.getRowCount());
+        assertEquals(1, this.rowHideShowLayer.getRowIndexByPosition(0));
+        assertEquals(-1, this.rowHideShowLayer.getRowIndexByPosition(3));
+
+        this.rowHideShowLayer.showRowIndexes(0);
+        assertEquals(4, this.rowHideShowLayer.getRowCount());
+        assertEquals(0, this.rowHideShowLayer.getRowIndexByPosition(0));
+        assertEquals(1, this.rowHideShowLayer.getRowIndexByPosition(1));
+        assertEquals(-1, this.rowHideShowLayer.getRowIndexByPosition(4));
+
+        this.rowHideShowLayer.showRowIndexes(2);
+        assertEquals(5, this.rowHideShowLayer.getRowCount());
+        assertEquals(0, this.rowHideShowLayer.getRowIndexByPosition(0));
+        assertEquals(1, this.rowHideShowLayer.getRowIndexByPosition(1));
+        assertEquals(2, this.rowHideShowLayer.getRowIndexByPosition(2));
+        assertEquals(-1, this.rowHideShowLayer.getRowIndexByPosition(5));
+    }
+
+    @Test
     public void showAllRows() {
         assertEquals(5, this.rowHideShowLayer.getRowCount());
 
@@ -174,6 +196,34 @@
     }
 
     @Test
+    public void showRowIndexesPrimitive() {
+        this.bodyDataProvider =
+                new ListDataProvider<>(
+                        PersonService.getPersons(10),
+                        new ReflectiveColumnPropertyAccessor<Person>(
+                                new String[] { "firstName", "lastName", "gender", "married", "birthday" }));
+        this.bodyDataLayer = new DataLayer(this.bodyDataProvider);
+        this.rowHideShowLayer = new RowIdHideShowLayer<>(this.bodyDataLayer, this.bodyDataProvider, new IRowIdAccessor<Person>() {
+            @Override
+            public Serializable getRowId(Person rowObject) {
+                return rowObject.getId();
+            }
+        });
+
+        assertEquals(10, this.rowHideShowLayer.getRowCount());
+
+        this.rowHideShowLayer.hideRowPositions(3, 4, 5);
+        assertEquals(7, this.rowHideShowLayer.getRowCount());
+        assertEquals(-1, this.rowHideShowLayer.getRowPositionByIndex(3));
+        assertEquals(-1, this.rowHideShowLayer.getRowPositionByIndex(4));
+
+        this.rowHideShowLayer.showRowIndexes(3, 4);
+        assertEquals(9, this.rowHideShowLayer.getRowCount());
+        assertEquals(3, this.rowHideShowLayer.getRowPositionByIndex(3));
+        assertEquals(4, this.rowHideShowLayer.getRowPositionByIndex(4));
+    }
+
+    @Test
     public void showRowPositions() {
         prepareReorderAndHide();
 
@@ -271,7 +321,7 @@
         // Row positions hidden: 2 4 (index 0 3)
 
         // hide additional row
-        this.rowHideShowLayer.hideRowPositions(Arrays.asList(1));
+        this.rowHideShowLayer.hideRowPositions(1);
 
         // Row reorder fixture index positions: 4 2 5 6
 
@@ -291,7 +341,7 @@
         // Row positions hidden: 2 4 (index 0 3)
 
         // hide additional row
-        this.rowHideShowLayer.hideRowPositions(Arrays.asList(2));
+        this.rowHideShowLayer.hideRowPositions(2);
 
         this.rowHideShowLayer.showRowPosition(0, false, false);
 
@@ -305,19 +355,19 @@
     public void shouldContainHideIndicatorLabels() {
         assertEquals(5, this.rowHideShowLayer.getRowCount());
 
-        this.rowHideShowLayer.hideRowPositions(Arrays.asList(0));
+        this.rowHideShowLayer.hideRowPositions(0);
 
         LabelStack configLabels = this.rowHideShowLayer.getConfigLabelsByPosition(0, 0);
         assertTrue(configLabels.hasLabel(HideIndicatorConstants.ROW_TOP_HIDDEN));
         assertFalse(configLabels.hasLabel(HideIndicatorConstants.ROW_BOTTOM_HIDDEN));
 
-        this.rowHideShowLayer.hideRowPositions(Arrays.asList(1));
+        this.rowHideShowLayer.hideRowPositions(1);
 
         configLabels = this.rowHideShowLayer.getConfigLabelsByPosition(0, 0);
         assertTrue(configLabels.hasLabel(HideIndicatorConstants.ROW_TOP_HIDDEN));
         assertTrue(configLabels.hasLabel(HideIndicatorConstants.ROW_BOTTOM_HIDDEN));
 
-        this.rowHideShowLayer.showRowIndexes(Arrays.asList(0));
+        this.rowHideShowLayer.showRowIndexes(0);
 
         configLabels = this.rowHideShowLayer.getConfigLabelsByPosition(0, 1);
         assertFalse(configLabels.hasLabel(HideIndicatorConstants.ROW_TOP_HIDDEN));
@@ -344,7 +394,7 @@
 
         this.bodyDataProvider.setDataValue(1, 4, "Zoolander");
 
-        this.rowHideShowLayer.hideRowPositions(Arrays.asList(4));
+        this.rowHideShowLayer.hideRowPositions(4);
 
         assertEquals(9, this.rowHideShowLayer.getRowCount());
         assertEquals(-1, this.rowHideShowLayer.getRowPositionByIndex(4));
@@ -382,7 +432,7 @@
 
     @Test
     public void shouldSaveState() {
-        this.rowHideShowLayer.hideRowPositions(Arrays.asList(new Integer[] { 2, 4 }));
+        this.rowHideShowLayer.hideRowPositions(2, 4);
 
         Properties properties = new Properties();
         this.rowHideShowLayer.saveState("prefix", properties);
@@ -416,6 +466,33 @@
         assertEquals(-1, this.rowHideShowLayer.getRowPositionByIndex(4));
     }
 
+    @Test
+    public void shouldReturnHiddenRowIndexes() {
+        this.rowHideShowLayer.hideRowPositions(1, 3);
+
+        Collection<Integer> hiddenRowIndexes = this.rowHideShowLayer.getHiddenRowIndexes();
+        assertEquals(2, hiddenRowIndexes.size());
+        assertTrue(hiddenRowIndexes.contains(Integer.valueOf(1)));
+        assertTrue(hiddenRowIndexes.contains(Integer.valueOf(3)));
+
+        int[] hiddenRowIndexesArray = this.rowHideShowLayer.getHiddenRowIndexesArray();
+        assertEquals(2, hiddenRowIndexesArray.length);
+        assertEquals(1, hiddenRowIndexesArray[0]);
+        assertEquals(3, hiddenRowIndexesArray[1]);
+
+        assertTrue(this.rowHideShowLayer.hasHiddenRows());
+
+        this.rowHideShowLayer.showAllRows();
+
+        hiddenRowIndexes = this.rowHideShowLayer.getHiddenRowIndexes();
+        assertEquals(0, hiddenRowIndexes.size());
+
+        hiddenRowIndexesArray = this.rowHideShowLayer.getHiddenRowIndexesArray();
+        assertEquals(0, hiddenRowIndexesArray.length);
+
+        assertFalse(this.rowHideShowLayer.hasHiddenRows());
+    }
+
     private void prepareReorderAndHide() {
         // trigger reordering
         this.reorderLayer.reorderRowPosition(4, 0); // 4 0 1 2 3
@@ -423,7 +500,7 @@
         this.reorderLayer.reorderRowPosition(3, 2); // 4 1 0 2 3
 
         // initial hide
-        this.rowHideShowLayer.hideRowPositions(Arrays.asList(2, 4));
+        this.rowHideShowLayer.hideRowPositions(2, 4);
 
         assertEquals(3, this.rowHideShowLayer.getRowCount());
         assertEquals(4, this.rowHideShowLayer.getRowIndexByPosition(0));
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiColumnShowCommandTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiColumnShowCommandTest.java
index 3e8f411..f9eb450 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiColumnShowCommandTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiColumnShowCommandTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2013 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -10,30 +10,25 @@
  ******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.hideshow.command;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertTrue;
 
-import java.util.ArrayList;
 import java.util.Collection;
 
-import org.eclipse.nebula.widgets.nattable.hideshow.command.MultiColumnShowCommand;
 import org.junit.Test;
 
 public class MultiColumnShowCommandTest {
 
     @Test
     public void testClone() throws Exception {
-        Collection<Integer> columnIndexes = new ArrayList<Integer>();
-        columnIndexes.add(3);
-        columnIndexes.add(6);
-        columnIndexes.add(9);
-        columnIndexes.add(12);
-        MultiColumnShowCommand command = new MultiColumnShowCommand(
-                columnIndexes);
+        MultiColumnShowCommand command = new MultiColumnShowCommand(3, 6, 9, 12);
         MultiColumnShowCommand copiedCommand = command.cloneCommand();
 
         Collection<Integer> commandIndexes = command.getColumnIndexes();
         Collection<Integer> cloneIndexes = copiedCommand.getColumnIndexes();
 
+        int[] commandIndexesArray = command.getColumnIndexesArray();
+        int[] cloneIndexesArray = copiedCommand.getColumnIndexesArray();
+
         assertTrue("The commands reference the same instance",
                 command != copiedCommand);
         assertTrue("The command collections reference the same instance",
@@ -46,5 +41,16 @@
                 cloneIndexes.contains(9));
         assertTrue("The cloned command does not contain index 12",
                 cloneIndexes.contains(12));
+
+        assertTrue("The command arrays reference the same instance",
+                commandIndexesArray != cloneIndexesArray);
+        assertTrue("The cloned command does not contain index 3",
+                cloneIndexesArray[0] == 3);
+        assertTrue("The cloned command does not contain index 6",
+                cloneIndexesArray[1] == 6);
+        assertTrue("The cloned command does not contain index 9",
+                cloneIndexesArray[2] == 9);
+        assertTrue("The cloned command does not contain index 12",
+                cloneIndexesArray[3] == 12);
     }
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiRowShowCommandTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiRowShowCommandTest.java
index 2178e34..0c33b37 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiRowShowCommandTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiRowShowCommandTest.java
@@ -1,18 +1,17 @@
 /*******************************************************************************
- * Copyright (c) 2013 Dirk Fauth and others.
+ * Copyright (c) 2013, 2020 Dirk Fauth and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
- *    Dirk Fauth <dirk.fauth@gmail.com> - initial API and implementation
+ *    Dirk Fauth <dirk.fauth@googlemail.com> - initial API and implementation
  *******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.hideshow.command;
 
 import static org.junit.Assert.assertTrue;
 
-import java.util.ArrayList;
 import java.util.Collection;
 
 import org.junit.Test;
@@ -20,18 +19,16 @@
 public class MultiRowShowCommandTest {
 
     @Test
-    public void testClone() throws Exception {
-        Collection<Integer> rowIndexes = new ArrayList<Integer>();
-        rowIndexes.add(3);
-        rowIndexes.add(6);
-        rowIndexes.add(9);
-        rowIndexes.add(12);
-        MultiRowShowCommand command = new MultiRowShowCommand(rowIndexes);
+    public void testClone() {
+        MultiRowShowCommand command = new MultiRowShowCommand(3, 6, 9, 12);
         MultiRowShowCommand copiedCommand = command.cloneCommand();
 
         Collection<Integer> commandIndexes = command.getRowIndexes();
         Collection<Integer> cloneIndexes = copiedCommand.getRowIndexes();
 
+        int[] commandIndexesArray = command.getRowIndexesArray();
+        int[] cloneIndexesArray = copiedCommand.getRowIndexesArray();
+
         assertTrue("The commands reference the same instance",
                 command != copiedCommand);
         assertTrue("The command collections reference the same instance",
@@ -44,5 +41,16 @@
                 cloneIndexes.contains(9));
         assertTrue("The cloned command does not contain index 12",
                 cloneIndexes.contains(12));
+
+        assertTrue("The command arrays reference the same instance",
+                commandIndexesArray != cloneIndexesArray);
+        assertTrue("The cloned command does not contain index 3",
+                cloneIndexesArray[0] == 3);
+        assertTrue("The cloned command does not contain index 6",
+                cloneIndexesArray[1] == 6);
+        assertTrue("The cloned command does not contain index 9",
+                cloneIndexesArray[2] == 9);
+        assertTrue("The cloned command does not contain index 12",
+                cloneIndexesArray[3] == 12);
     }
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/event/HideShowColumnEventTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/event/HideShowColumnEventTest.java
index 52a6348..6edbc60 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/event/HideShowColumnEventTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/event/HideShowColumnEventTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2019 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -46,7 +46,7 @@
 
     @Test
     public void shouldFireHideColumnPositionsEventOnHide() {
-        this.hideShowLayer.hideColumnPositions(Arrays.asList(1, 4));
+        this.hideShowLayer.hideColumnPositions(1, 4);
 
         assertTrue(this.hideShowLayer.isColumnIndexHidden(1));
         assertTrue(this.hideShowLayer.isColumnIndexHidden(4));
@@ -67,7 +67,7 @@
         // reorder first two columns to the end
         this.reorderLayer.reorderMultipleColumnPositions(Arrays.asList(0, 1), 4, false);
 
-        this.hideShowLayer.hideColumnPositions(Arrays.asList(1, 4));
+        this.hideShowLayer.hideColumnPositions(1, 4);
 
         assertTrue(this.hideShowLayer.isColumnIndexHidden(3));
         assertTrue(this.hideShowLayer.isColumnIndexHidden(1));
@@ -85,7 +85,7 @@
 
     @Test
     public void shouldFireShowColumnPositionsEventOnShowIndexes() {
-        this.hideShowLayer.hideColumnPositions(Arrays.asList(1, 4));
+        this.hideShowLayer.hideColumnPositions(1, 4);
 
         // hide is tested in another test, so clear the test initialization
         this.layerListener.clearReceivedEvents();
@@ -93,7 +93,7 @@
         assertTrue(this.hideShowLayer.isColumnIndexHidden(1));
         assertTrue(this.hideShowLayer.isColumnIndexHidden(4));
 
-        this.hideShowLayer.showColumnIndexes(Arrays.asList(1, 4));
+        this.hideShowLayer.showColumnIndexes(1, 4);
         assertFalse(this.hideShowLayer.isColumnIndexHidden(1));
         assertFalse(this.hideShowLayer.isColumnIndexHidden(4));
 
@@ -113,12 +113,12 @@
         // reorder first two columns to the end
         this.reorderLayer.reorderMultipleColumnPositions(Arrays.asList(0, 1), 4, false);
 
-        this.hideShowLayer.hideColumnPositions(Arrays.asList(1, 4));
+        this.hideShowLayer.hideColumnPositions(1, 4);
 
         assertTrue(this.hideShowLayer.isColumnIndexHidden(3));
         assertTrue(this.hideShowLayer.isColumnIndexHidden(1));
 
-        this.hideShowLayer.showColumnIndexes(Arrays.asList(3, 1));
+        this.hideShowLayer.showColumnIndexes(3, 1);
         assertFalse(this.hideShowLayer.isColumnIndexHidden(3));
         assertFalse(this.hideShowLayer.isColumnIndexHidden(1));
 
@@ -135,7 +135,7 @@
 
     @Test
     public void shouldFireShowColumnPositionsEventOnShowAll() {
-        this.hideShowLayer.hideColumnPositions(Arrays.asList(1, 4));
+        this.hideShowLayer.hideColumnPositions(1, 4);
 
         // hide is tested in another test, so clear the test initialization
         this.layerListener.clearReceivedEvents();
@@ -163,7 +163,7 @@
         // reorder first two columns to the end
         this.reorderLayer.reorderMultipleColumnPositions(Arrays.asList(0, 1), 4, false);
 
-        this.hideShowLayer.hideColumnPositions(Arrays.asList(1, 4));
+        this.hideShowLayer.hideColumnPositions(1, 4);
 
         assertTrue(this.hideShowLayer.isColumnIndexHidden(3));
         assertTrue(this.hideShowLayer.isColumnIndexHidden(1));
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/event/HideShowRowEventTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/event/HideShowRowEventTest.java
index 4440883..dd21da3 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/event/HideShowRowEventTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/event/HideShowRowEventTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2013, 2019 Dirk Fauth and others.
+ * Copyright (c) 2013, 2020 Dirk Fauth and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -46,7 +46,7 @@
 
     @Test
     public void shouldFireHideColumnPositionsEventOnHide() {
-        this.hideShowLayer.hideRowPositions(Arrays.asList(1, 4));
+        this.hideShowLayer.hideRowPositions(1, 4);
 
         assertTrue(this.hideShowLayer.isRowIndexHidden(1));
         assertTrue(this.hideShowLayer.isRowIndexHidden(4));
@@ -67,7 +67,7 @@
         // reorder first two rows to the end
         this.reorderLayer.reorderMultipleRowPositions(Arrays.asList(0, 1), 4, false);
 
-        this.hideShowLayer.hideRowPositions(Arrays.asList(1, 4));
+        this.hideShowLayer.hideRowPositions(1, 4);
 
         assertTrue(this.hideShowLayer.isRowIndexHidden(3));
         assertTrue(this.hideShowLayer.isRowIndexHidden(1));
@@ -85,7 +85,7 @@
 
     @Test
     public void shouldFireShowColumnPositionsEventOnShowIndexes() {
-        this.hideShowLayer.hideRowPositions(Arrays.asList(1, 4));
+        this.hideShowLayer.hideRowPositions(1, 4);
 
         // hide is tested in another test, so clear the test initialization
         this.layerListener.clearReceivedEvents();
@@ -93,7 +93,7 @@
         assertTrue(this.hideShowLayer.isRowIndexHidden(1));
         assertTrue(this.hideShowLayer.isRowIndexHidden(4));
 
-        this.hideShowLayer.showRowIndexes(Arrays.asList(1, 4));
+        this.hideShowLayer.showRowIndexes(1, 4);
         assertFalse(this.hideShowLayer.isRowIndexHidden(1));
         assertFalse(this.hideShowLayer.isRowIndexHidden(4));
 
@@ -113,12 +113,12 @@
         // reorder first two columns to the end
         this.reorderLayer.reorderMultipleRowPositions(Arrays.asList(0, 1), 4, false);
 
-        this.hideShowLayer.hideRowPositions(Arrays.asList(1, 4));
+        this.hideShowLayer.hideRowPositions(1, 4);
 
         assertTrue(this.hideShowLayer.isRowIndexHidden(3));
         assertTrue(this.hideShowLayer.isRowIndexHidden(1));
 
-        this.hideShowLayer.showRowIndexes(Arrays.asList(3, 1));
+        this.hideShowLayer.showRowIndexes(3, 1);
         assertFalse(this.hideShowLayer.isRowIndexHidden(3));
         assertFalse(this.hideShowLayer.isRowIndexHidden(1));
 
@@ -135,7 +135,7 @@
 
     @Test
     public void shouldFireShowColumnPositionsEventOnShowAll() {
-        this.hideShowLayer.hideRowPositions(Arrays.asList(1, 4));
+        this.hideShowLayer.hideRowPositions(1, 4);
 
         // hide is tested in another test, so clear the test initialization
         this.layerListener.clearReceivedEvents();
@@ -163,7 +163,7 @@
         // reorder first two columns to the end
         this.reorderLayer.reorderMultipleRowPositions(Arrays.asList(0, 1), 4, false);
 
-        this.hideShowLayer.hideRowPositions(Arrays.asList(1, 4));
+        this.hideShowLayer.hideRowPositions(1, 4);
 
         assertTrue(this.hideShowLayer.isRowIndexHidden(3));
         assertTrue(this.hideShowLayer.isRowIndexHidden(1));
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/event/ShowColumnPositionsEventDiffTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/event/ShowColumnPositionsEventDiffTest.java
index cf574d2..a3d23ac 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/event/ShowColumnPositionsEventDiffTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/event/ShowColumnPositionsEventDiffTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2013 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -10,13 +10,17 @@
  ******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.hideshow.event;
 
-import java.util.Arrays;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
 import java.util.Collection;
 import java.util.Iterator;
 
 import org.eclipse.nebula.widgets.nattable.coordinate.Range;
 import org.eclipse.nebula.widgets.nattable.hideshow.ColumnHideShowLayer;
-import org.eclipse.nebula.widgets.nattable.hideshow.event.ShowColumnPositionsEvent;
 import org.eclipse.nebula.widgets.nattable.layer.event.StructuralDiff;
 import org.eclipse.nebula.widgets.nattable.layer.event.StructuralDiff.DiffTypeEnum;
 import org.eclipse.nebula.widgets.nattable.test.fixture.layer.DataLayerFixture;
@@ -24,7 +28,6 @@
 import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer;
 import org.eclipse.swt.graphics.Rectangle;
 import org.junit.After;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -51,16 +54,15 @@
         this.viewportLayer.setOriginX(this.viewportLayer.getStartXOfColumnPosition(2));
         this.viewportLayer.setOriginY(this.viewportLayer.getStartYOfRowPosition(2));
 
-        this.event = new ShowColumnPositionsEvent(this.dataLayer,
-                Arrays.asList(new Integer[] { 2, 4, 7, 8, 9 }));
+        this.event = new ShowColumnPositionsEvent(this.dataLayer, 2, 4, 7, 8, 9);
     }
 
     @After
     public void after() {
-        Assert.assertTrue(this.event.isHorizontalStructureChanged());
+        assertTrue(this.event.isHorizontalStructureChanged());
 
-        Assert.assertFalse(this.event.isVerticalStructureChanged());
-        Assert.assertNull(this.event.getRowDiffs());
+        assertFalse(this.event.isVerticalStructureChanged());
+        assertNull(this.event.getRowDiffs());
     }
 
     /**
@@ -69,14 +71,14 @@
     @Test
     public void testColumnDiffs() {
         Collection<StructuralDiff> columnDiffs = this.event.getColumnDiffs();
-        Assert.assertNotNull(columnDiffs);
-        Assert.assertEquals(3, columnDiffs.size());
+        assertNotNull(columnDiffs);
+        assertEquals(3, columnDiffs.size());
         Iterator<StructuralDiff> iterator = columnDiffs.iterator();
-        Assert.assertEquals(new StructuralDiff(DiffTypeEnum.ADD,
+        assertEquals(new StructuralDiff(DiffTypeEnum.ADD,
                 new Range(2, 2), new Range(2, 3)), iterator.next());
-        Assert.assertEquals(new StructuralDiff(DiffTypeEnum.ADD,
+        assertEquals(new StructuralDiff(DiffTypeEnum.ADD,
                 new Range(3, 3), new Range(4, 5)), iterator.next());
-        Assert.assertEquals(new StructuralDiff(DiffTypeEnum.ADD,
+        assertEquals(new StructuralDiff(DiffTypeEnum.ADD,
                 new Range(5, 5), new Range(7, 10)), iterator.next());
     }
 
@@ -88,27 +90,27 @@
         this.event.convertToLocal(this.hideShowLayer);
 
         Collection<StructuralDiff> columnDiffs = this.event.getColumnDiffs();
-        Assert.assertNotNull(columnDiffs);
-        Assert.assertEquals(3, columnDiffs.size());
+        assertNotNull(columnDiffs);
+        assertEquals(3, columnDiffs.size());
         Iterator<StructuralDiff> iterator = columnDiffs.iterator();
-        Assert.assertEquals(new StructuralDiff(DiffTypeEnum.ADD,
+        assertEquals(new StructuralDiff(DiffTypeEnum.ADD,
                 new Range(2, 2), new Range(2, 3)), iterator.next());
-        Assert.assertEquals(new StructuralDiff(DiffTypeEnum.ADD,
+        assertEquals(new StructuralDiff(DiffTypeEnum.ADD,
                 new Range(3, 3), new Range(4, 5)), iterator.next());
-        Assert.assertEquals(new StructuralDiff(DiffTypeEnum.ADD,
+        assertEquals(new StructuralDiff(DiffTypeEnum.ADD,
                 new Range(5, 5), new Range(7, 10)), iterator.next());
 
         this.event.convertToLocal(this.viewportLayer);
 
         columnDiffs = this.event.getColumnDiffs();
-        Assert.assertNotNull(columnDiffs);
-        Assert.assertEquals(3, columnDiffs.size());
+        assertNotNull(columnDiffs);
+        assertEquals(3, columnDiffs.size());
         iterator = columnDiffs.iterator();
-        Assert.assertEquals(new StructuralDiff(DiffTypeEnum.ADD,
+        assertEquals(new StructuralDiff(DiffTypeEnum.ADD,
                 new Range(0, 0), new Range(0, 1)), iterator.next());
-        Assert.assertEquals(new StructuralDiff(DiffTypeEnum.ADD,
+        assertEquals(new StructuralDiff(DiffTypeEnum.ADD,
                 new Range(1, 1), new Range(2, 3)), iterator.next());
-        Assert.assertEquals(new StructuralDiff(DiffTypeEnum.ADD,
+        assertEquals(new StructuralDiff(DiffTypeEnum.ADD,
                 new Range(3, 3), new Range(5, 8)), iterator.next());
     }
 
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/event/ShowRowPositionsEventDiffTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/event/ShowRowPositionsEventDiffTest.java
index 53b4b50..7f3926e 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/event/ShowRowPositionsEventDiffTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hideshow/event/ShowRowPositionsEventDiffTest.java
@@ -1,16 +1,15 @@
 /*******************************************************************************
- * Copyright (c) 2013 Dirk Fauth and others.
+ * Copyright (c) 2013, 2020 Dirk Fauth and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
- *    Dirk Fauth <dirk.fauth@gmail.com> - initial API and implementation
+ *    Dirk Fauth <dirk.fauth@googlemail.com> - initial API and implementation
  *******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.hideshow.event;
 
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Iterator;
 
@@ -50,8 +49,7 @@
         this.viewportLayer.setOriginX(this.viewportLayer.getStartXOfColumnPosition(2));
         this.viewportLayer.setOriginY(this.viewportLayer.getStartYOfRowPosition(2));
 
-        this.event = new ShowRowPositionsEvent(this.dataLayer,
-                Arrays.asList(new Integer[] { 2, 4, 7, 8, 9 }));
+        this.event = new ShowRowPositionsEvent(this.dataLayer, 2, 4, 7, 8, 9);
     }
 
     @After
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hierarchical/HierarchicalTreeLayerTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hierarchical/HierarchicalTreeLayerTest.java
index 6802b0f..4d4daa6 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hierarchical/HierarchicalTreeLayerTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/hierarchical/HierarchicalTreeLayerTest.java
@@ -22,6 +22,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
 
 import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes;
@@ -41,6 +42,7 @@
 import org.eclipse.nebula.widgets.nattable.hideshow.RowHideShowLayer;
 import org.eclipse.nebula.widgets.nattable.hideshow.command.ColumnHideCommand;
 import org.eclipse.nebula.widgets.nattable.hideshow.command.MultiColumnHideCommand;
+import org.eclipse.nebula.widgets.nattable.hideshow.command.RowHideCommand;
 import org.eclipse.nebula.widgets.nattable.hideshow.command.RowPositionHideCommand;
 import org.eclipse.nebula.widgets.nattable.hideshow.command.ShowAllColumnsCommand;
 import org.eclipse.nebula.widgets.nattable.hideshow.command.ShowAllRowsCommand;
@@ -48,6 +50,7 @@
 import org.eclipse.nebula.widgets.nattable.hideshow.event.ShowRowPositionsEvent;
 import org.eclipse.nebula.widgets.nattable.hideshow.indicator.HideIndicatorConstants;
 import org.eclipse.nebula.widgets.nattable.hierarchical.HierarchicalTreeLayer.HierarchicalTreeNode;
+import org.eclipse.nebula.widgets.nattable.hierarchical.command.HierarchicalTreeExpandCollapseCommand;
 import org.eclipse.nebula.widgets.nattable.layer.AbstractDpiConverter;
 import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
 import org.eclipse.nebula.widgets.nattable.layer.IDpiConverter;
@@ -1557,6 +1560,9 @@
         Collection<Range> rowPositionRanges = hideEvent.getRowPositionRanges();
         assertEquals(1, rowPositionRanges.size());
         assertEquals(new Range(1, 2), rowPositionRanges.iterator().next());
+        int[] rowIndexes = hideEvent.getRowIndexes();
+        assertEquals(1, rowIndexes.length);
+        assertEquals(1, rowIndexes[0]);
         this.layerListener.clearReceivedEvents();
 
         // collapse first node in first level
@@ -1574,7 +1580,14 @@
         hideEvent = (HideRowPositionsEvent) this.layerListener.getReceivedEvent(HideRowPositionsEvent.class);
         rowPositionRanges = hideEvent.getRowPositionRanges();
         assertEquals(1, rowPositionRanges.size());
-        assertEquals(new Range(1, 5), rowPositionRanges.iterator().next());
+        // only range 1 to 4 because one row is already hidden through previous
+        // collapse
+        assertEquals(new Range(1, 4), rowPositionRanges.iterator().next());
+        rowIndexes = hideEvent.getRowIndexes();
+        assertEquals(3, rowIndexes.length);
+        assertEquals(2, rowIndexes[0]);
+        assertEquals(3, rowIndexes[1]);
+        assertEquals(4, rowIndexes[2]);
 
         // expand first node in first level again
         this.treeLayer.doCommand(new TreeExpandCollapseCommand(0, 0));
@@ -1593,7 +1606,19 @@
         ShowRowPositionsEvent showEvent = (ShowRowPositionsEvent) this.layerListener.getReceivedEvent(ShowRowPositionsEvent.class);
         rowPositionRanges = showEvent.getRowPositionRanges();
         assertEquals(1, rowPositionRanges.size());
-        assertEquals(new Range(2, 5), rowPositionRanges.iterator().next());
+        assertEquals(new Range(1, 4), rowPositionRanges.iterator().next());
+
+        // first sub node is still collapsed, the indexes 2, 3, 4 are shown
+        // again, 1 stays hidden
+        rowIndexes = showEvent.getRowIndexes();
+        assertEquals(3, rowIndexes.length);
+        assertEquals(2, rowIndexes[0]);
+        assertEquals(3, rowIndexes[1]);
+        assertEquals(4, rowIndexes[2]);
+
+        int[] hiddenRowIndexesArray = this.treeLayer.getHiddenRowIndexesArray();
+        assertEquals(1, hiddenRowIndexesArray.length);
+        assertEquals(1, hiddenRowIndexesArray[0]);
     }
 
     // collapse second - collapse first - expand first to level 1 - second also
@@ -1652,7 +1677,24 @@
         ShowRowPositionsEvent showEvent = (ShowRowPositionsEvent) this.layerListener.getReceivedEvent(ShowRowPositionsEvent.class);
         rowPositionRanges = showEvent.getRowPositionRanges();
         assertEquals(1, rowPositionRanges.size());
-        assertEquals(new Range(2, 4), rowPositionRanges.iterator().next());
+        // row positions 1 and 2 are made visible again
+        assertEquals(new Range(1, 3), rowPositionRanges.iterator().next());
+
+        // as sub nodes are still collapsed, the indexes 2 and 3 are shown
+        // again, 1 and 4 stay hidden
+        int[] rowIndexes = showEvent.getRowIndexes();
+        assertEquals(2, rowIndexes.length);
+        assertEquals(2, rowIndexes[0]);
+        assertEquals(3, rowIndexes[1]);
+
+        int[] hiddenRowIndexesArray = this.treeLayer.getHiddenRowIndexesArray();
+        assertEquals(6, hiddenRowIndexesArray.length);
+        assertEquals(1, hiddenRowIndexesArray[0]);
+        assertEquals(4, hiddenRowIndexesArray[1]);
+        assertEquals(7, hiddenRowIndexesArray[2]);
+        assertEquals(8, hiddenRowIndexesArray[3]);
+        assertEquals(9, hiddenRowIndexesArray[4]);
+        assertEquals(10, hiddenRowIndexesArray[5]);
     }
 
     @Test
@@ -2113,4 +2155,159 @@
         assertTrue(EditUtils.allCellsEditable(this.selectionLayer, configRegistry));
         assertFalse(EditUtils.allCellsEditable(this.selectionLayer, this.treeLayer, configRegistry));
     }
+
+    @Test
+    public void shouldHideAndCollapse() {
+        this.treeLayer.doCommand(new RowHideCommand(this.treeLayer, 2));
+
+        assertEquals(10, this.treeLayer.getRowCount());
+        assertTrue(this.treeLayer.isRowIndexHidden(2));
+
+        this.layerListener.clearReceivedEvents();
+
+        this.treeLayer.doCommand(new HierarchicalTreeExpandCollapseCommand(0, 0));
+
+        assertEquals(7, this.treeLayer.getRowCount());
+
+        assertTrue(this.treeLayer.hasHiddenRows());
+
+        int[] hiddenRowIndexes = this.treeLayer.getHiddenRowIndexesArray();
+        assertEquals(4, hiddenRowIndexes.length);
+        assertEquals(1, hiddenRowIndexes[0]);
+        assertEquals(2, hiddenRowIndexes[1]);
+        assertEquals(3, hiddenRowIndexes[2]);
+        assertEquals(4, hiddenRowIndexes[3]);
+
+        assertEquals(1, this.layerListener.getEventsCount());
+        assertTrue(this.layerListener.containsInstanceOf(HideRowPositionsEvent.class));
+
+        HideRowPositionsEvent receivedEvent = (HideRowPositionsEvent) this.layerListener.getReceivedEvent(HideRowPositionsEvent.class);
+        Collection<Range> rowPositionRanges = receivedEvent.getRowPositionRanges();
+        assertEquals(1, rowPositionRanges.size());
+        assertEquals(new Range(1, 4), rowPositionRanges.iterator().next());
+
+        int[] rowIndexes = receivedEvent.getRowIndexes();
+        // only 3 indexes hidden as 1 index was already hidden in an underlying
+        // layer before
+        assertEquals(3, rowIndexes.length);
+        assertEquals(1, rowIndexes[0]);
+        assertEquals(3, rowIndexes[1]);
+        assertEquals(4, rowIndexes[2]);
+    }
+
+    @Test
+    public void shouldCollapseAllWithHidden() {
+        this.treeLayer.doCommand(new RowHideCommand(this.treeLayer, 2));
+
+        assertEquals(10, this.treeLayer.getRowCount());
+
+        this.layerListener.clearReceivedEvents();
+
+        this.treeLayer.doCommand(new TreeCollapseAllCommand());
+
+        assertEquals(3, this.treeLayer.getRowCount());
+
+        int[] hiddenRowIndexes = this.treeLayer.getHiddenRowIndexesArray();
+        assertEquals(8, hiddenRowIndexes.length);
+
+        assertEquals(1, this.layerListener.getEventsCount());
+        assertTrue(this.layerListener.containsInstanceOf(HideRowPositionsEvent.class));
+
+        HideRowPositionsEvent receivedEvent = (HideRowPositionsEvent) this.layerListener.getReceivedEvent(HideRowPositionsEvent.class);
+        Collection<Range> rowPositionRanges = receivedEvent.getRowPositionRanges();
+        assertEquals(2, rowPositionRanges.size());
+        Iterator<Range> iterator = rowPositionRanges.iterator();
+        assertEquals(new Range(1, 4), iterator.next());
+        assertEquals(new Range(6, 10), iterator.next());
+
+        int[] rowIndexes = receivedEvent.getRowIndexes();
+        // 7 because 1 row was already hidden
+        assertEquals(7, rowIndexes.length);
+        assertEquals(1, rowIndexes[0]);
+        assertEquals(3, rowIndexes[1]);
+        assertEquals(4, rowIndexes[2]);
+        assertEquals(7, rowIndexes[3]);
+        assertEquals(8, rowIndexes[4]);
+        assertEquals(9, rowIndexes[5]);
+        assertEquals(10, rowIndexes[6]);
+    }
+
+    @Test
+    public void shouldExpandWithHidden() {
+        this.treeLayer.doCommand(new RowHideCommand(this.treeLayer, 2));
+
+        // collapse
+        this.treeLayer.doCommand(new HierarchicalTreeExpandCollapseCommand(0, 0));
+
+        this.layerListener.clearReceivedEvents();
+
+        // expand again
+        this.treeLayer.doCommand(new HierarchicalTreeExpandCollapseCommand(0, 0));
+
+        assertEquals(10, this.treeLayer.getRowCount());
+
+        assertFalse(this.treeLayer.hasHiddenRows());
+
+        assertTrue(this.treeLayer.isRowIndexHidden(2));
+
+        int[] hiddenRowIndexes = this.treeLayer.getHiddenRowIndexesArray();
+        assertEquals(0, hiddenRowIndexes.length);
+
+        assertEquals(1, this.layerListener.getEventsCount());
+        assertTrue(this.layerListener.containsInstanceOf(ShowRowPositionsEvent.class));
+
+        ShowRowPositionsEvent receivedEvent = (ShowRowPositionsEvent) this.layerListener.getReceivedEvent(ShowRowPositionsEvent.class);
+        Collection<Range> rowPositionRanges = receivedEvent.getRowPositionRanges();
+        assertEquals(1, rowPositionRanges.size());
+        assertEquals(new Range(1, 4), rowPositionRanges.iterator().next());
+
+        int[] rowIndexes = receivedEvent.getRowIndexes();
+        // only 3 indexes shown again as 1 index is still hidden in an
+        // underlying layer
+        assertEquals(3, rowIndexes.length);
+        assertEquals(1, rowIndexes[0]);
+        assertEquals(3, rowIndexes[1]);
+        assertEquals(4, rowIndexes[2]);
+    }
+
+    @Test
+    public void shouldExpandAllWithHidden() {
+        this.treeLayer.doCommand(new RowHideCommand(this.treeLayer, 2));
+
+        // collapse
+        this.treeLayer.doCommand(new TreeCollapseAllCommand());
+
+        this.layerListener.clearReceivedEvents();
+
+        // expand again
+        this.treeLayer.doCommand(new TreeExpandAllCommand());
+
+        assertEquals(10, this.treeLayer.getRowCount());
+
+        assertTrue(this.treeLayer.isRowIndexHidden(2));
+        assertFalse(this.treeLayer.hasHiddenRows());
+
+        int[] hiddenRowIndexes = this.treeLayer.getHiddenRowIndexesArray();
+        assertEquals(0, hiddenRowIndexes.length);
+
+        assertEquals(1, this.layerListener.getEventsCount());
+        assertTrue(this.layerListener.containsInstanceOf(ShowRowPositionsEvent.class));
+
+        ShowRowPositionsEvent receivedEvent = (ShowRowPositionsEvent) this.layerListener.getReceivedEvent(ShowRowPositionsEvent.class);
+        Collection<Range> rowPositionRanges = receivedEvent.getRowPositionRanges();
+        Iterator<Range> iterator = rowPositionRanges.iterator();
+        assertEquals(new Range(1, 4), iterator.next());
+        assertEquals(new Range(6, 10), iterator.next());
+
+        int[] rowIndexes = receivedEvent.getRowIndexes();
+        // 7 because 1 row was already hidden
+        assertEquals(7, rowIndexes.length);
+        assertEquals(1, rowIndexes[0]);
+        assertEquals(3, rowIndexes[1]);
+        assertEquals(4, rowIndexes[2]);
+        assertEquals(7, rowIndexes[3]);
+        assertEquals(8, rowIndexes[4]);
+        assertEquals(9, rowIndexes[5]);
+        assertEquals(10, rowIndexes[6]);
+    }
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/ColumnReorderLayerTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/ColumnReorderLayerTest.java
index dfc86a6..5eb61f8 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/ColumnReorderLayerTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/ColumnReorderLayerTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2019 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -94,9 +94,7 @@
      *  Position 	0 	1	2	3
      */
     public void reorderMultipleColumnsLeftToRight() throws Exception {
-        List<Integer> fromColumnPositions = Arrays.asList(new Integer[] { 0, 1 });
-
-        this.columnReorderLayer.reorderMultipleColumnPositions(fromColumnPositions, 3);
+        this.columnReorderLayer.reorderMultipleColumnPositions(new int[] { 0, 1 }, 3);
 
         assertEquals(2, this.columnReorderLayer.getColumnIndexByPosition(0));
         assertEquals(0, this.columnReorderLayer.getColumnIndexByPosition(1));
@@ -111,9 +109,7 @@
      *  Position 	0 	1	2	3
      */
     public void reorderMultipleColumnsLeftToRightToTheEnd() throws Exception {
-        List<Integer> fromColumnPositions = Arrays.asList(new Integer[] { 0, 1 });
-
-        this.columnReorderLayer.reorderMultipleColumnPositions(fromColumnPositions, 4);
+        this.columnReorderLayer.reorderMultipleColumnPositions(new int[] { 0, 1 }, 4);
 
         assertEquals(2, this.columnReorderLayer.getColumnIndexByPosition(0));
         assertEquals(3, this.columnReorderLayer.getColumnIndexByPosition(1));
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/ColumnReorderLayerTest2.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/ColumnReorderLayerTest2.java
index 04ffd0d..b17caaf 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/ColumnReorderLayerTest2.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/ColumnReorderLayerTest2.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2013 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -10,13 +10,15 @@
  ******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.reorder;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
 import java.util.Properties;
 
 import org.eclipse.nebula.widgets.nattable.layer.event.ColumnStructuralRefreshEvent;
 import org.eclipse.nebula.widgets.nattable.test.LayerAssert;
 import org.eclipse.nebula.widgets.nattable.test.fixture.TestLayer;
 import org.eclipse.nebula.widgets.nattable.test.fixture.layer.LayerListenerFixture;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -49,7 +51,7 @@
     }
 
     @Test
-    public void shouldLoadstateFromProperties() throws Exception {
+    public void shouldLoadstateFromProperties() {
         LayerListenerFixture listener = new LayerListenerFixture();
         this.reorderLayer.addLayerListener(listener);
 
@@ -60,18 +62,16 @@
 
         this.reorderLayer.loadState("", testProperties);
 
-        Assert.assertEquals(0, this.reorderLayer.getColumnIndexByPosition(0));
-        Assert.assertEquals(1, this.reorderLayer.getColumnIndexByPosition(1));
-        Assert.assertEquals(3, this.reorderLayer.getColumnIndexByPosition(2));
-        Assert.assertEquals(2, this.reorderLayer.getColumnIndexByPosition(3));
+        assertEquals(0, this.reorderLayer.getColumnIndexByPosition(0));
+        assertEquals(1, this.reorderLayer.getColumnIndexByPosition(1));
+        assertEquals(3, this.reorderLayer.getColumnIndexByPosition(2));
+        assertEquals(2, this.reorderLayer.getColumnIndexByPosition(3));
 
-        Assert.assertTrue(listener
-                .containsInstanceOf(ColumnStructuralRefreshEvent.class));
+        assertTrue(listener.containsInstanceOf(ColumnStructuralRefreshEvent.class));
     }
 
     @Test
-    public void skipLoadingStateIfPersistedStateDoesNotMatchDataSource()
-            throws Exception {
+    public void skipLoadingStateIfPersistedStateDoesNotMatchDataSource() {
         Properties testProperties = new Properties();
 
         // Index 5 is valid
@@ -81,21 +81,37 @@
         this.reorderLayer.loadState("", testProperties);
 
         // Ordering unchanged
-        Assert.assertEquals(0, this.reorderLayer.getColumnIndexByPosition(0));
-        Assert.assertEquals(1, this.reorderLayer.getColumnIndexByPosition(1));
-        Assert.assertEquals(2, this.reorderLayer.getColumnIndexByPosition(2));
-        Assert.assertEquals(3, this.reorderLayer.getColumnIndexByPosition(3));
+        assertEquals(0, this.reorderLayer.getColumnIndexByPosition(0));
+        assertEquals(1, this.reorderLayer.getColumnIndexByPosition(1));
+        assertEquals(2, this.reorderLayer.getColumnIndexByPosition(2));
+        assertEquals(3, this.reorderLayer.getColumnIndexByPosition(3));
 
         // Number of columns is different
-        testProperties
-                .put(ColumnReorderLayer.PERSISTENCE_KEY_COLUMN_INDEX_ORDER,
-                        "2,1,0,");
+        testProperties.put(ColumnReorderLayer.PERSISTENCE_KEY_COLUMN_INDEX_ORDER, "2,1,0,");
         this.reorderLayer.loadState("", testProperties);
 
         // Ordering unchanged
-        Assert.assertEquals(0, this.reorderLayer.getColumnIndexByPosition(0));
-        Assert.assertEquals(1, this.reorderLayer.getColumnIndexByPosition(1));
-        Assert.assertEquals(2, this.reorderLayer.getColumnIndexByPosition(2));
-        Assert.assertEquals(3, this.reorderLayer.getColumnIndexByPosition(3));
+        assertEquals(0, this.reorderLayer.getColumnIndexByPosition(0));
+        assertEquals(1, this.reorderLayer.getColumnIndexByPosition(1));
+        assertEquals(2, this.reorderLayer.getColumnIndexByPosition(2));
+        assertEquals(3, this.reorderLayer.getColumnIndexByPosition(3));
+    }
+
+    @Test
+    public void shouldSaveState() {
+        this.reorderLayer.reorderColumnPosition(0, 4);
+
+        assertEquals(1, this.reorderLayer.getColumnIndexByPosition(0));
+        assertEquals(2, this.reorderLayer.getColumnIndexByPosition(1));
+        assertEquals(3, this.reorderLayer.getColumnIndexByPosition(2));
+        assertEquals(0, this.reorderLayer.getColumnIndexByPosition(3));
+
+        Properties properties = new Properties();
+        this.reorderLayer.saveState("", properties);
+
+        assertTrue(properties.containsKey(ColumnReorderLayer.PERSISTENCE_KEY_COLUMN_INDEX_ORDER));
+
+        String order = properties.get(ColumnReorderLayer.PERSISTENCE_KEY_COLUMN_INDEX_ORDER).toString();
+        assertEquals("1,2,3,0", order);
     }
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/ColumnReorderLayerVisibleChangeTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/ColumnReorderLayerVisibleChangeTest.java
index 69e36c9..83ebaea 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/ColumnReorderLayerVisibleChangeTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/ColumnReorderLayerVisibleChangeTest.java
@@ -12,10 +12,7 @@
 
 import static org.junit.Assert.assertTrue;
 
-import java.util.Arrays;
 import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
 
 import org.eclipse.nebula.widgets.nattable.layer.ILayerListener;
 import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent;
@@ -33,38 +30,32 @@
      *  Position 	0 	1	2	3 ... 20
      */
     public void returnsCorrectPositionRectangleForMultiColumnReorderLeftCase() {
-        ColumnReorderLayer reorderLayer = new ColumnReorderLayer(
-                new BaseDataLayerFixture(20, 20));
+        ColumnReorderLayer reorderLayer = new ColumnReorderLayer(new BaseDataLayerFixture(20, 20));
 
         // Build expected cell positions to redraw
-        final Set<Rectangle> expectedPositions = new HashSet<Rectangle>();
+        HashSet<Rectangle> expectedPositions = new HashSet<Rectangle>();
         expectedPositions.add(new Rectangle(0, 0, 20, 20));
 
         reorderLayer.addLayerListener(new ILayerListener() {
             @Override
             public void handleLayerEvent(ILayerEvent event) {
                 ColumnReorderEvent multiReorder = (ColumnReorderEvent) event;
-                assertTrue(multiReorder.getChangedPositionRectangles()
-                        .containsAll(expectedPositions));
+                assertTrue(multiReorder.getChangedPositionRectangles().containsAll(expectedPositions));
             }
         });
 
         // Reorder to beginning of grid
-        List<Integer> fromColumnPositions = Arrays.asList(new Integer[] { 10,
-                11, 12, 13 });
-        reorderLayer.reorderMultipleColumnPositions(fromColumnPositions, 0);
+        reorderLayer.reorderMultipleColumnPositions(new int[] { 10, 11, 12, 13 }, 0);
 
         // Reorder to middle of grid
         expectedPositions.clear();
         expectedPositions.add(new Rectangle(10, 0, 10, 20));
-        fromColumnPositions = Arrays.asList(new Integer[] { 19, 18, 17, 16 });
-        reorderLayer.reorderMultipleColumnPositions(fromColumnPositions, 10);
+        reorderLayer.reorderMultipleColumnPositions(new int[] { 19, 18, 17, 16 }, 10);
 
         // Reorder to end of grid
         expectedPositions.clear();
         expectedPositions.add(new Rectangle(5, 0, 15, 20));
-        fromColumnPositions = Arrays.asList(new Integer[] { 5, 6, 7, 8 });
-        reorderLayer.reorderMultipleColumnPositions(fromColumnPositions, 10);
+        reorderLayer.reorderMultipleColumnPositions(new int[] { 5, 6, 7, 8 }, 10);
     }
 
     @Test
@@ -74,38 +65,32 @@
      *  Position 	0 	1	2	3 ... 20
      */
     public void returnsCorrectPositionRectangleForMultiColumnReorderRightCase() {
-        ColumnReorderLayer reorderLayer = new ColumnReorderLayer(
-                new BaseDataLayerFixture(20, 20));
+        ColumnReorderLayer reorderLayer = new ColumnReorderLayer(new BaseDataLayerFixture(20, 20));
 
         // Build expected cell positions to redraw
-        final Set<Rectangle> expectedPositions = new HashSet<Rectangle>();
+        HashSet<Rectangle> expectedPositions = new HashSet<Rectangle>();
         expectedPositions.add(new Rectangle(0, 0, 20, 20));
 
         reorderLayer.addLayerListener(new ILayerListener() {
             @Override
             public void handleLayerEvent(ILayerEvent event) {
                 ColumnReorderEvent multiReorder = (ColumnReorderEvent) event;
-                assertTrue(multiReorder.getChangedPositionRectangles()
-                        .containsAll(expectedPositions));
+                assertTrue(multiReorder.getChangedPositionRectangles().containsAll(expectedPositions));
             }
         });
 
         // Reorder from beginning of grid
-        List<Integer> fromColumnPositions = Arrays
-                .asList(new Integer[] { 0, 1 });
-        reorderLayer.reorderMultipleColumnPositions(fromColumnPositions, 2);
+        reorderLayer.reorderMultipleColumnPositions(new int[] { 0, 1 }, 2);
 
         // Reorder to middle of grid
         expectedPositions.clear();
         expectedPositions.add(new Rectangle(5, 0, 15, 20));
-        fromColumnPositions = Arrays.asList(new Integer[] { 5, 6, 7, 8 });
-        reorderLayer.reorderMultipleColumnPositions(fromColumnPositions, 10);
+        reorderLayer.reorderMultipleColumnPositions(new int[] { 5, 6, 7, 8 }, 10);
 
         // Reorder to end of grid
         expectedPositions.clear();
         expectedPositions.add(new Rectangle(10, 0, 10, 20));
-        fromColumnPositions = Arrays.asList(new Integer[] { 10, 11, 12, 13 });
-        reorderLayer.reorderMultipleColumnPositions(fromColumnPositions, 19);
+        reorderLayer.reorderMultipleColumnPositions(new int[] { 10, 11, 12, 13 }, 19);
     }
 
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/RowReorderLayerTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/RowReorderLayerTest.java
index 0cda48e..59edfb7 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/RowReorderLayerTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/RowReorderLayerTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2013, 2019 Dirk Fauth and others.
+ * Copyright (c) 2013, 2020 Dirk Fauth and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -89,9 +89,7 @@
      *  Position 	0 	1	2	3
      */
     public void reorderMultipleRowsTopToBottom() throws Exception {
-        List<Integer> fromRowsPositions = Arrays.asList(new Integer[] { 0, 1 });
-
-        this.rowReorderLayer.reorderMultipleRowPositions(fromRowsPositions, 3);
+        this.rowReorderLayer.reorderMultipleRowPositions(new int[] { 0, 1 }, 3);
 
         assertEquals(2, this.rowReorderLayer.getRowIndexByPosition(0));
         assertEquals(0, this.rowReorderLayer.getRowIndexByPosition(1));
@@ -106,9 +104,7 @@
      *  Position 	0 	1	2	3
      */
     public void reorderMultipleRowsTopToBottomToTheEnd() throws Exception {
-        List<Integer> fromRowPositions = Arrays.asList(new Integer[] { 0, 1 });
-
-        this.rowReorderLayer.reorderMultipleRowPositions(fromRowPositions, 4);
+        this.rowReorderLayer.reorderMultipleRowPositions(new int[] { 0, 1 }, 4);
 
         assertEquals(2, this.rowReorderLayer.getRowPositionByIndex(0));
         assertEquals(3, this.rowReorderLayer.getRowPositionByIndex(1));
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/RowReorderLayerTest2.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/RowReorderLayerTest2.java
index f91e688..1cb3d65 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/RowReorderLayerTest2.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/RowReorderLayerTest2.java
@@ -1,21 +1,23 @@
 /*******************************************************************************
- * Copyright (c) 2013 Dirk Fauth and others.
+ * Copyright (c) 2013, 2020 Dirk Fauth and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
- *    Dirk Fauth <dirk.fauth@gmail.com> - initial API and implementation
+ *    Dirk Fauth <dirk.fauth@googlemail.com> - initial API and implementation
  *******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.reorder;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
 import java.util.Properties;
 
 import org.eclipse.nebula.widgets.nattable.layer.event.RowStructuralRefreshEvent;
 import org.eclipse.nebula.widgets.nattable.test.fixture.TestLayer;
 import org.eclipse.nebula.widgets.nattable.test.fixture.layer.LayerListenerFixture;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -35,50 +37,63 @@
     }
 
     @Test
-    public void shouldLoadstateFromProperties() throws Exception {
+    public void shouldLoadstateFromProperties() {
         LayerListenerFixture listener = new LayerListenerFixture();
         this.reorderLayer.addLayerListener(listener);
 
         Properties testProperties = new Properties();
-        testProperties.put(RowReorderLayer.PERSISTENCE_KEY_ROW_INDEX_ORDER,
-                "0,1,3,2,");
+        testProperties.put(RowReorderLayer.PERSISTENCE_KEY_ROW_INDEX_ORDER, "0,1,3,2,");
 
         this.reorderLayer.loadState("", testProperties);
 
-        Assert.assertEquals(0, this.reorderLayer.getRowIndexByPosition(0));
-        Assert.assertEquals(1, this.reorderLayer.getRowIndexByPosition(1));
-        Assert.assertEquals(3, this.reorderLayer.getRowIndexByPosition(2));
-        Assert.assertEquals(2, this.reorderLayer.getRowIndexByPosition(3));
+        assertEquals(0, this.reorderLayer.getRowIndexByPosition(0));
+        assertEquals(1, this.reorderLayer.getRowIndexByPosition(1));
+        assertEquals(3, this.reorderLayer.getRowIndexByPosition(2));
+        assertEquals(2, this.reorderLayer.getRowIndexByPosition(3));
 
-        Assert.assertTrue(listener
-                .containsInstanceOf(RowStructuralRefreshEvent.class));
+        assertTrue(listener.containsInstanceOf(RowStructuralRefreshEvent.class));
     }
 
     @Test
-    public void skipLoadingStateIfPersistedStateDoesNotMatchDataSource()
-            throws Exception {
+    public void skipLoadingStateIfPersistedStateDoesNotMatchDataSource() {
         Properties testProperties = new Properties();
 
         // Index 5 is valid
-        testProperties.put(RowReorderLayer.PERSISTENCE_KEY_ROW_INDEX_ORDER,
-                "0,1,5,2,");
+        testProperties.put(RowReorderLayer.PERSISTENCE_KEY_ROW_INDEX_ORDER, "0,1,5,2,");
         this.reorderLayer.loadState("", testProperties);
 
         // Ordering unchanged
-        Assert.assertEquals(0, this.reorderLayer.getRowIndexByPosition(0));
-        Assert.assertEquals(1, this.reorderLayer.getRowIndexByPosition(1));
-        Assert.assertEquals(2, this.reorderLayer.getRowIndexByPosition(2));
-        Assert.assertEquals(3, this.reorderLayer.getRowIndexByPosition(3));
+        assertEquals(0, this.reorderLayer.getRowIndexByPosition(0));
+        assertEquals(1, this.reorderLayer.getRowIndexByPosition(1));
+        assertEquals(2, this.reorderLayer.getRowIndexByPosition(2));
+        assertEquals(3, this.reorderLayer.getRowIndexByPosition(3));
 
         // Number of columns is different
-        testProperties.put(RowReorderLayer.PERSISTENCE_KEY_ROW_INDEX_ORDER,
-                "2,1,0,");
+        testProperties.put(RowReorderLayer.PERSISTENCE_KEY_ROW_INDEX_ORDER, "2,1,0,");
         this.reorderLayer.loadState("", testProperties);
 
         // Ordering unchanged
-        Assert.assertEquals(0, this.reorderLayer.getRowIndexByPosition(0));
-        Assert.assertEquals(1, this.reorderLayer.getRowIndexByPosition(1));
-        Assert.assertEquals(2, this.reorderLayer.getRowIndexByPosition(2));
-        Assert.assertEquals(3, this.reorderLayer.getRowIndexByPosition(3));
+        assertEquals(0, this.reorderLayer.getRowIndexByPosition(0));
+        assertEquals(1, this.reorderLayer.getRowIndexByPosition(1));
+        assertEquals(2, this.reorderLayer.getRowIndexByPosition(2));
+        assertEquals(3, this.reorderLayer.getRowIndexByPosition(3));
+    }
+
+    @Test
+    public void shouldSaveState() {
+        this.reorderLayer.reorderRowPosition(0, 4);
+
+        assertEquals(1, this.reorderLayer.getRowIndexByPosition(0));
+        assertEquals(2, this.reorderLayer.getRowIndexByPosition(1));
+        assertEquals(3, this.reorderLayer.getRowIndexByPosition(2));
+        assertEquals(0, this.reorderLayer.getRowIndexByPosition(3));
+
+        Properties properties = new Properties();
+        this.reorderLayer.saveState("", properties);
+
+        assertTrue(properties.containsKey(RowReorderLayer.PERSISTENCE_KEY_ROW_INDEX_ORDER));
+
+        String order = properties.get(RowReorderLayer.PERSISTENCE_KEY_ROW_INDEX_ORDER).toString();
+        assertEquals("1,2,3,0", order);
     }
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/RowReorderLayerVisibleChangeTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/RowReorderLayerVisibleChangeTest.java
index 25c7f8d..a26f453 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/RowReorderLayerVisibleChangeTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/RowReorderLayerVisibleChangeTest.java
@@ -12,10 +12,7 @@
 
 import static org.junit.Assert.assertTrue;
 
-import java.util.Arrays;
 import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
 
 import org.eclipse.nebula.widgets.nattable.layer.ILayerListener;
 import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent;
@@ -33,38 +30,32 @@
      *  Position 	0 	1	2	3 ... 20
      */
     public void returnsCorrectPositionRectangleForMultiColumnReorderLeftCase() {
-        RowReorderLayer reorderLayer = new RowReorderLayer(
-                new BaseDataLayerFixture(20, 20));
+        RowReorderLayer reorderLayer = new RowReorderLayer(new BaseDataLayerFixture(20, 20));
 
         // Build expected cell positions to redraw
-        final Set<Rectangle> expectedPositions = new HashSet<Rectangle>();
+        HashSet<Rectangle> expectedPositions = new HashSet<Rectangle>();
         expectedPositions.add(new Rectangle(0, 0, 20, 20));
 
         reorderLayer.addLayerListener(new ILayerListener() {
             @Override
             public void handleLayerEvent(ILayerEvent event) {
                 RowReorderEvent multiReorder = (RowReorderEvent) event;
-                assertTrue(multiReorder.getChangedPositionRectangles()
-                        .containsAll(expectedPositions));
+                assertTrue(multiReorder.getChangedPositionRectangles().containsAll(expectedPositions));
             }
         });
 
         // Reorder to beginning of grid
-        List<Integer> fromRowPositions = Arrays.asList(new Integer[] { 10, 11,
-                12, 13 });
-        reorderLayer.reorderMultipleRowPositions(fromRowPositions, 0);
+        reorderLayer.reorderMultipleRowPositions(new int[] { 10, 11, 12, 13 }, 0);
 
         // Reorder to middle of grid
         expectedPositions.clear();
         expectedPositions.add(new Rectangle(0, 10, 20, 10));
-        fromRowPositions = Arrays.asList(new Integer[] { 19, 18, 17, 16 });
-        reorderLayer.reorderMultipleRowPositions(fromRowPositions, 10);
+        reorderLayer.reorderMultipleRowPositions(new int[] { 19, 18, 17, 16 }, 10);
 
         // Reorder to end of grid
         expectedPositions.clear();
         expectedPositions.add(new Rectangle(0, 5, 20, 15));
-        fromRowPositions = Arrays.asList(new Integer[] { 5, 6, 7, 8 });
-        reorderLayer.reorderMultipleRowPositions(fromRowPositions, 10);
+        reorderLayer.reorderMultipleRowPositions(new int[] { 5, 6, 7, 8 }, 10);
     }
 
     @Test
@@ -74,37 +65,32 @@
      *  Position 	0 	1	2	3 ... 20
      */
     public void returnsCorrectPositionRectangleForMultiColumnReorderRightCase() {
-        RowReorderLayer reorderLayer = new RowReorderLayer(
-                new BaseDataLayerFixture(20, 20));
+        RowReorderLayer reorderLayer = new RowReorderLayer(new BaseDataLayerFixture(20, 20));
 
         // Build expected cell positions to redraw
-        final Set<Rectangle> expectedPositions = new HashSet<Rectangle>();
+        HashSet<Rectangle> expectedPositions = new HashSet<Rectangle>();
         expectedPositions.add(new Rectangle(0, 0, 20, 20));
 
         reorderLayer.addLayerListener(new ILayerListener() {
             @Override
             public void handleLayerEvent(ILayerEvent event) {
                 RowReorderEvent multiReorder = (RowReorderEvent) event;
-                assertTrue(multiReorder.getChangedPositionRectangles()
-                        .containsAll(expectedPositions));
+                assertTrue(multiReorder.getChangedPositionRectangles().containsAll(expectedPositions));
             }
         });
 
         // Reorder from beginning of grid
-        List<Integer> fromRowPositions = Arrays.asList(new Integer[] { 0, 1 });
-        reorderLayer.reorderMultipleRowPositions(fromRowPositions, 2);
+        reorderLayer.reorderMultipleRowPositions(new int[] { 0, 1 }, 2);
 
         // Reorder to middle of grid
         expectedPositions.clear();
         expectedPositions.add(new Rectangle(0, 5, 20, 15));
-        fromRowPositions = Arrays.asList(new Integer[] { 5, 6, 7, 8 });
-        reorderLayer.reorderMultipleRowPositions(fromRowPositions, 10);
+        reorderLayer.reorderMultipleRowPositions(new int[] { 5, 6, 7, 8 }, 10);
 
         // Reorder to end of grid
         expectedPositions.clear();
         expectedPositions.add(new Rectangle(0, 10, 20, 10));
-        fromRowPositions = Arrays.asList(new Integer[] { 10, 11, 12, 13 });
-        reorderLayer.reorderMultipleRowPositions(fromRowPositions, 19);
+        reorderLayer.reorderMultipleRowPositions(new int[] { 10, 11, 12, 13 }, 19);
     }
 
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/event/MultiColumnReorderEventDiffTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/event/MultiColumnReorderEventDiffTest.java
index 925761f..ee60e09 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/event/MultiColumnReorderEventDiffTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/event/MultiColumnReorderEventDiffTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2019 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -16,7 +16,6 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Iterator;
 
@@ -68,8 +67,8 @@
     public void testReorderRightColumnDiffs() {
         this.event = new ColumnReorderEvent(
                 this.dataLayer,
-                Arrays.asList(new Integer[] { 2, 3, 4 }),
-                Arrays.asList(new Integer[] { 2, 3, 4 }),
+                new int[] { 2, 3, 4 },
+                new int[] { 2, 3, 4 },
                 7,
                 7,
                 true);
@@ -89,8 +88,8 @@
     public void testReorderRightConvertToLocal() {
         this.event = new ColumnReorderEvent(
                 this.dataLayer,
-                Arrays.asList(new Integer[] { 2, 3, 4 }),
-                Arrays.asList(new Integer[] { 2, 3, 4 }),
+                new int[] { 2, 3, 4 },
+                new int[] { 2, 3, 4 },
                 7,
                 7,
                 true);
@@ -112,8 +111,8 @@
     public void testReorderLeftColumnDiffs() {
         this.event = new ColumnReorderEvent(
                 this.dataLayer,
-                Arrays.asList(new Integer[] { 7, 8, 9 }),
-                Arrays.asList(new Integer[] { 7, 8, 9 }),
+                new int[] { 7, 8, 9 },
+                new int[] { 7, 8, 9 },
                 2,
                 2,
                 true);
@@ -134,8 +133,8 @@
     public void testReorderLeftConvertToLocal() {
         this.event = new ColumnReorderEvent(
                 this.dataLayer,
-                Arrays.asList(new Integer[] { 7, 8, 9 }),
-                Arrays.asList(new Integer[] { 7, 8, 9 }),
+                new int[] { 7, 8, 9 },
+                new int[] { 7, 8, 9 },
                 2,
                 2,
                 true);
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/event/MultiRowReorderEventDiffTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/event/MultiRowReorderEventDiffTest.java
index 9775832..d794e84 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/event/MultiRowReorderEventDiffTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/reorder/event/MultiRowReorderEventDiffTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2019 Dirk Fauth.
+ * Copyright (c) 2019, 2020 Dirk Fauth.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -16,7 +16,6 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Iterator;
 
@@ -68,8 +67,8 @@
     public void testReorderRightRowDiffs() {
         this.event = new RowReorderEvent(
                 this.dataLayer,
-                Arrays.asList(new Integer[] { 2, 3, 4 }),
-                Arrays.asList(new Integer[] { 2, 3, 4 }),
+                new int[] { 2, 3, 4 },
+                new int[] { 2, 3, 4 },
                 7,
                 7,
                 true);
@@ -89,8 +88,8 @@
     public void testReorderRightConvertToLocal() {
         this.event = new RowReorderEvent(
                 this.dataLayer,
-                Arrays.asList(new Integer[] { 2, 3, 4 }),
-                Arrays.asList(new Integer[] { 2, 3, 4 }),
+                new int[] { 2, 3, 4 },
+                new int[] { 2, 3, 4 },
                 7,
                 7,
                 true);
@@ -112,8 +111,8 @@
     public void testReorderLeftRowDiffs() {
         this.event = new RowReorderEvent(
                 this.dataLayer,
-                Arrays.asList(new Integer[] { 7, 8, 9 }),
-                Arrays.asList(new Integer[] { 7, 8, 9 }),
+                new int[] { 7, 8, 9 },
+                new int[] { 7, 8, 9 },
                 2,
                 2,
                 true);
@@ -134,8 +133,8 @@
     public void testReorderLeftConvertToLocal() {
         this.event = new RowReorderEvent(
                 this.dataLayer,
-                Arrays.asList(new Integer[] { 7, 8, 9 }),
-                Arrays.asList(new Integer[] { 7, 8, 9 }),
+                new int[] { 7, 8, 9 },
+                new int[] { 7, 8, 9 },
                 2,
                 2,
                 true);
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/selection/HideSelectedRowsTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/selection/HideSelectedRowsTest.java
index ca4367f..317936c 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/selection/HideSelectedRowsTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/selection/HideSelectedRowsTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2013 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -10,17 +10,20 @@
  ******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.selection;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
 import java.util.Arrays;
 
 import org.eclipse.nebula.widgets.nattable.hideshow.RowHideShowLayer;
 import org.eclipse.nebula.widgets.nattable.hideshow.command.MultiRowHideCommand;
 import org.eclipse.nebula.widgets.nattable.hideshow.command.RowHideCommand;
 import org.eclipse.nebula.widgets.nattable.test.fixture.layer.DataLayerFixture;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
 public class HideSelectedRowsTest {
+
     private SelectionLayer selectionLayer;
     private RowHideShowLayer rowHideShowLayer;
 
@@ -33,7 +36,7 @@
     @Test
     public void shouldAlsoHideRowWhichIsNotSelectedButHasAMouseOverIt() {
         this.selectionLayer.doCommand(new MultiRowHideCommand(this.selectionLayer, 2));
-        Assert.assertTrue(this.rowHideShowLayer.isRowIndexHidden(2));
+        assertTrue(this.rowHideShowLayer.isRowIndexHidden(2));
     }
 
     @Test
@@ -45,13 +48,27 @@
         this.selectionLayer.doCommand(new RowHideCommand(this.selectionLayer, 3));
 
         // The previously selected row should be hidden
-        Assert.assertTrue(this.rowHideShowLayer.isRowIndexHidden(3));
-        Assert.assertEquals(6, this.selectionLayer.getRowCount());
+        assertTrue(this.rowHideShowLayer.isRowIndexHidden(3));
+        assertEquals(6, this.selectionLayer.getRowCount());
     }
 
     @Test
     public void shouldHideSelectedRow() {
         // Select row to hide
+        new SelectRowCommandHandler(this.selectionLayer).selectRows(0, new int[] { 2 }, false, false, 2);
+
+        // Hide row
+        this.selectionLayer.doCommand(new MultiRowHideCommand(this.selectionLayer, 2));
+
+        // The previously selected row should be hidden
+        assertTrue(this.rowHideShowLayer.isRowIndexHidden(2));
+        assertEquals(6, this.selectionLayer.getRowCount());
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void shouldHideSelectedRowViaCollection() {
+        // Select row to hide
         new SelectRowCommandHandler(this.selectionLayer).selectRows(0,
                 Arrays.asList(Integer.valueOf(2)), false, false, 2);
 
@@ -59,25 +76,23 @@
         this.selectionLayer.doCommand(new MultiRowHideCommand(this.selectionLayer, 2));
 
         // The previously selected row should be hidden
-        Assert.assertTrue(this.rowHideShowLayer.isRowIndexHidden(2));
-        Assert.assertEquals(6, this.selectionLayer.getRowCount());
+        assertTrue(this.rowHideShowLayer.isRowIndexHidden(2));
+        assertEquals(6, this.selectionLayer.getRowCount());
     }
 
     @Test
     public void shouldHideAllSelectedRows() {
         // Select cells and rows
-        new SelectRowCommandHandler(this.selectionLayer).selectRows(0,
-                Arrays.asList(Integer.valueOf(2)), false, false, 2);
+        new SelectRowCommandHandler(this.selectionLayer).selectRows(0, new int[] { 2 }, false, false, 2);
         this.selectionLayer.selectCell(0, 1, false, true);
         this.selectionLayer.selectCell(4, 4, false, true);
 
         // Hide selection
-        this.selectionLayer.doCommand(new MultiRowHideCommand(this.selectionLayer,
-                new int[] { 2, 0, 4 }));
+        this.selectionLayer.doCommand(new MultiRowHideCommand(this.selectionLayer, 2, 0, 4));
 
         // Previously selected rows should be hidden
-        Assert.assertTrue(this.rowHideShowLayer.isRowIndexHidden(2));
-        Assert.assertTrue(this.rowHideShowLayer.isRowIndexHidden(0));
-        Assert.assertTrue(this.rowHideShowLayer.isRowIndexHidden(4));
+        assertTrue(this.rowHideShowLayer.isRowIndexHidden(2));
+        assertTrue(this.rowHideShowLayer.isRowIndexHidden(0));
+        assertTrue(this.rowHideShowLayer.isRowIndexHidden(4));
     }
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/selection/RangeTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/selection/RangeTest.java
index 5e5494a..97325e7 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/selection/RangeTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/selection/RangeTest.java
@@ -17,7 +17,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Set;
 
 import org.eclipse.nebula.widgets.nattable.coordinate.Range;
@@ -26,23 +25,39 @@
 public class RangeTest {
 
     @Test
-    public void range() {
+    public void shouldContainStartPosition() {
         // 1 cell
         Range range = new Range(2, 3);
-
         assertTrue(range.contains(2));
+    }
+
+    @Test
+    public void shouldNotContainEndPosition() {
+        // 1 cell
+        Range range = new Range(2, 3);
         assertFalse(range.contains(3));
     }
 
     @Test
-    public void testEquality() {
+    public void shouldContainPosition() {
+        // 1 cell
+        Range range = new Range(2, 4);
+        assertTrue(range.contains(3));
+    }
+
+    @Test
+    public void shouldIdentifyEquality() {
         assertTrue(new Range(3, 10).equals(new Range(3, 10)));
+    }
+
+    @Test
+    public void shouldIdentifyNonEquality() {
         assertFalse(new Range(3, 10).equals(new Range(3, 11)));
     }
 
     @Test
-    public void sortByStart() {
-        List<Range> ranges = new ArrayList<>();
+    public void shouldSortRangesByStart() {
+        ArrayList<Range> ranges = new ArrayList<>();
         ranges.add(new Range(3, 5));
         ranges.add(new Range(3, 7));
         ranges.add(new Range(20, 25));
@@ -57,11 +72,20 @@
     }
 
     @Test
-    public void getMembers() {
+    public void shouldReturnMemberCollection() {
         Set<Integer> members = new Range(3, 10).getMembers();
 
         assertEquals(7, members.size());
         HashSet<Integer> expectedMembes = new HashSet<>(Arrays.asList(3, 4, 5, 6, 7, 8, 9));
         assertEquals(expectedMembes, members);
     }
+
+    @Test
+    public void shouldReturnMemberArray() {
+        int[] members = new Range(3, 10).getMembersArray();
+
+        assertEquals(7, members.length);
+        int[] expectedMembes = new int[] { 3, 4, 5, 6, 7, 8, 9 };
+        assertTrue("Expected member array is not the same as the returned member array", Arrays.equals(expectedMembes, members));
+    }
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/selection/SelectionModelStructuralChangeEventHandlerTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/selection/SelectionModelStructuralChangeEventHandlerTest.java
index e255090..46cf02f 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/selection/SelectionModelStructuralChangeEventHandlerTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/selection/SelectionModelStructuralChangeEventHandlerTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014 Original authors and others.
+ * Copyright (c) 2014, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -11,9 +11,9 @@
 package org.eclipse.nebula.widgets.nattable.selection;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import java.util.ArrayList;
 import java.util.List;
 
 import org.eclipse.nebula.widgets.nattable.NatTable;
@@ -34,7 +34,6 @@
 import org.eclipse.nebula.widgets.nattable.test.fixture.NatTableFixture;
 import org.eclipse.nebula.widgets.nattable.test.fixture.layer.DataLayerFixture;
 import org.eclipse.nebula.widgets.nattable.test.fixture.layer.GridLayerFixture;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -59,7 +58,7 @@
 
         this.selectionModel.handleLayerEvent(new RowDeleteEvent(this.dataLayer, 3));
 
-        Assert.assertTrue(this.selectionModel.isEmpty());
+        assertTrue(this.selectionModel.isEmpty());
     }
 
     @Test
@@ -68,8 +67,8 @@
 
         this.selectionModel.handleLayerEvent(new RowDeleteEvent(this.dataLayer, 5));
 
-        Assert.assertFalse(this.selectionModel.isEmpty());
-        Assert.assertTrue(this.selectionModel.isRowPositionSelected(3));
+        assertFalse(this.selectionModel.isEmpty());
+        assertTrue(this.selectionModel.isRowPositionSelected(3));
     }
 
     @Test
@@ -78,78 +77,65 @@
 
         this.selectionModel.handleLayerEvent(new RowDeleteEvent(this.dataLayer, 5));
 
-        Assert.assertFalse(this.selectionModel.isEmpty());
-        Assert.assertTrue(this.selectionModel.isRowPositionSelected(4));
+        assertFalse(this.selectionModel.isEmpty());
+        assertTrue(this.selectionModel.isRowPositionSelected(4));
     }
 
     @Test
     public void shouldClearSelectionIfListIsCleared() {
         this.selectionModel.addSelection(3, 4);
 
-        this.selectionModel.handleLayerEvent(new RowDeleteEvent(this.dataLayer, new Range(0, 9)));
+        this.selectionModel.handleLayerEvent(
+                new RowDeleteEvent(this.dataLayer, new Range(0, 9)));
 
-        Assert.assertTrue(this.selectionModel.isEmpty());
+        assertTrue(this.selectionModel.isEmpty());
     }
 
     @Test
     public void shouldClearSelectionOnStructuralChanges() {
         this.selectionModel.addSelection(3, 4);
-        Assert.assertFalse(this.selectionModel.isEmpty());
+        assertFalse(this.selectionModel.isEmpty());
 
         this.selectionModel.handleLayerEvent(new StructuralRefreshEvent(this.dataLayer));
-        Assert.assertTrue(this.selectionModel.isEmpty());
+        assertTrue(this.selectionModel.isEmpty());
     }
 
     @Test
     public void shouldNotClearSelectionOnStructuralChanges() {
         ((SelectionModel) this.selectionModel).setClearSelectionOnChange(false);
         this.selectionModel.addSelection(3, 4);
-        Assert.assertFalse(this.selectionModel.isEmpty());
+        assertFalse(this.selectionModel.isEmpty());
 
         this.selectionModel.handleLayerEvent(new StructuralRefreshEvent(this.dataLayer));
-        Assert.assertFalse(this.selectionModel.isEmpty());
+        assertFalse(this.selectionModel.isEmpty());
     }
 
     @Test
     public void shouldClearSelectionIfAllRowsAreHidden() {
         this.selectionModel.addSelection(3, 4);
 
-        List<Integer> rows = new ArrayList<Integer>();
-        rows.add(0);
-        rows.add(1);
-        rows.add(2);
-        rows.add(3);
-        rows.add(4);
-        rows.add(5);
-        rows.add(6);
-        rows.add(7);
-        rows.add(8);
-        rows.add(9);
-        this.selectionModel.handleLayerEvent(new HideRowPositionsEvent(this.dataLayer, rows));
+        this.selectionModel.handleLayerEvent(
+                new HideRowPositionsEvent(this.dataLayer, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9));
 
-        Assert.assertTrue(this.selectionModel.isEmpty());
+        assertTrue(this.selectionModel.isEmpty());
     }
 
     @Test
     public void shouldClearSelectionIfSelectedColumnIsHidden() {
         this.selectionModel.addSelection(3, 4);
 
-        List<Integer> columns = new ArrayList<Integer>();
-        columns.add(3);
-        this.selectionModel.handleLayerEvent(new HideColumnPositionsEvent(this.dataLayer, columns));
+        this.selectionModel.handleLayerEvent(new HideColumnPositionsEvent(this.dataLayer, 3));
 
-        Assert.assertTrue(this.selectionModel.isEmpty());
+        assertTrue(this.selectionModel.isEmpty());
     }
 
     @Test
     public void shouldNotClearSelectionIfOtherColumnIsHidden() {
         this.selectionModel.addSelection(3, 4);
 
-        List<Integer> columns = new ArrayList<Integer>();
-        columns.add(2);
-        this.selectionModel.handleLayerEvent(new HideColumnPositionsEvent(this.dataLayer, columns));
+        this.selectionModel.handleLayerEvent(new HideColumnPositionsEvent(this.dataLayer, 2));
 
-        Assert.assertTrue(this.selectionModel.isEmpty());
+        assertTrue(this.selectionModel.isEmpty());
     }
 
     @Test
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/test/fixture/layer/ColumnHideShowLayerFixture.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/test/fixture/layer/ColumnHideShowLayerFixture.java
index 5f1ff47..b2b0efa 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/test/fixture/layer/ColumnHideShowLayerFixture.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/test/fixture/layer/ColumnHideShowLayerFixture.java
@@ -10,11 +10,6 @@
  ******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.test.fixture.layer;
 
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-
 import org.eclipse.nebula.widgets.nattable.command.ILayerCommand;
 import org.eclipse.nebula.widgets.nattable.hideshow.ColumnHideShowLayer;
 import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer;
@@ -31,8 +26,7 @@
         // Column reorder fixture index positions: 4 1 0 2 3
         super(new ColumnReorderLayerFixture());
 
-        List<Integer> columnPositions = Arrays.asList(2, 4);
-        hideColumnPositions(columnPositions);
+        hideColumnPositions(2, 4);
     }
 
     public ColumnHideShowLayerFixture(IUniqueIndexLayer underlyingLayerFixture) {
@@ -42,12 +36,7 @@
     public ColumnHideShowLayerFixture(int... columnPositionsToHide) {
         super(new DataLayerFixture(10, 10, 20, 5));
 
-        Collection<Integer> columnPositions = new HashSet<Integer>();
-        for (int columnPosition : columnPositionsToHide) {
-            columnPositions.add(Integer.valueOf(columnPosition));
-        }
-
-        hideColumnPositions(columnPositions);
+        hideColumnPositions(columnPositionsToHide);
     }
 
     @Override
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/test/fixture/layer/RowHideShowLayerFixture.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/test/fixture/layer/RowHideShowLayerFixture.java
index 3b49414..92a8ab8 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/test/fixture/layer/RowHideShowLayerFixture.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/test/fixture/layer/RowHideShowLayerFixture.java
@@ -10,11 +10,6 @@
  *******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.test.fixture.layer;
 
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-
 import org.eclipse.nebula.widgets.nattable.command.ILayerCommand;
 import org.eclipse.nebula.widgets.nattable.hideshow.RowHideShowLayer;
 import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer;
@@ -31,8 +26,7 @@
         // Row reorder fixture index positions: 4 1 0 2 3 5 6
         super(new RowReorderLayerFixture());
 
-        List<Integer> rowPositions = Arrays.asList(2, 4);
-        hideRowPositions(rowPositions);
+        hideRowPositions(2, 4);
     }
 
     public RowHideShowLayerFixture(IUniqueIndexLayer underlyingLayerFixture) {
@@ -42,12 +36,7 @@
     public RowHideShowLayerFixture(int... rowPositionsToHide) {
         super(new DataLayerFixture(10, 10, 20, 5));
 
-        Collection<Integer> rowPositions = new HashSet<Integer>();
-        for (int rowPosition : rowPositionsToHide) {
-            rowPositions.add(Integer.valueOf(rowPosition));
-        }
-
-        hideRowPositions(rowPositions);
+        hideRowPositions(rowPositionsToHide);
     }
 
     @Override
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/test/integration/PersistenceIntegrationTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/test/integration/PersistenceIntegrationTest.java
index 47140d7..c6451ce 100644
--- a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/test/integration/PersistenceIntegrationTest.java
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/test/integration/PersistenceIntegrationTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2013 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -144,7 +144,7 @@
         assertEquals("true",
                 this.properties
                         .get("testPrefix.BODY.columnWidth.resizableByDefault"));
-        assertEquals("1,2,3,0,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,",
+        assertEquals("1,2,3,0,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19",
                 this.properties.get("testPrefix.BODY.columnIndexOrder"));
         assertEquals("1:100,",
                 this.properties.get("testPrefix.BODY.rowHeight.sizes"));
diff --git a/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/tree/TreeLayerTest.java b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/tree/TreeLayerTest.java
new file mode 100644
index 0000000..30e1615
--- /dev/null
+++ b/org.eclipse.nebula.widgets.nattable.core.test/src/org/eclipse/nebula/widgets/nattable/tree/TreeLayerTest.java
@@ -0,0 +1,397 @@
+/*****************************************************************************
+ * Copyright (c) 2020 Dirk Fauth.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *      Dirk Fauth <dirk.fauth@googlemail.com> - Initial API and implementation
+ *
+ *****************************************************************************/
+package org.eclipse.nebula.widgets.nattable.tree;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.eclipse.nebula.widgets.nattable.coordinate.Range;
+import org.eclipse.nebula.widgets.nattable.data.IColumnPropertyAccessor;
+import org.eclipse.nebula.widgets.nattable.data.ListDataProvider;
+import org.eclipse.nebula.widgets.nattable.data.ReflectiveColumnPropertyAccessor;
+import org.eclipse.nebula.widgets.nattable.dataset.person.Person;
+import org.eclipse.nebula.widgets.nattable.dataset.person.PersonService;
+import org.eclipse.nebula.widgets.nattable.hideshow.RowHideShowLayer;
+import org.eclipse.nebula.widgets.nattable.hideshow.command.RowHideCommand;
+import org.eclipse.nebula.widgets.nattable.hideshow.event.HideRowPositionsEvent;
+import org.eclipse.nebula.widgets.nattable.hideshow.event.ShowRowPositionsEvent;
+import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
+import org.eclipse.nebula.widgets.nattable.layer.cell.ColumnLabelAccumulator;
+import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
+import org.eclipse.nebula.widgets.nattable.test.fixture.layer.LayerListenerFixture;
+import org.eclipse.nebula.widgets.nattable.tree.command.TreeCollapseAllCommand;
+import org.eclipse.nebula.widgets.nattable.tree.command.TreeExpandAllCommand;
+import org.eclipse.nebula.widgets.nattable.tree.command.TreeExpandCollapseCommand;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TreeLayerTest {
+
+    private TreeLayer treeLayer;
+    private LayerListenerFixture listener;
+
+    @Before
+    public void setup() {
+        String[] propertyNames = { "lastName", "firstName", "gender", "married", "birthday" };
+        IColumnPropertyAccessor<Person> columnPropertyAccessor = new ReflectiveColumnPropertyAccessor<>(propertyNames);
+
+        List<Person> values = PersonService.getFixedPersons();
+
+        ListDataProvider<Person> bodyDataProvider = new ListDataProvider<>(values, columnPropertyAccessor);
+        DataLayer bodyDataLayer = new DataLayer(bodyDataProvider);
+
+        // simply apply labels for every column by index
+        bodyDataLayer.setConfigLabelAccumulator(new ColumnLabelAccumulator());
+
+        ITreeRowModel<Person> treeRowModel = new TreeRowModel<>(new PersonTreeData(values));
+
+        RowHideShowLayer hideShowLayer = new RowHideShowLayer(bodyDataLayer);
+
+        SelectionLayer selectionLayer = new SelectionLayer(hideShowLayer);
+
+        this.treeLayer = new TreeLayer(selectionLayer, treeRowModel);
+        this.listener = new LayerListenerFixture();
+        this.treeLayer.addLayerListener(this.listener);
+    }
+
+    @Test
+    public void shouldHaveTreeNodes() {
+        assertTrue(this.treeLayer.isTreeColumn(0));
+        assertTrue(this.treeLayer.getModel().hasChildren(0));
+        assertTrue(this.treeLayer.getModel().hasChildren(8));
+    }
+
+    @Test
+    public void shouldCollapseAll() {
+        this.treeLayer.collapseAll();
+        assertEquals(2, this.treeLayer.getRowCount());
+
+        assertEquals(1, this.listener.getEventsCount());
+        assertTrue(this.listener.containsInstanceOf(HideRowPositionsEvent.class));
+        HideRowPositionsEvent event = (HideRowPositionsEvent) this.listener.getReceivedEvents().get(0);
+        assertEquals(2, event.getRowPositionRanges().size());
+        Iterator<Range> iterator = event.getRowPositionRanges().iterator();
+        assertEquals(new Range(1, 8), iterator.next());
+        assertEquals(new Range(9, 18), iterator.next());
+    }
+
+    @Test
+    public void shouldHideAndCollapse() {
+        this.treeLayer.doCommand(new RowHideCommand(this.treeLayer, 2));
+
+        assertEquals(17, this.treeLayer.getRowCount());
+
+        this.listener.clearReceivedEvents();
+
+        this.treeLayer.doCommand(new TreeExpandCollapseCommand(0));
+
+        // 10 Simpsons + 1 Flanders parent node
+        assertEquals(11, this.treeLayer.getRowCount());
+
+        assertTrue(this.treeLayer.hasHiddenRows());
+
+        int[] hiddenRowIndexes = this.treeLayer.getHiddenRowIndexesArray();
+        assertEquals(7, hiddenRowIndexes.length);
+        assertEquals(1, hiddenRowIndexes[0]);
+        assertEquals(2, hiddenRowIndexes[1]);
+        assertEquals(3, hiddenRowIndexes[2]);
+        assertEquals(4, hiddenRowIndexes[3]);
+        assertEquals(5, hiddenRowIndexes[4]);
+        assertEquals(6, hiddenRowIndexes[5]);
+        assertEquals(7, hiddenRowIndexes[6]);
+
+        assertEquals(1, this.listener.getEventsCount());
+        assertTrue(this.listener.containsInstanceOf(HideRowPositionsEvent.class));
+
+        HideRowPositionsEvent receivedEvent = (HideRowPositionsEvent) this.listener.getReceivedEvent(HideRowPositionsEvent.class);
+        Collection<Range> rowPositionRanges = receivedEvent.getRowPositionRanges();
+        assertEquals(1, rowPositionRanges.size());
+        assertEquals(new Range(1, 7), rowPositionRanges.iterator().next());
+
+        int[] rowIndexes = receivedEvent.getRowIndexes();
+        // only 6 indexes hidden as 1 index was already hidden in an underlying
+        // layer before
+        assertEquals(6, rowIndexes.length);
+        assertEquals(1, rowIndexes[0]);
+        assertEquals(3, rowIndexes[1]);
+        assertEquals(4, rowIndexes[2]);
+        assertEquals(5, rowIndexes[3]);
+        assertEquals(6, rowIndexes[4]);
+        assertEquals(7, rowIndexes[5]);
+    }
+
+    @Test
+    public void shouldCollapseAllWithHidden() {
+        this.treeLayer.doCommand(new RowHideCommand(this.treeLayer, 2));
+
+        assertEquals(17, this.treeLayer.getRowCount());
+
+        this.listener.clearReceivedEvents();
+
+        this.treeLayer.doCommand(new TreeCollapseAllCommand());
+
+        // 1 Simpsons parent node + 1 Flanders parent node
+        assertEquals(2, this.treeLayer.getRowCount());
+
+        int[] hiddenRowIndexes = this.treeLayer.getHiddenRowIndexesArray();
+        assertEquals(16, hiddenRowIndexes.length);
+
+        assertEquals(1, this.listener.getEventsCount());
+        assertTrue(this.listener.containsInstanceOf(HideRowPositionsEvent.class));
+
+        HideRowPositionsEvent receivedEvent = (HideRowPositionsEvent) this.listener.getReceivedEvent(HideRowPositionsEvent.class);
+        Collection<Range> rowPositionRanges = receivedEvent.getRowPositionRanges();
+        assertEquals(2, rowPositionRanges.size());
+        Iterator<Range> iterator = rowPositionRanges.iterator();
+        assertEquals(new Range(1, 7), iterator.next());
+        assertEquals(new Range(8, 17), iterator.next());
+
+        int[] rowIndexes = receivedEvent.getRowIndexes();
+        // 15 because 1 row was already hidden
+        assertEquals(15, rowIndexes.length);
+        assertEquals(1, rowIndexes[0]);
+        assertEquals(3, rowIndexes[1]);
+        assertEquals(4, rowIndexes[2]);
+        assertEquals(5, rowIndexes[3]);
+        assertEquals(6, rowIndexes[4]);
+        assertEquals(7, rowIndexes[5]);
+        assertEquals(9, rowIndexes[6]);
+        assertEquals(10, rowIndexes[7]);
+        assertEquals(11, rowIndexes[8]);
+        assertEquals(12, rowIndexes[9]);
+        assertEquals(13, rowIndexes[10]);
+        assertEquals(14, rowIndexes[11]);
+        assertEquals(15, rowIndexes[12]);
+        assertEquals(16, rowIndexes[13]);
+        assertEquals(17, rowIndexes[14]);
+    }
+
+    @Test
+    public void shouldExpandWithHidden() {
+        this.treeLayer.doCommand(new RowHideCommand(this.treeLayer, 2));
+
+        // collapse
+        this.treeLayer.doCommand(new TreeExpandCollapseCommand(0));
+
+        this.listener.clearReceivedEvents();
+
+        // expand again
+        this.treeLayer.doCommand(new TreeExpandCollapseCommand(0));
+
+        // 10 Simpsons + 7 Flanders (1 Flanders still hidden)
+        assertEquals(17, this.treeLayer.getRowCount());
+
+        assertFalse(this.treeLayer.hasHiddenRows());
+
+        assertTrue(this.treeLayer.isRowIndexHidden(2));
+
+        int[] hiddenRowIndexes = this.treeLayer.getHiddenRowIndexesArray();
+        assertEquals(0, hiddenRowIndexes.length);
+
+        assertEquals(1, this.listener.getEventsCount());
+        assertTrue(this.listener.containsInstanceOf(ShowRowPositionsEvent.class));
+
+        ShowRowPositionsEvent receivedEvent = (ShowRowPositionsEvent) this.listener.getReceivedEvent(ShowRowPositionsEvent.class);
+        Collection<Range> rowPositionRanges = receivedEvent.getRowPositionRanges();
+        assertEquals(1, rowPositionRanges.size());
+        assertEquals(new Range(1, 7), rowPositionRanges.iterator().next());
+
+        int[] rowIndexes = receivedEvent.getRowIndexes();
+        // only 6 indexes shown again as 1 index is still hidden in an
+        // underlying layer
+        assertEquals(6, rowIndexes.length);
+        assertEquals(1, rowIndexes[0]);
+        assertEquals(3, rowIndexes[1]);
+        assertEquals(4, rowIndexes[2]);
+        assertEquals(5, rowIndexes[3]);
+        assertEquals(6, rowIndexes[4]);
+        assertEquals(7, rowIndexes[5]);
+    }
+
+    @Test
+    public void shouldExpandAllWithHidden() {
+        this.treeLayer.doCommand(new RowHideCommand(this.treeLayer, 2));
+
+        // collapse
+        this.treeLayer.doCommand(new TreeCollapseAllCommand());
+
+        this.listener.clearReceivedEvents();
+
+        // expand again
+        this.treeLayer.doCommand(new TreeExpandAllCommand());
+
+        // 10 Simpsons + 7 Flanders (1 Flanders still hidden)
+        assertEquals(17, this.treeLayer.getRowCount());
+
+        assertTrue(this.treeLayer.isRowIndexHidden(2));
+
+        int[] hiddenRowIndexes = this.treeLayer.getHiddenRowIndexesArray();
+        assertEquals(0, hiddenRowIndexes.length);
+
+        assertEquals(1, this.listener.getEventsCount());
+        assertTrue(this.listener.containsInstanceOf(ShowRowPositionsEvent.class));
+
+        ShowRowPositionsEvent receivedEvent = (ShowRowPositionsEvent) this.listener.getReceivedEvent(ShowRowPositionsEvent.class);
+        Collection<Range> rowPositionRanges = receivedEvent.getRowPositionRanges();
+        Iterator<Range> iterator = rowPositionRanges.iterator();
+        assertEquals(new Range(1, 7), iterator.next());
+        assertEquals(new Range(8, 17), iterator.next());
+
+        int[] rowIndexes = receivedEvent.getRowIndexes();
+        // 15 because 1 row was already hidden
+        assertEquals(15, rowIndexes.length);
+        assertEquals(1, rowIndexes[0]);
+        assertEquals(3, rowIndexes[1]);
+        assertEquals(4, rowIndexes[2]);
+        assertEquals(5, rowIndexes[3]);
+        assertEquals(6, rowIndexes[4]);
+        assertEquals(7, rowIndexes[5]);
+        assertEquals(9, rowIndexes[6]);
+        assertEquals(10, rowIndexes[7]);
+        assertEquals(11, rowIndexes[8]);
+        assertEquals(12, rowIndexes[9]);
+        assertEquals(13, rowIndexes[10]);
+        assertEquals(14, rowIndexes[11]);
+        assertEquals(15, rowIndexes[12]);
+        assertEquals(16, rowIndexes[13]);
+        assertEquals(17, rowIndexes[14]);
+    }
+
+    private static class PersonTreeData implements ITreeData<Person> {
+
+        private List<Person> values;
+
+        private Map<String, List<Person>> parentMapping;
+
+        private Map<String, Person> firstElementMapping = new HashMap<>();
+
+        public PersonTreeData(List<Person> values) {
+            this.values = values;
+
+            // first we need to sort by lastname to ensure all elements with the
+            // same lastname are grouped together
+            this.values.sort(Comparator.comparing(Person::getLastName));
+
+            // then we build up the mapping from lastname to all child elements
+            this.parentMapping = values.stream().collect(Collectors.groupingBy(Person::getLastName));
+
+            // identify the parent node element
+            String current = null;
+            for (Person p : this.values) {
+                if (p.getLastName() != current) {
+                    this.firstElementMapping.put(p.getLastName(), p);
+                    current = p.getLastName();
+                }
+            }
+
+            // remove the parent node element from the children list
+            this.firstElementMapping.forEach((lastname, parent) -> {
+                this.parentMapping.get(lastname).remove(parent);
+            });
+        }
+
+        @Override
+        public String formatDataForDepth(int depth, Person object) {
+            if (object != null) {
+                return object.toString();
+            } else {
+                return ""; //$NON-NLS-1$
+            }
+        }
+
+        @Override
+        public String formatDataForDepth(int depth, int index) {
+            return formatDataForDepth(depth, getDataAtIndex(index));
+        }
+
+        @Override
+        public int getDepthOfData(Person object) {
+            Person firstElement = this.firstElementMapping.get(object.getLastName());
+            return firstElement.equals(object) ? 0 : 1;
+        }
+
+        @Override
+        public int getDepthOfData(int index) {
+            return getDepthOfData(getDataAtIndex(index));
+        }
+
+        @Override
+        public Person getDataAtIndex(int index) {
+            if (!isValidIndex(index)) {
+                return null;
+            }
+            return this.values.get(index);
+        }
+
+        @Override
+        public int indexOf(Person child) {
+            return this.values.indexOf(child);
+        }
+
+        @Override
+        public boolean hasChildren(Person object) {
+            if (object != null && getDepthOfData(object) == 0) {
+                List<Person> children = this.parentMapping.get(object.getLastName());
+                return children != null && !children.isEmpty();
+            }
+            return false;
+        }
+
+        @Override
+        public boolean hasChildren(int index) {
+            return hasChildren(getDataAtIndex(index));
+        }
+
+        @Override
+        public List<Person> getChildren(Person object) {
+            if (object != null && getDepthOfData(object) == 0) {
+                return this.parentMapping.get(object.getLastName());
+            }
+            return new ArrayList<>(0);
+        }
+
+        @Override
+        public List<Person> getChildren(Person object, boolean fullDepth) {
+            // since we only support one level here it is the same as
+            // getChildren(PersonWithAddress)
+            return getChildren(object);
+        }
+
+        @Override
+        public List<Person> getChildren(int index) {
+            return getChildren(getDataAtIndex(index));
+        }
+
+        @Override
+        public int getElementCount() {
+            return this.values.size();
+        }
+
+        @Override
+        public boolean isValidIndex(int index) {
+            return (!(index < 0) && index < this.values.size());
+        }
+
+    }
+
+}
diff --git a/org.eclipse.nebula.widgets.nattable.core/META-INF/MANIFEST.MF b/org.eclipse.nebula.widgets.nattable.core/META-INF/MANIFEST.MF
index b8b5e97..d0a11cc 100644
--- a/org.eclipse.nebula.widgets.nattable.core/META-INF/MANIFEST.MF
+++ b/org.eclipse.nebula.widgets.nattable.core/META-INF/MANIFEST.MF
@@ -181,8 +181,26 @@
  org.eclipse.nebula.widgets.nattable.widget;version="2.0.0"
 Import-Package: org.apache.commons.logging;version="[1.0.0,2.0.0)",
  org.eclipse.collections.api;version="10.1.0",
+ org.eclipse.collections.api.block;version="10.1.0",
+ org.eclipse.collections.api.block.function;version="10.1.0",
+ org.eclipse.collections.api.block.function.primitive;version="10.1.0",
+ org.eclipse.collections.api.block.predicate;version="10.1.0",
+ org.eclipse.collections.api.block.predicate.primitive;version="10.1.0",
+ org.eclipse.collections.api.block.procedure;version="10.1.0",
+ org.eclipse.collections.api.block.procedure.primitive;version="10.1.0",
+ org.eclipse.collections.api.collection.primitive;version="10.1.0",
+ org.eclipse.collections.api.factory;version="10.1.0",
+ org.eclipse.collections.api.factory.list.primitive;version="10.1.0",
+ org.eclipse.collections.api.factory.map;version="10.1.0",
  org.eclipse.collections.api.factory.map.primitive;version="10.1.0",
+ org.eclipse.collections.api.factory.set.primitive;version="10.1.0",
+ org.eclipse.collections.api.iterator;version="10.1.0",
+ org.eclipse.collections.api.list;version="10.1.0",
+ org.eclipse.collections.api.list.primitive;version="10.1.0",
+ org.eclipse.collections.api.map;version="10.1.0",
  org.eclipse.collections.api.map.primitive;version="10.1.0",
+ org.eclipse.collections.api.set.primitive;version="10.1.0",
+ org.eclipse.collections.api.tuple.primitive;version="10.1.0",
  org.eclipse.collections.impl.factory.primitive;version="10.1.0",
  org.eclipse.core.commands.common,
  org.eclipse.jface.action,
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/columnChooser/ColumnChooserUtils.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/columnChooser/ColumnChooserUtils.java
index 36d5454..89de893 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/columnChooser/ColumnChooserUtils.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/columnChooser/ColumnChooserUtils.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2013 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -13,7 +13,6 @@
 import static org.eclipse.nebula.widgets.nattable.util.ObjectUtils.asIntArray;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.List;
 
 import org.eclipse.nebula.widgets.nattable.grid.layer.ColumnHeaderLayer;
@@ -26,44 +25,39 @@
 
     public static final String RENAMED_COLUMN_INDICATOR = "*"; //$NON-NLS-1$
 
-    public static void hideColumnEntries(List<ColumnEntry> removedItems,
-            ColumnHideShowLayer hideShowLayer) {
+    public static void hideColumnEntries(List<ColumnEntry> removedItems, ColumnHideShowLayer hideShowLayer) {
         MultiColumnHideCommand hideCommand = new MultiColumnHideCommand(
                 hideShowLayer,
                 asIntArray(getColumnEntryPositions(removedItems)));
         hideShowLayer.doCommand(hideCommand);
     }
 
-    public static void hideColumnPositions(List<Integer> removedPositions,
-            ColumnHideShowLayer hideShowLayer) {
+    public static void hideColumnPositions(List<Integer> removedPositions, ColumnHideShowLayer hideShowLayer) {
         MultiColumnHideCommand hideCommand = new MultiColumnHideCommand(
-                hideShowLayer, asIntArray(removedPositions));
+                hideShowLayer,
+                asIntArray(removedPositions));
         hideShowLayer.doCommand(hideCommand);
     }
 
-    public static void showColumnEntries(List<ColumnEntry> addedItems,
-            ColumnHideShowLayer hideShowLayer) {
-        hideShowLayer.doCommand(new MultiColumnShowCommand(
-                getColumnEntryIndexes(addedItems)));
+    public static void showColumnEntries(List<ColumnEntry> addedItems, ColumnHideShowLayer hideShowLayer) {
+        hideShowLayer.doCommand(new MultiColumnShowCommand(getColumnEntryIndexes(addedItems)));
     }
 
-    public static void showColumnIndexes(List<Integer> addedColumnIndexes,
-            ColumnHideShowLayer hideShowLayer) {
+    public static void showColumnIndexes(List<Integer> addedColumnIndexes, ColumnHideShowLayer hideShowLayer) {
         hideShowLayer.doCommand(new MultiColumnShowCommand(addedColumnIndexes));
     }
 
     public static List<ColumnEntry> getHiddenColumnEntries(
             ColumnHideShowLayer columnHideShowLayer,
-            ColumnHeaderLayer columnHeaderLayer, DataLayer columnHeaderDataLayer) {
-        Collection<Integer> hiddenColumnIndexes = columnHideShowLayer
-                .getHiddenColumnIndexes();
-        ArrayList<ColumnEntry> hiddenColumnEntries = new ArrayList<ColumnEntry>();
+            ColumnHeaderLayer columnHeaderLayer,
+            DataLayer columnHeaderDataLayer) {
 
-        for (Integer hiddenColumnIndex : hiddenColumnIndexes) {
-            String label = getColumnLabel(columnHeaderLayer,
-                    columnHeaderDataLayer, hiddenColumnIndex);
-            ColumnEntry columnEntry = new ColumnEntry(label, hiddenColumnIndex,
-                    Integer.valueOf(-1));
+        int[] hiddenColumnIndexes = columnHideShowLayer.getHiddenColumnIndexesArray();
+        ArrayList<ColumnEntry> hiddenColumnEntries = new ArrayList<>();
+
+        for (int hiddenColumnIndex : hiddenColumnIndexes) {
+            String label = getColumnLabel(columnHeaderLayer, columnHeaderDataLayer, hiddenColumnIndex);
+            ColumnEntry columnEntry = new ColumnEntry(label, hiddenColumnIndex, -1);
             hiddenColumnEntries.add(columnEntry);
         }
 
@@ -118,7 +112,7 @@
      */
     public static ColumnEntry find(List<ColumnEntry> entries, int indexToFind) {
         for (ColumnEntry columnEntry : entries) {
-            if (columnEntry.getIndex().equals(indexToFind)) {
+            if (columnEntry.getIndex() == indexToFind) {
                 return columnEntry;
             }
         }
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/columnChooser/ColumnEntry.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/columnChooser/ColumnEntry.java
index 61149fd..2f4c8a7 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/columnChooser/ColumnEntry.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/columnChooser/ColumnEntry.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2013 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -22,48 +22,89 @@
 public class ColumnEntry {
 
     private final String label;
-    private final Integer index;
-    private Integer position;
+    private final int index;
+    private int position;
 
-    public ColumnEntry(String label, Integer index, Integer position) {
+    /**
+     *
+     * @param label
+     *            The label of the column.
+     * @param index
+     *            The index of the column.
+     * @param position
+     *            The position of the column.
+     *
+     * @since 2.0
+     */
+    public ColumnEntry(String label, int index, int position) {
         this.label = label;
         this.index = index;
         this.position = position;
     }
 
+    /**
+     *
+     * @return The position of the column.
+     *
+     * @since 2.0
+     */
+    public int getPosition() {
+        return this.position;
+    }
+
+    /**
+     *
+     * @param position
+     *            The new position of the column.
+     *
+     * @since 2.0
+     */
+    public void setPosition(int position) {
+        this.position = position;
+    }
+
+    /**
+     *
+     * @return The index of the column.
+     *
+     * @since 2.0
+     */
+    public int getIndex() {
+        return this.index;
+    }
+
+    /**
+     *
+     * @return The label of the column.
+     */
+    public String getLabel() {
+        return toString();
+    }
+
     @Override
     public String toString() {
         return this.label != null ? this.label : Messages.getString("ColumnEntry.0"); //$NON-NLS-1$
     }
 
-    public Integer getPosition() {
-        return this.position;
-    }
-
-    public void setPosition(Integer position) {
-        this.position = position;
-    }
-
-    public Integer getIndex() {
-        return this.index;
-    }
-
-    public String getLabel() {
-        return toString();
-    }
-
     @Override
     public boolean equals(Object obj) {
-        if (obj instanceof ColumnEntry) {
-            ColumnEntry that = (ColumnEntry) obj;
-            return this.index.intValue() == that.index.intValue();
-        }
-
-        return super.equals(obj);
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        ColumnEntry other = (ColumnEntry) obj;
+        if (this.index != other.index)
+            return false;
+        return true;
     }
 
     @Override
     public int hashCode() {
-        return this.index.hashCode();
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + this.index;
+        return result;
     }
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/columnChooser/gui/ColumnChooserDialog.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/columnChooser/gui/ColumnChooserDialog.java
index 166cab3..8934beb 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/columnChooser/gui/ColumnChooserDialog.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/columnChooser/gui/ColumnChooserDialog.java
@@ -430,7 +430,7 @@
         if (columnGroupModel != null && sort) {
             ArrayList<ColumnGroup> groups = new ArrayList<>();
             for (ColumnEntry columnEntry : columnEntries) {
-                int columnEntryIndex = columnEntry.getIndex().intValue();
+                int columnEntryIndex = columnEntry.getIndex();
                 if (columnGroupModel.isPartOfAGroup(columnEntryIndex)) {
                     ColumnGroup columnGroup = columnGroupModel.getColumnGroupByIndex(columnEntryIndex);
                     groups.add(columnGroup);
@@ -452,7 +452,7 @@
 
         for (ColumnEntry columnEntry : columnEntries) {
             TreeItem treeItem;
-            int columnEntryIndex = columnEntry.getIndex().intValue();
+            int columnEntryIndex = columnEntry.getIndex();
 
             // Create a node for the column group - if needed
             if (columnGroupModel != null
@@ -1210,7 +1210,7 @@
         List<ColumnEntry> allColumnEntries = getColumnEntriesIncludingNested(this.selectedTree.getItems());
 
         for (ColumnEntry columnEntry : allColumnEntries) {
-            if (columnEntry.getPosition().intValue() == columnEntryPosition) {
+            if (columnEntry.getPosition() == columnEntryPosition) {
                 return columnEntry;
             }
         }
@@ -1362,7 +1362,7 @@
 
         for (TreeItem leaf : allLeaves) {
             if (!isColumnGroupLeaf(leaf)) {
-                int index = getColumnEntryInLeaf(leaf).getIndex().intValue();
+                int index = getColumnEntryInLeaf(leaf).getIndex();
                 if (columnEntryIndexes.contains(Integer.valueOf(index))) {
                     selectedLeaves.add(leaf);
                 }
@@ -1370,7 +1370,7 @@
                 // Check all children in column groups
                 Collection<TreeItem> columnGroupLeaves = ArrayUtil.asCollection(leaf.getItems());
                 for (TreeItem columnGroupLeaf : columnGroupLeaves) {
-                    int index = getColumnEntryInLeaf(columnGroupLeaf).getIndex().intValue();
+                    int index = getColumnEntryInLeaf(columnGroupLeaf).getIndex();
                     if (columnEntryIndexes.contains(Integer.valueOf(index))) {
                         selectedLeaves.add(columnGroupLeaf);
                     }
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/command/AbstractMultiColumnCommand.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/command/AbstractMultiColumnCommand.java
index ebb512f..58c9189 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/command/AbstractMultiColumnCommand.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/command/AbstractMultiColumnCommand.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2019 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -59,6 +59,20 @@
 
     /**
      *
+     * @return The unique column positions that should be processed by this
+     *         command.
+     *
+     * @since 2.0
+     */
+    public int[] getColumnPositionsArray() {
+        return this.columnPositionCoordinates.stream()
+                .mapToInt(ColumnPositionCoordinate::getColumnPosition)
+                .sorted()
+                .toArray();
+    }
+
+    /**
+     *
      * @param layer
      *            The {@link ILayer} to which the positions match.
      * @param columnPositions
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/command/AbstractMultiRowCommand.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/command/AbstractMultiRowCommand.java
index 0ebc225..857dbd2 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/command/AbstractMultiRowCommand.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/command/AbstractMultiRowCommand.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2019 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -59,6 +59,20 @@
 
     /**
      *
+     * @return The unique row positions that should be processed by this
+     *         command.
+     *
+     * @since 2.0
+     */
+    public int[] getRowPositionsArray() {
+        return this.rowPositionCoordinates.stream()
+                .mapToInt(RowPositionCoordinate::getRowPosition)
+                .sorted()
+                .toArray();
+    }
+
+    /**
+     *
      * @param layer
      *            The {@link ILayer} to which the positions match.
      * @param rowPositions
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/coordinate/PositionUtil.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/coordinate/PositionUtil.java
index 1ab0a24..7ee5716 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/coordinate/PositionUtil.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/coordinate/PositionUtil.java
@@ -105,7 +105,7 @@
                         });
 
         return ranges.stream()
-                .map(r -> IntStream.range(r.start, r.end).toArray())
+                .map(r -> r.getMembersArray())
                 .toArray(size -> new int[size][]);
     }
 
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/coordinate/Range.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/coordinate/Range.java
index c879a72..12b9297 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/coordinate/Range.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/coordinate/Range.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2013 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -17,8 +17,8 @@
 import java.util.Set;
 
 /**
- * Represents an Range of numbers. Example a Range of selected rows: 1 - 100
- * Ranges are inclusive of their start value and not inclusive of their end
+ * Represents a consecutive range of numbers, e.g. a range of selected rows: 1 -
+ * 100. Ranges are inclusive of their start value and exclusive of their end
  * value, i.e. start &lt;= x &lt; end
  */
 public class Range {
@@ -26,22 +26,44 @@
     public int start = 0;
     public int end = 0;
 
+    /**
+     * Create a new {@link Range}.
+     *
+     * @param start
+     *            The start position inclusive.
+     * @param end
+     *            The end position exclusive.
+     */
     public Range(int start, int end) {
         this.start = start;
         this.end = end;
     }
 
+    /**
+     *
+     * @return The size of this range.
+     */
     public int size() {
         return this.end - this.start;
     }
 
     /**
-     * @return TRUE if the range contains the given row position
+     * Check if the given position is contained in this {@link Range}.
+     *
+     * @return <code>true</code> if the range contains the given position.
      */
     public boolean contains(int position) {
         return position >= this.start && position < this.end;
     }
 
+    /**
+     * Check if the given {@link Range} overlaps this {@link Range}.
+     *
+     * @param range
+     *            The {@link Range} to check.
+     * @return <code>true</code> if the given {@link Range} contains positions
+     *         that are also contained in this {@link Range}.
+     */
     public boolean overlap(Range range) {
         return (this.start < this.end) && // this is a non-empty range
                 (range.start < range.end) && // range parameter is non-empty
@@ -49,6 +71,10 @@
                         || range.contains(this.start) || range.contains(this.end - 1));
     }
 
+    /**
+     *
+     * @return The values represented by this {@link Range}.
+     */
     public Set<Integer> getMembers() {
         Set<Integer> members = new HashSet<Integer>();
         for (int i = this.start; i < this.end; i++) {
@@ -57,6 +83,23 @@
         return members;
     }
 
+    /**
+     *
+     * @return The values represented by this {@link Range}.
+     * @since 2.0
+     */
+    public int[] getMembersArray() {
+        // iterating this way is faster than using IntStream.range()
+        int[] result = new int[this.end - this.start];
+        int i = 0;
+        for (int pos = this.start; pos < this.end; pos++) {
+            result[i] = pos;
+            i++;
+        }
+
+        return result;
+    }
+
     @Override
     public String toString() {
         return "Range[" + this.start + "," + this.end + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
@@ -87,14 +130,15 @@
         return result;
     }
 
+    /**
+     * Helper method to sort a list of {@link Range} objects by their start
+     * position.
+     *
+     * @param ranges
+     *            The {@link Range} list to sort.
+     */
     public static void sortByStart(List<Range> ranges) {
-        Collections.sort(ranges, new Comparator<Range>() {
-            @Override
-            public int compare(Range range1, Range range2) {
-                return Integer.valueOf(range1.start).compareTo(
-                        Integer.valueOf(range2.start));
-            }
-        });
+        Collections.sort(ranges, Comparator.comparing(r -> r.start));
     }
 
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/data/command/RowDeleteCommandHandler.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/data/command/RowDeleteCommandHandler.java
index a0ecc9b..d4b0a32 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/data/command/RowDeleteCommandHandler.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/data/command/RowDeleteCommandHandler.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2018 Dirk Fauth.
+ * Copyright (c) 2018, 2020 Dirk Fauth.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -12,7 +12,6 @@
 
 import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 import org.eclipse.nebula.widgets.nattable.command.ILayerCommandHandler;
 import org.eclipse.nebula.widgets.nattable.layer.ILayer;
@@ -52,12 +51,9 @@
     public boolean doCommand(ILayer targetLayer, RowDeleteCommand command) {
         // convert the transported position to the target layer
         if (command.convertToTargetLayer(targetLayer)) {
-            int[] positions = command.getRowPositions().stream()
-                    .sorted()
-                    .mapToInt(i -> i)
-                    .toArray();
+            int[] positions = command.getRowPositionsArray();
 
-            Map<Integer, T> deleted = new HashMap<Integer, T>();
+            HashMap<Integer, T> deleted = new HashMap<Integer, T>();
             for (int i = positions.length - 1; i >= 0; i--) {
                 // remove the element
                 int pos = positions[i];
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/data/validate/DefaultNumericDataValidator.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/data/validate/DefaultNumericDataValidator.java
index 1c72f0b..338d936 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/data/validate/DefaultNumericDataValidator.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/data/validate/DefaultNumericDataValidator.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2013 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -15,8 +15,9 @@
     @Override
     public boolean validate(int columnIndex, int rowIndex, Object newValue) {
         try {
-            if (newValue != null)
-                new Double(newValue.toString());
+            if (newValue != null) {
+                Double.valueOf(newValue.toString());
+            }
         } catch (Exception e) {
             return false;
         }
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/grid/command/AutoResizeColumnCommandHandler.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/grid/command/AutoResizeColumnCommandHandler.java
index bdc7134..d996aec 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/grid/command/AutoResizeColumnCommandHandler.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/grid/command/AutoResizeColumnCommandHandler.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2018 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -20,7 +20,6 @@
 import org.eclipse.nebula.widgets.nattable.resize.command.InitializeAutoResizeColumnsCommand;
 import org.eclipse.nebula.widgets.nattable.resize.command.MultiColumnResizeCommand;
 import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
-import org.eclipse.nebula.widgets.nattable.util.ObjectUtils;
 
 /**
  * This command is triggered by the {@link InitializeAutoResizeColumnsCommand}.
@@ -83,7 +82,7 @@
         // NatTable itself
         targetLayer.doCommand(new TurnViewportOffCommand());
 
-        int[] columnPositions = ObjectUtils.asIntArray(command.getColumnPositions());
+        int[] columnPositions = command.getColumnPositionsArray();
         int[] gridColumnPositions =
                 command.doPositionTransformation() ? convertFromPositionToCommandLayer(columnPositions) : columnPositions;
 
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/grid/command/AutoResizeRowCommandHandler.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/grid/command/AutoResizeRowCommandHandler.java
index 808950d..eca87b0 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/grid/command/AutoResizeRowCommandHandler.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/grid/command/AutoResizeRowCommandHandler.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2018 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -20,7 +20,6 @@
 import org.eclipse.nebula.widgets.nattable.resize.command.InitializeAutoResizeRowsCommand;
 import org.eclipse.nebula.widgets.nattable.resize.command.MultiRowResizeCommand;
 import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
-import org.eclipse.nebula.widgets.nattable.util.ObjectUtils;
 
 /**
  * This command is triggered by the {@link InitializeAutoResizeRowsCommand}. The
@@ -80,7 +79,7 @@
         // Need to resize selected rows even if they are outside the viewport
         targetLayer.doCommand(new TurnViewportOffCommand());
 
-        int[] rowPositions = ObjectUtils.asIntArray(command.getRowPositions());
+        int[] rowPositions = command.getRowPositionsArray();
         int[] gridRowPositions =
                 command.doPositionTransformation() ? convertFromPositionToCommandLayer(rowPositions) : rowPositions;
 
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/ColumnGroupExpandCollapseLayer.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/ColumnGroupExpandCollapseLayer.java
index 18836a1..63e68cd 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/ColumnGroupExpandCollapseLayer.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/ColumnGroupExpandCollapseLayer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2013 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -10,9 +10,12 @@
  ******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.group;
 
+import java.util.Arrays;
 import java.util.Collection;
-import java.util.HashSet;
+import java.util.stream.Collectors;
 
+import org.eclipse.collections.api.set.primitive.MutableIntSet;
+import org.eclipse.collections.impl.factory.primitive.IntSets;
 import org.eclipse.nebula.widgets.nattable.group.ColumnGroupModel.ColumnGroup;
 import org.eclipse.nebula.widgets.nattable.group.command.ColumnGroupExpandCollapseCommandHandler;
 import org.eclipse.nebula.widgets.nattable.hideshow.AbstractColumnHideShowLayer;
@@ -22,18 +25,15 @@
  * Tracks the Expand/Collapse of a Column Group header NOTE: Only relevant when
  * Column Grouping is enabled.
  */
-public class ColumnGroupExpandCollapseLayer extends AbstractColumnHideShowLayer
-        implements IColumnGroupModelListener {
+public class ColumnGroupExpandCollapseLayer extends AbstractColumnHideShowLayer implements IColumnGroupModelListener {
 
     private final ColumnGroupModel[] models;
 
-    public ColumnGroupExpandCollapseLayer(IUniqueIndexLayer underlyingLayer,
-            ColumnGroupModel model) {
+    public ColumnGroupExpandCollapseLayer(IUniqueIndexLayer underlyingLayer, ColumnGroupModel model) {
         this(underlyingLayer, new ColumnGroupModel[] { model });
     }
 
-    public ColumnGroupExpandCollapseLayer(IUniqueIndexLayer underlyingLayer,
-            ColumnGroupModel... models) {
+    public ColumnGroupExpandCollapseLayer(IUniqueIndexLayer underlyingLayer, ColumnGroupModel... models) {
         super(underlyingLayer);
         this.models = models;
 
@@ -47,11 +47,9 @@
     public ColumnGroupModel getModel(int row) {
         // fallback in case of more complex layer compositions
         // if there is a ColumnGroupModel requested for a row that is greater
-        // than the
-        // registered models, always use the bottom most ColumnGroupModel
-        // this is the same behaviour as it was before the modifications to
-        // support
-        // expand/collapse for two level column groups
+        // than the registered models, always use the bottom most
+        // ColumnGroupModel this is the same behaviour as it was before the
+        // modifications to support expand/collapse for two level column groups
         if (row >= this.models.length) {
             row = this.models.length - 1;
         }
@@ -63,25 +61,24 @@
     @Override
     public boolean isColumnIndexHidden(int columnIndex) {
 
-        IUniqueIndexLayer underlyingLayer = (IUniqueIndexLayer) getUnderlyingLayer();
+        IUniqueIndexLayer underlyingLayer = getUnderlyingLayer();
 
-        boolean isHiddeninUnderlyingLayer = ColumnGroupUtils
-                .isColumnIndexHiddenInUnderLyingLayer(columnIndex, this,
-                        underlyingLayer);
+        boolean isHiddeninUnderlyingLayer =
+                ColumnGroupUtils.isColumnIndexHiddenInUnderLyingLayer(columnIndex, this, underlyingLayer);
 
-        if (isHiddeninUnderlyingLayer)
+        if (isHiddeninUnderlyingLayer) {
             return true;
+        }
 
         for (ColumnGroupModel model : this.models) {
             ColumnGroup columnGroup = model.getColumnGroupByIndex(columnIndex);
             boolean isCollapsedAndStaticColumn = columnGroup != null
                     && columnGroup.isCollapsed()
-                    && !ColumnGroupUtils.isStaticOrFirstVisibleColumn(
-                            columnIndex, underlyingLayer, underlyingLayer,
-                            model);
+                    && !ColumnGroupUtils.isStaticOrFirstVisibleColumn(columnIndex, underlyingLayer, underlyingLayer, model);
 
-            if (isCollapsedAndStaticColumn)
+            if (isCollapsedAndStaticColumn) {
                 return true;
+            }
         }
 
         return false;
@@ -89,28 +86,40 @@
 
     @Override
     public Collection<Integer> getHiddenColumnIndexes() {
-        Collection<Integer> hiddenColumnIndexes = new HashSet<Integer>();
+        return Arrays.stream(getHiddenColumnIndexesArray()).boxed().collect(Collectors.toList());
+    }
 
-        IUniqueIndexLayer underlyingLayer = (IUniqueIndexLayer) getUnderlyingLayer();
+    @Override
+    public int[] getHiddenColumnIndexesArray() {
+        MutableIntSet hiddenColumnIndexes = IntSets.mutable.empty();
+
+        IUniqueIndexLayer underlyingLayer = getUnderlyingLayer();
         int underlyingColumnCount = underlyingLayer.getColumnCount();
         for (int i = 0; i < underlyingColumnCount; i++) {
             int columnIndex = underlyingLayer.getColumnIndexByPosition(i);
 
             for (ColumnGroupModel model : this.models) {
-                ColumnGroup columnGroup = model
-                        .getColumnGroupByIndex(columnIndex);
+                ColumnGroup columnGroup = model.getColumnGroupByIndex(columnIndex);
 
                 if (columnGroup != null && columnGroup.isCollapsed()) {
-                    if (!ColumnGroupUtils.isStaticOrFirstVisibleColumn(
-                            columnIndex, underlyingLayer, underlyingLayer,
-                            model)) {
-                        hiddenColumnIndexes.add(Integer.valueOf(columnIndex));
+                    if (!ColumnGroupUtils.isStaticOrFirstVisibleColumn(columnIndex, underlyingLayer, underlyingLayer, model)) {
+                        hiddenColumnIndexes.add(columnIndex);
                     }
                 }
             }
         }
 
-        return hiddenColumnIndexes;
+        return hiddenColumnIndexes.toSortedArray();
+    }
+
+    @Override
+    public boolean hasHiddenColumns() {
+        for (ColumnGroupModel model : this.models) {
+            if (model.getCollapsedColumnCount() > 0) {
+                return true;
+            }
+        }
+        return false;
     }
 
     // IColumnGroupModelListener
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/ColumnGroupUtils.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/ColumnGroupUtils.java
index 83dd92e..2f3486c 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/ColumnGroupUtils.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/ColumnGroupUtils.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2019 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -12,10 +12,11 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 
+import org.eclipse.collections.api.list.primitive.MutableIntList;
+import org.eclipse.collections.impl.factory.primitive.IntLists;
 import org.eclipse.nebula.widgets.nattable.coordinate.PositionUtil;
 import org.eclipse.nebula.widgets.nattable.group.ColumnGroupModel.ColumnGroup;
 import org.eclipse.nebula.widgets.nattable.group.performance.ColumnGroupHeaderLayer;
@@ -25,7 +26,6 @@
 import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer;
 import org.eclipse.nebula.widgets.nattable.layer.LayerUtil;
 import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer.MoveDirectionEnum;
-import org.eclipse.nebula.widgets.nattable.util.ArrayUtil;
 
 public class ColumnGroupUtils {
 
@@ -506,15 +506,14 @@
      * @since 1.6
      */
     public static boolean isGroupReordered(Group fromGroup, int[] fromColumnPositions) {
-        Collection<Integer> visiblePositions = fromGroup.getVisiblePositions();
-        if (visiblePositions.size() > fromColumnPositions.length) {
+        int[] visiblePositions = fromGroup.getVisiblePositions();
+        if (visiblePositions.length > fromColumnPositions.length) {
             return false;
-        } else if (visiblePositions.size() < fromColumnPositions.length) {
-            List<Integer> fromPositions = ArrayUtil.asIntegerList(fromColumnPositions);
+        } else if (visiblePositions.length < fromColumnPositions.length) {
+            MutableIntList fromPositions = IntLists.mutable.of(fromColumnPositions);
             return fromPositions.containsAll(visiblePositions);
         } else {
-            int[] positionsArray = ArrayUtil.asIntArray(visiblePositions);
-            return Arrays.equals(positionsArray, fromColumnPositions);
+            return Arrays.equals(visiblePositions, fromColumnPositions);
         }
     }
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/RowGroupExpandCollapseLayer.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/RowGroupExpandCollapseLayer.java
index d920c18..ffb5142 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/RowGroupExpandCollapseLayer.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/RowGroupExpandCollapseLayer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -10,9 +10,12 @@
  ******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.group;
 
+import java.util.Arrays;
 import java.util.Collection;
-import java.util.HashSet;
+import java.util.stream.Collectors;
 
+import org.eclipse.collections.api.set.primitive.MutableIntSet;
+import org.eclipse.collections.impl.factory.primitive.IntSets;
 import org.eclipse.nebula.widgets.nattable.group.command.RowGroupExpandCollapseCommandHandler;
 import org.eclipse.nebula.widgets.nattable.group.model.IRowGroup;
 import org.eclipse.nebula.widgets.nattable.group.model.IRowGroupModel;
@@ -20,13 +23,11 @@
 import org.eclipse.nebula.widgets.nattable.hideshow.AbstractRowHideShowLayer;
 import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer;
 
-public class RowGroupExpandCollapseLayer<T> extends AbstractRowHideShowLayer
-        implements IRowGroupModelListener {
+public class RowGroupExpandCollapseLayer<T> extends AbstractRowHideShowLayer implements IRowGroupModelListener {
 
     private final IRowGroupModel<T> model;
 
-    public RowGroupExpandCollapseLayer(IUniqueIndexLayer underlyingLayer,
-            IRowGroupModel<T> model) {
+    public RowGroupExpandCollapseLayer(IUniqueIndexLayer underlyingLayer, IRowGroupModel<T> model) {
         super(underlyingLayer);
         this.model = model;
 
@@ -49,11 +50,10 @@
             return true;
         }
 
-        IUniqueIndexLayer underlyingLayer = (IUniqueIndexLayer) getUnderlyingLayer();
+        IUniqueIndexLayer underlyingLayer = getUnderlyingLayer();
 
-        boolean isHiddeninUnderlyingLayer = RowGroupUtils
-                .isRowIndexHiddenInUnderLyingLayer(rowIndex, this,
-                        underlyingLayer);
+        boolean isHiddeninUnderlyingLayer =
+                RowGroupUtils.isRowIndexHiddenInUnderLyingLayer(rowIndex, this, underlyingLayer);
 
         // Get the row and the group from our cache and model.
         final T row = this.model.getRowFromIndexCache(rowIndex);
@@ -63,26 +63,41 @@
             return false;
         }
 
-        boolean isCollapsedAndNotStaticRow = RowGroupUtils.isCollapsed(this.model,
-                rowGroup) && !rowGroup.getOwnStaticMemberRows().contains(row);
+        boolean isCollapsedAndNotStaticRow = RowGroupUtils.isCollapsed(this.model, rowGroup)
+                && !rowGroup.getOwnStaticMemberRows().contains(row);
 
         return isHiddeninUnderlyingLayer || isCollapsedAndNotStaticRow;
     }
 
     @Override
     public Collection<Integer> getHiddenRowIndexes() {
-        Collection<Integer> hiddenRowIndexes = new HashSet<Integer>();
+        return Arrays.stream(getHiddenRowIndexesArray()).boxed().collect(Collectors.toList());
+    }
 
-        IUniqueIndexLayer underlyingLayer = (IUniqueIndexLayer) getUnderlyingLayer();
+    @Override
+    public int[] getHiddenRowIndexesArray() {
+        MutableIntSet hiddenRowIndexes = IntSets.mutable.empty();
+
+        IUniqueIndexLayer underlyingLayer = getUnderlyingLayer();
         int underlyingColumnCount = underlyingLayer.getRowCount();
         for (int i = 0; i < underlyingColumnCount; i++) {
             int rowIndex = underlyingLayer.getRowIndexByPosition(i);
             if (isRowIndexHidden(rowIndex)) {
-                hiddenRowIndexes.add(Integer.valueOf(rowIndex));
+                hiddenRowIndexes.add(rowIndex);
             }
         }
 
-        return hiddenRowIndexes;
+        return hiddenRowIndexes.toSortedArray();
+    }
+
+    @Override
+    public boolean hasHiddenRows() {
+        for (IRowGroup<T> rowGroup : this.model.getRowGroups()) {
+            if (rowGroup.isCollapsed()) {
+                return true;
+            }
+        }
+        return false;
     }
 
     @Override
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/RowGroupHeaderLayer.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/RowGroupHeaderLayer.java
index 29fe578..853ed57 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/RowGroupHeaderLayer.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/RowGroupHeaderLayer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2018 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -255,8 +255,7 @@
         int rowIndex = getRowIndexByPosition(rowPosition);
 
         // Get the row and the group from our cache and model.
-        final IRowGroup<T> rowGroup =
-                RowGroupUtils.getRowGroupForRowIndex(this.model, rowIndex);
+        IRowGroup<T> rowGroup = RowGroupUtils.getRowGroupForRowIndex(this.model, rowIndex);
 
         int sizeOfGroup = RowGroupUtils.sizeOfGroup(this.model, rowIndex);
 
@@ -271,13 +270,19 @@
 
         int startPositionOfGroup = getStartPositionOfGroup(rowPosition);
         int endPositionOfGroup = startPositionOfGroup + sizeOfGroup;
-        List<Integer> rowIndexesInGroup =
-                RowGroupUtils.getRowIndexesInGroup(this.model, rowIndex);
+        int[] rowIndexesInGroup = RowGroupUtils.getRowIndexesInGroupAsArray(this.model, rowIndex);
 
         for (int i = startPositionOfGroup; i < endPositionOfGroup; i++) {
             int index = getRowIndexByPosition(i);
-            if (!rowIndexesInGroup.contains(Integer.valueOf(index))) {
-                sizeOfGroup--;
+            for (int j = 0; j < rowIndexesInGroup.length; j++) {
+                boolean contained = false;
+                if (rowIndexesInGroup[j] == index) {
+                    contained = true;
+                    break;
+                }
+                if (contained) {
+                    sizeOfGroup--;
+                }
             }
         }
 
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/RowGroupUtils.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/RowGroupUtils.java
index 560805c..095f72f 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/RowGroupUtils.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/RowGroupUtils.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2019 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -10,12 +10,14 @@
  ******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.group;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.stream.Collectors;
 
+import org.eclipse.collections.api.list.primitive.MutableIntList;
+import org.eclipse.collections.impl.factory.primitive.IntLists;
 import org.eclipse.nebula.widgets.nattable.coordinate.PositionUtil;
 import org.eclipse.nebula.widgets.nattable.group.model.IRowGroup;
 import org.eclipse.nebula.widgets.nattable.group.model.IRowGroupModel;
@@ -26,7 +28,6 @@
 import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer;
 import org.eclipse.nebula.widgets.nattable.layer.LayerUtil;
 import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer.MoveDirectionEnum;
-import org.eclipse.nebula.widgets.nattable.util.ArrayUtil;
 
 /**
  * The utility methods in this class bridge the divide between the world of row
@@ -180,8 +181,7 @@
         return false;
     }
 
-    public static boolean isRowIndexHiddenInUnderLyingLayer(
-            final int rowIndex, final ILayer layer, final IUniqueIndexLayer underlyingLayer) {
+    public static boolean isRowIndexHiddenInUnderLyingLayer(int rowIndex, ILayer layer, IUniqueIndexLayer underlyingLayer) {
         return underlyingLayer.getRowPositionByIndex(rowIndex) == -1;
     }
 
@@ -198,17 +198,12 @@
      *            The row indexes for which the positions are requested.
      * @return Unmodifiable list of the row positions for the given layer
      */
-    public static List<Integer> getRowPositionsInGroup(
-            final IUniqueIndexLayer layer, final Collection<Integer> bodyRowIndexes) {
-
-        final List<Integer> rowPositions = new ArrayList<Integer>();
-        for (Integer bodyRowIndex : bodyRowIndexes) {
-            final int rowPosition = layer.getRowPositionByIndex(bodyRowIndex);
-            if (rowPosition != -1) {
-                rowPositions.add(rowPosition);
-            }
-        }
-        return Collections.unmodifiableList(rowPositions);
+    public static List<Integer> getRowPositionsInGroup(IUniqueIndexLayer layer, Collection<Integer> bodyRowIndexes) {
+        return Collections.unmodifiableList(bodyRowIndexes.stream()
+                .map(layer::getRowPositionByIndex)
+                .filter(pos -> pos != -1)
+                .sorted()
+                .collect(Collectors.toList()));
     }
 
     /**
@@ -220,20 +215,89 @@
      * @return Unmodifiable list of row indexes and static row indexes in the
      *         same group as this index
      */
-    public static <T> List<Integer> getRowIndexesInGroup(final IRowGroupModel<T> model, final int rowIndex) {
+    public static <T> List<Integer> getRowIndexesInGroup(IRowGroupModel<T> model, int rowIndex) {
         final IRowGroup<T> group = getRowGroupForRowIndex(model, rowIndex);
         return getRowIndexesInGroup(model, group, true);
     }
 
-    public static <T> List<Integer> getRowIndexesInGroup(
-            final IRowGroupModel<T> model, final IRowGroup<T> group, final boolean includeStatic) {
-        List<Integer> indexes = new ArrayList<Integer>();
+    /**
+     * Return the row indexes of the rows that belong to a group.
+     *
+     * @param <T>
+     *            The type of the objects in the backing data.
+     * @param model
+     *            The {@link IRowGroupModel} to check.
+     * @param group
+     *            The {@link IRowGroup} to check.
+     * @param includeStatic
+     *            <code>true</code> if static rows should be included,
+     *            <code>false</code> if not.
+     * @return The row indexes of the rows that belong to the given group.
+     */
+    public static <T> List<Integer> getRowIndexesInGroup(IRowGroupModel<T> model, IRowGroup<T> group, boolean includeStatic) {
+        return Collections.unmodifiableList(group.getMemberRows(includeStatic).stream()
+                .map(model::getIndexFromRowCache)
+                .sorted()
+                .collect(Collectors.toList()));
+    }
 
-        for (T row : group.getMemberRows(includeStatic)) {
-            indexes.add(model.getIndexFromRowCache(row));
-        }
+    /**
+     * Helper method to get the row positions for a specified layer
+     *
+     * If a row is currently invisible (-1) it will not be returned within the
+     * collection
+     *
+     * @param layer
+     *            The layer for which the position transformation should be
+     *            performed.
+     * @param bodyRowIndexes
+     *            The row indexes for which the positions are requested.
+     * @return The row positions for the given layer.
+     * @since 2.0
+     */
+    public static int[] getRowPositionsInGroup(IUniqueIndexLayer layer, int... bodyRowIndexes) {
+        return Arrays.stream(bodyRowIndexes)
+                .map(layer::getRowPositionByIndex)
+                .filter(pos -> pos != -1)
+                .sorted()
+                .toArray();
+    }
 
-        return indexes;
+    /**
+     *
+     * @param model
+     *            The {@link IRowGroupModel} to check.
+     * @param rowIndex
+     *            The index of a row whose row group should be inspected.
+     * @return The row indexes and static row indexes in the same group as this
+     *         index.
+     * @since 2.0
+     */
+    public static <T> int[] getRowIndexesInGroupAsArray(IRowGroupModel<T> model, int rowIndex) {
+        final IRowGroup<T> group = getRowGroupForRowIndex(model, rowIndex);
+        return getRowIndexesInGroupAsArray(model, group, true);
+    }
+
+    /**
+     * Return the row indexes of the rows that belong to a group.
+     *
+     * @param <T>
+     *            The type of the objects in the backing data.
+     * @param model
+     *            The {@link IRowGroupModel} to check.
+     * @param group
+     *            The {@link IRowGroup} to check.
+     * @param includeStatic
+     *            <code>true</code> if static rows should be included,
+     *            <code>false</code> if not.
+     * @return The row indexes of the rows that belong to the given group.
+     * @since 2.0
+     */
+    public static <T> int[] getRowIndexesInGroupAsArray(IRowGroupModel<T> model, IRowGroup<T> group, boolean includeStatic) {
+        return group.getMemberRows(includeStatic).stream()
+                .mapToInt(model::getIndexFromRowCache)
+                .sorted()
+                .toArray();
     }
 
     public static <T> String getRowGroupNameForIndex(IRowGroupModel<T> model, int bodyRowIndex) {
@@ -528,15 +592,14 @@
      * @since 1.6
      */
     public static boolean isGroupReordered(Group fromGroup, int[] fromPositions) {
-        Collection<Integer> visiblePositions = fromGroup.getVisiblePositions();
-        if (visiblePositions.size() > fromPositions.length) {
+        int[] visiblePositions = fromGroup.getVisiblePositions();
+        if (visiblePositions.length > fromPositions.length) {
             return false;
-        } else if (visiblePositions.size() < fromPositions.length) {
-            List<Integer> from = ArrayUtil.asIntegerList(fromPositions);
+        } else if (visiblePositions.length < fromPositions.length) {
+            MutableIntList from = IntLists.mutable.of(fromPositions);
             return from.containsAll(visiblePositions);
         } else {
-            int[] positionsArray = ArrayUtil.asIntArray(visiblePositions);
-            return Arrays.equals(positionsArray, fromPositions);
+            return Arrays.equals(visiblePositions, fromPositions);
         }
     }
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/command/ColumnGroupExpandCollapseCommandHandler.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/command/ColumnGroupExpandCollapseCommandHandler.java
index 787dc6b..2921a29 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/command/ColumnGroupExpandCollapseCommandHandler.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/command/ColumnGroupExpandCollapseCommandHandler.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2013 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -12,7 +12,6 @@
 
 import java.util.ArrayList;
 import java.util.Iterator;
-import java.util.List;
 
 import org.eclipse.nebula.widgets.nattable.command.AbstractLayerCommandHandler;
 import org.eclipse.nebula.widgets.nattable.group.ColumnGroupExpandCollapseLayer;
@@ -22,13 +21,11 @@
 import org.eclipse.nebula.widgets.nattable.hideshow.event.ShowColumnPositionsEvent;
 import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent;
 
-public class ColumnGroupExpandCollapseCommandHandler extends
-        AbstractLayerCommandHandler<ColumnGroupExpandCollapseCommand> {
+public class ColumnGroupExpandCollapseCommandHandler extends AbstractLayerCommandHandler<ColumnGroupExpandCollapseCommand> {
 
     private final ColumnGroupExpandCollapseLayer columnGroupExpandCollapseLayer;
 
-    public ColumnGroupExpandCollapseCommandHandler(
-            ColumnGroupExpandCollapseLayer columnGroupExpandCollapseLayer) {
+    public ColumnGroupExpandCollapseCommandHandler(ColumnGroupExpandCollapseLayer columnGroupExpandCollapseLayer) {
         this.columnGroupExpandCollapseLayer = columnGroupExpandCollapseLayer;
     }
 
@@ -40,10 +37,8 @@
     @Override
     protected boolean doCommand(ColumnGroupExpandCollapseCommand command) {
 
-        int columnIndex = this.columnGroupExpandCollapseLayer
-                .getColumnIndexByPosition(command.getColumnPosition());
-        ColumnGroupModel model = this.columnGroupExpandCollapseLayer
-                .getModel(command.getRowPosition());
+        int columnIndex = this.columnGroupExpandCollapseLayer.getColumnIndexByPosition(command.getColumnPosition());
+        ColumnGroupModel model = this.columnGroupExpandCollapseLayer.getModel(command.getRowPosition());
         ColumnGroup columnGroup = model.getColumnGroupByIndex(columnIndex);
 
         // if group of columnIndex is not collapseable return without any
@@ -52,8 +47,7 @@
             return true;
         }
 
-        List<Integer> columnIndexes = new ArrayList<Integer>(
-                columnGroup.getMembers());
+        ArrayList<Integer> columnIndexes = new ArrayList<>(columnGroup.getMembers());
         columnIndexes.removeAll(columnGroup.getStaticColumnIndexes());
 
         boolean wasCollapsed = columnGroup.isCollapsed();
@@ -76,10 +70,12 @@
         ILayerEvent event;
         if (wasCollapsed) {
             event = new ShowColumnPositionsEvent(
-                    this.columnGroupExpandCollapseLayer, columnIndexes);
+                    this.columnGroupExpandCollapseLayer,
+                    columnIndexes.stream().mapToInt(Integer::intValue).toArray());
         } else {
             event = new HideColumnPositionsEvent(
-                    this.columnGroupExpandCollapseLayer, columnIndexes);
+                    this.columnGroupExpandCollapseLayer,
+                    columnIndexes.stream().mapToInt(Integer::intValue).toArray());
         }
 
         this.columnGroupExpandCollapseLayer.fireLayerEvent(event);
@@ -95,12 +91,11 @@
      * @param columnIndexes
      *            The column indexes to cleanup.
      */
-    private void cleanupColumnIndexes(List<Integer> columnIndexes) {
+    private void cleanupColumnIndexes(ArrayList<Integer> columnIndexes) {
         for (Iterator<Integer> it = columnIndexes.iterator(); it.hasNext();) {
             Integer columnIndex = it.next();
 
-            if (!this.columnGroupExpandCollapseLayer
-                    .isColumnIndexHidden(columnIndex)) {
+            if (!this.columnGroupExpandCollapseLayer.isColumnIndexHidden(columnIndex)) {
                 it.remove();
             }
         }
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/command/RowGroupExpandCollapseCommandHandler.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/command/RowGroupExpandCollapseCommandHandler.java
index 1ac4bf6..902e31d 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/command/RowGroupExpandCollapseCommandHandler.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/command/RowGroupExpandCollapseCommandHandler.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -10,9 +10,6 @@
  ******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.group.command;
 
-import java.util.ArrayList;
-import java.util.List;
-
 import org.eclipse.nebula.widgets.nattable.command.AbstractLayerCommandHandler;
 import org.eclipse.nebula.widgets.nattable.group.RowGroupExpandCollapseLayer;
 import org.eclipse.nebula.widgets.nattable.group.RowGroupUtils;
@@ -22,13 +19,11 @@
 import org.eclipse.nebula.widgets.nattable.hideshow.event.ShowRowPositionsEvent;
 import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent;
 
-public class RowGroupExpandCollapseCommandHandler<T> extends
-        AbstractLayerCommandHandler<RowGroupExpandCollapseCommand> {
+public class RowGroupExpandCollapseCommandHandler<T> extends AbstractLayerCommandHandler<RowGroupExpandCollapseCommand> {
 
     private final RowGroupExpandCollapseLayer<T> rowGroupExpandCollapseLayer;
 
-    public RowGroupExpandCollapseCommandHandler(
-            RowGroupExpandCollapseLayer<T> rowGroupExpandCollapseLayer) {
+    public RowGroupExpandCollapseCommandHandler(RowGroupExpandCollapseLayer<T> rowGroupExpandCollapseLayer) {
         this.rowGroupExpandCollapseLayer = rowGroupExpandCollapseLayer;
     }
 
@@ -40,11 +35,9 @@
     @Override
     protected boolean doCommand(RowGroupExpandCollapseCommand command) {
 
-        int rowIndex = this.rowGroupExpandCollapseLayer
-                .getRowIndexByPosition(command.getRowPosition());
+        int rowIndex = this.rowGroupExpandCollapseLayer.getRowIndexByPosition(command.getRowPosition());
         IRowGroupModel<T> model = this.rowGroupExpandCollapseLayer.getModel();
-        IRowGroup<T> group = RowGroupUtils.getTopMostParentGroup(RowGroupUtils
-                .getRowGroupForRowIndex(model, rowIndex));
+        IRowGroup<T> group = RowGroupUtils.getTopMostParentGroup(RowGroupUtils.getRowGroupForRowIndex(model, rowIndex));
 
         // if group of rowIndex is not collapseable return without any
         // further operation ...
@@ -60,18 +53,14 @@
             group.collapse();
         }
 
-        List<Integer> rowIndexes = new ArrayList<Integer>(
-                RowGroupUtils.getRowIndexesInGroup(model, rowIndex));
-        List<Integer> rowPositions = RowGroupUtils.getRowPositionsInGroup(
-                this.rowGroupExpandCollapseLayer, rowIndexes);
+        int[] rowIndexes = RowGroupUtils.getRowIndexesInGroupAsArray(model, rowIndex);
+        int[] rowPositions = RowGroupUtils.getRowPositionsInGroup(this.rowGroupExpandCollapseLayer, rowIndexes);
 
         ILayerEvent event;
         if (wasCollapsed) {
-            event = new ShowRowPositionsEvent(this.rowGroupExpandCollapseLayer,
-                    rowPositions);
+            event = new ShowRowPositionsEvent(this.rowGroupExpandCollapseLayer, rowPositions);
         } else {
-            event = new HideRowPositionsEvent(this.rowGroupExpandCollapseLayer,
-                    rowPositions);
+            event = new HideRowPositionsEvent(this.rowGroupExpandCollapseLayer, rowPositions);
         }
 
         this.rowGroupExpandCollapseLayer.fireLayerEvent(event);
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/ColumnGroupExpandCollapseLayer.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/ColumnGroupExpandCollapseLayer.java
index 713f4fa..5308d99 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/ColumnGroupExpandCollapseLayer.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/ColumnGroupExpandCollapseLayer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2019 Dirk Fauth.
+ * Copyright (c) 2019, 2020 Dirk Fauth.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -12,13 +12,15 @@
 
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
+import java.util.stream.Collectors;
 
+import org.eclipse.collections.api.factory.Maps;
+import org.eclipse.collections.api.list.primitive.MutableIntList;
+import org.eclipse.collections.api.map.MutableMap;
+import org.eclipse.collections.api.set.primitive.MutableIntSet;
+import org.eclipse.collections.impl.factory.primitive.IntLists;
+import org.eclipse.collections.impl.factory.primitive.IntSets;
 import org.eclipse.nebula.widgets.nattable.command.ILayerCommand;
 import org.eclipse.nebula.widgets.nattable.group.performance.GroupModel.Group;
 import org.eclipse.nebula.widgets.nattable.group.performance.command.ColumnGroupCollapseCommand;
@@ -38,7 +40,7 @@
  */
 public class ColumnGroupExpandCollapseLayer extends AbstractColumnHideShowLayer {
 
-    private final Map<Group, Collection<Integer>> hidden = new HashMap<Group, Collection<Integer>>();
+    private final MutableMap<Group, MutableIntSet> hidden = Maps.mutable.empty();
 
     public ColumnGroupExpandCollapseLayer(IUniqueIndexLayer underlyingLayer) {
         super(underlyingLayer);
@@ -49,7 +51,7 @@
         if (command instanceof ColumnGroupExpandCommand) {
             List<Group> groups = ((ColumnGroupExpandCommand) command).getGroups();
 
-            Set<Integer> shownIndexes = new TreeSet<Integer>();
+            MutableIntSet shownIndexes = IntSets.mutable.empty();
 
             for (Group group : groups) {
                 // if group is not collapseable return without any further
@@ -60,7 +62,7 @@
 
                 if (group.isCollapsed()) {
                     group.setCollapsed(false);
-                    Collection<Integer> columnIndexes = this.hidden.get(group);
+                    MutableIntSet columnIndexes = this.hidden.get(group);
                     this.hidden.remove(group);
                     shownIndexes.addAll(columnIndexes);
                 }
@@ -68,7 +70,7 @@
 
             if (!shownIndexes.isEmpty()) {
                 invalidateCache();
-                fireLayerEvent(new ShowColumnPositionsEvent(this, getColumnPositionsByIndexes(shownIndexes)));
+                fireLayerEvent(new ShowColumnPositionsEvent(this, getColumnPositionsByIndexes(shownIndexes.toArray())));
             } else {
                 fireLayerEvent(new VisualRefreshEvent(this));
             }
@@ -77,16 +79,12 @@
         } else if (command instanceof ColumnGroupCollapseCommand) {
             GroupModel groupModel = ((ColumnGroupCollapseCommand) command).getGroupModel();
             List<Group> groups = ((ColumnGroupCollapseCommand) command).getGroups();
-            Collections.sort(groups, new Comparator<Group>() {
-
-                @Override
-                public int compare(Group o1, Group o2) {
-                    return o2.getVisibleStartPosition() - o1.getVisibleStartPosition();
-                }
+            Collections.sort(groups, (Group o1, Group o2) -> {
+                return o2.getVisibleStartPosition() - o1.getVisibleStartPosition();
             });
 
-            Set<Integer> hiddenPositions = new TreeSet<Integer>();
-            Set<Integer> hiddenIndexes = new TreeSet<Integer>();
+            MutableIntSet hiddenPositions = IntSets.mutable.empty();
+            MutableIntSet hiddenIndexes = IntSets.mutable.empty();
 
             for (Group group : groups) {
                 // if group is not collapseable return without any further
@@ -95,7 +93,7 @@
                     continue;
                 }
 
-                Set<Integer> columnIndexes = new TreeSet<Integer>();
+                MutableIntSet columnIndexes = IntSets.mutable.empty();
                 if (!group.isCollapsed()) {
                     columnIndexes.addAll(group.getVisibleIndexes());
                     group.setCollapsed(true);
@@ -111,13 +109,13 @@
                 modifyForVisible(group, columnIndexes);
                 this.hidden.put(group, columnIndexes);
 
-                hiddenPositions.addAll(getColumnPositionsByIndexes(columnIndexes));
+                hiddenPositions.addAll(getColumnPositionsByIndexes(columnIndexes.toArray()));
                 hiddenIndexes.addAll(columnIndexes);
             }
 
             if (!hiddenPositions.isEmpty()) {
                 invalidateCache();
-                fireLayerEvent(new HideColumnPositionsEvent(this, hiddenPositions, hiddenIndexes));
+                fireLayerEvent(new HideColumnPositionsEvent(this, hiddenPositions.toArray(), hiddenIndexes.toArray()));
             } else {
                 fireLayerEvent(new VisualRefreshEvent(this));
             }
@@ -126,10 +124,10 @@
         } else if (command instanceof UpdateColumnGroupCollapseCommand) {
             UpdateColumnGroupCollapseCommand cmd = (UpdateColumnGroupCollapseCommand) command;
             Group group = cmd.getGroup();
-            Collection<Integer> hiddenColumnIndexes = this.hidden.get(group);
-            if (group.getVisibleIndexes().size() + hiddenColumnIndexes.size() <= group.getOriginalSpan()) {
-                Collection<Integer> indexesToHide = cmd.getIndexesToHide();
-                Collection<Integer> indexesToShow = cmd.getIndexesToShow();
+            MutableIntSet hiddenColumnIndexes = this.hidden.get(group);
+            if (group.getVisibleIndexes().length + hiddenColumnIndexes.size() <= group.getOriginalSpan()) {
+                MutableIntSet indexesToHide = IntSets.mutable.of(cmd.getIndexesToHide());
+                MutableIntSet indexesToShow = IntSets.mutable.of(cmd.getIndexesToShow());
 
                 // remove already hidden indexes
                 indexesToHide.removeAll(hiddenColumnIndexes);
@@ -137,14 +135,14 @@
                 // remove static indexes
                 modifyForVisible(group, indexesToHide);
 
-                Collection<Integer> hiddenPositions = getColumnPositionsByIndexes(indexesToHide);
+                int[] hiddenPositions = getColumnPositionsByIndexes(indexesToHide.toArray());
 
                 hiddenColumnIndexes.addAll(indexesToHide);
                 hiddenColumnIndexes.removeAll(indexesToShow);
 
                 invalidateCache();
 
-                fireLayerEvent(new HideColumnPositionsEvent(this, hiddenPositions, indexesToHide));
+                fireLayerEvent(new HideColumnPositionsEvent(this, hiddenPositions, indexesToHide.toArray()));
             }
 
             return true;
@@ -162,9 +160,9 @@
      * @param columnIndexes
      *            The collection of indexes that should be hidden.
      */
-    private void modifyForVisible(Group group, Collection<Integer> columnIndexes) {
-        Collection<Integer> staticIndexes = group.getStaticIndexes();
-        if (staticIndexes.isEmpty()) {
+    private void modifyForVisible(Group group, MutableIntSet columnIndexes) {
+        int[] staticIndexes = group.getStaticIndexes();
+        if (staticIndexes.length == 0) {
             // keep the first column
             columnIndexes.remove(group.getVisibleStartIndex());
         } else {
@@ -175,21 +173,31 @@
 
     @Override
     public boolean isColumnIndexHidden(int columnIndex) {
-        for (Collection<Integer> indexes : this.hidden.values()) {
-            if (indexes.contains(Integer.valueOf(columnIndex))) {
-                return true;
-            }
-        }
-        return false;
+        MutableIntSet found = this.hidden.detect(indexes -> indexes.contains(columnIndex));
+        return found != null;
     }
 
     @Override
     public Collection<Integer> getHiddenColumnIndexes() {
-        Set<Integer> hiddenColumnIndexes = new TreeSet<Integer>();
-        for (Collection<Integer> indexes : this.hidden.values()) {
+        MutableIntList hiddenColumnIndexes = IntLists.mutable.empty();
+        for (MutableIntSet indexes : this.hidden.values()) {
             hiddenColumnIndexes.addAll(indexes);
         }
-        return hiddenColumnIndexes;
+        return hiddenColumnIndexes.distinct().toSortedList().primitiveStream().boxed().collect(Collectors.toList());
+    }
+
+    @Override
+    public int[] getHiddenColumnIndexesArray() {
+        MutableIntList hiddenColumnIndexes = IntLists.mutable.empty();
+        for (MutableIntSet indexes : this.hidden.values()) {
+            hiddenColumnIndexes.addAll(indexes);
+        }
+        return hiddenColumnIndexes.distinct().toSortedArray();
+    }
+
+    @Override
+    public boolean hasHiddenColumns() {
+        return !this.hidden.isEmpty();
     }
 
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/ColumnGroupHeaderLayer.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/ColumnGroupHeaderLayer.java
index 7bf3307..3d40334 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/ColumnGroupHeaderLayer.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/ColumnGroupHeaderLayer.java
@@ -11,9 +11,8 @@
 package org.eclipse.nebula.widgets.nattable.group.performance;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -21,6 +20,8 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.eclipse.collections.api.list.primitive.MutableIntList;
+import org.eclipse.collections.impl.factory.primitive.IntLists;
 import org.eclipse.nebula.widgets.nattable.NatTable;
 import org.eclipse.nebula.widgets.nattable.command.ILayerCommand;
 import org.eclipse.nebula.widgets.nattable.coordinate.PositionUtil;
@@ -625,7 +626,7 @@
             return true;
         } else if (command instanceof MultiRowResizeCommand && command.convertToTargetLayer(this)) {
             MultiRowResizeCommand rowResizeCommand = (MultiRowResizeCommand) command;
-            for (int row : rowResizeCommand.getRowPositions()) {
+            for (int row : rowResizeCommand.getRowPositionsArray()) {
                 int newRowHeight = rowResizeCommand.downScaleValue()
                         ? this.rowHeightConfig.downScale(rowResizeCommand.getRowHeight(row))
                         : rowResizeCommand.getRowHeight(row);
@@ -1199,7 +1200,7 @@
         int sizeOfGroup = group.getVisibleSpan();
 
         if (group.isCollapsed()) {
-            int sizeOfStaticColumns = group.getStaticIndexes().size();
+            int sizeOfStaticColumns = group.getStaticIndexes().length;
             if (sizeOfStaticColumns == 0) {
                 return 1;
             } else {
@@ -2260,7 +2261,7 @@
                 GroupMultiColumnReorderCommand command =
                         new GroupMultiColumnReorderCommand(
                                 getPositionLayer(),
-                                new ArrayList<>(group.getMembers()),
+                                group.getMembers(),
                                 underlyingTo);
                 Group toRight = groupModel.getGroupByPosition(toPosition);
                 if (toRight != null) {
@@ -2318,7 +2319,7 @@
                 Collection<StructuralDiff> columnDiffs = changeEvent.getColumnDiffs();
                 if (columnDiffs != null && !columnDiffs.isEmpty()) {
 
-                    List<Integer> deletedPositions = getDeletedPositions(columnDiffs);
+                    int[] deletedPositions = getDeletedPositions(columnDiffs);
                     if (deletedPositions != null) {
                         // check if the number of positions are the same as the
                         // number of indexes, otherwise trigger a consistency
@@ -2326,9 +2327,9 @@
                         // always in a valid range, and that could cause a loss
                         // of hidden positions on conversion
                         if (event instanceof ColumnStructuralChangeEvent
-                                && ((ColumnStructuralChangeEvent) event).getColumnIndexes().size() > deletedPositions.size()) {
+                                && ((ColumnStructuralChangeEvent) event).getColumnIndexes().length > deletedPositions.length) {
                             // this triggers a consistency check
-                            handleDeleteDiffs(new ArrayList<Integer>());
+                            handleDeleteDiffs(new int[0]);
                         } else {
                             handleDeleteDiffs(deletedPositions);
                         }
@@ -2356,9 +2357,9 @@
                                                 cmd = new UpdateColumnGroupCollapseCommand(groupModel, group);
                                                 collapseUpdates.put(group, cmd);
                                             }
-                                            if (!group.getStaticIndexes().contains(Integer.valueOf(newStartIndex))) {
-                                                cmd.getIndexesToShow().add(newStartIndex);
-                                                cmd.getIndexesToHide().add(getPositionLayer().getColumnIndexByPosition(i) + 1);
+                                            if (!group.containsStaticIndex(newStartIndex)) {
+                                                cmd.addIndexesToShow(newStartIndex);
+                                                cmd.addIndexesToHide(getPositionLayer().getColumnIndexByPosition(i) + 1);
                                             }
                                             if (group.getVisibleSpan() == 0) {
                                                 group.setVisibleSpan(1);
@@ -2373,18 +2374,18 @@
                                         Group leftGroup = groupModel.getGroupByPosition(i - 1);
                                         if (leftGroup != null
                                                 && leftGroup.getVisibleSpan() < leftGroup.getOriginalSpan()
-                                                && leftGroup.getMembers().contains(Integer.valueOf(newStartIndex))) {
+                                                && leftGroup.hasMember(newStartIndex)) {
                                             if (!leftGroup.isCollapsed()) {
                                                 leftGroup.setVisibleSpan(leftGroup.getVisibleSpan() + 1);
                                             } else {
-                                                if (!leftGroup.getStaticIndexes().contains(Integer.valueOf(newStartIndex))) {
+                                                if (!leftGroup.containsStaticIndex(newStartIndex)) {
                                                     // update collapsed state
                                                     UpdateColumnGroupCollapseCommand cmd = collapseUpdates.get(leftGroup);
                                                     if (cmd == null) {
                                                         cmd = new UpdateColumnGroupCollapseCommand(groupModel, leftGroup);
                                                         collapseUpdates.put(leftGroup, cmd);
                                                     }
-                                                    cmd.getIndexesToHide().add(getPositionLayer().getColumnIndexByPosition(i));
+                                                    cmd.addIndexesToHide(getPositionLayer().getColumnIndexByPosition(i));
                                                 } else {
                                                     leftGroup.setVisibleSpan(leftGroup.getVisibleSpan() + 1);
                                                 }
@@ -2405,7 +2406,7 @@
                                                 // member
                                                 for (int e = diff.getAfterPositionRange().end; e > diff.getAfterPositionRange().start; e--) {
                                                     g = groupModel.getGroupByPosition(e);
-                                                    if (g != null && g.getMembers().contains(newStartIndex)) {
+                                                    if (g != null && g.hasMember(newStartIndex)) {
                                                         g.setStartIndex(newStartIndex);
                                                         g.setVisibleStartIndex(newStartIndex);
                                                         g.setVisibleSpan(g.getVisibleSpan() + 1);
@@ -2438,35 +2439,33 @@
             }
         }
 
-        private List<Integer> getDeletedPositions(Collection<StructuralDiff> columnDiffs) {
-            List<Integer> result = new ArrayList<Integer>();
+        private int[] getDeletedPositions(Collection<StructuralDiff> columnDiffs) {
+            MutableIntList result = IntLists.mutable.empty();
             boolean deleteDiffFound = false;
             for (StructuralDiff diff : columnDiffs) {
                 if (diff.getDiffType() == DiffTypeEnum.DELETE) {
                     deleteDiffFound = true;
                     int[] positions = PositionUtil.getPositions(diff.getBeforePositionRange());
-                    result.addAll(ArrayUtil.asIntegerList(positions));
+                    result.addAll(positions);
                 }
             }
             if (deleteDiffFound) {
                 // we need to handle the deleted positions backwards
-                Collections.sort(result, new Comparator<Integer>() {
-                    @Override
-                    public int compare(Integer o1, Integer o2) {
-                        return o2.compareTo(o1);
-                    }
-                });
-                return result;
+                return result.sortThis().reverseThis().toArray();
             } else {
                 // there was no DELETE diff, so we return null
                 return null;
             }
         }
 
-        private void handleDeleteDiffs(List<Integer> positionList) {
-            if (positionList.size() > 0) {
+        private void handleDeleteDiffs(int[] positionList) {
+            if (positionList.length > 0) {
                 for (GroupModel groupModel : ColumnGroupHeaderLayer.this.model) {
-                    List<Integer> groupPositionList = new ArrayList<Integer>(positionList);
+                    // we need to create the MutableIntList on a copy of the
+                    // positionList array, because modifications on the
+                    // MutableIntList will be traversed to the the underlying
+                    // array
+                    MutableIntList groupPositionList = IntLists.mutable.of(Arrays.copyOf(positionList, positionList.length));
                     while (!groupPositionList.isEmpty()) {
                         // find group and update visible span
                         // we need to iterate because one could hide the
@@ -2477,16 +2476,11 @@
                         if (group != null) {
                             // find all positions belonging to
                             // the group
-                            List<Integer> groupPositions = new ArrayList<Integer>(group.getVisiblePositions());
+                            MutableIntList groupPositions = IntLists.mutable.of(group.getVisiblePositions());
 
                             // find the positions in the group
                             // that are hidden
-                            List<Integer> hiddenGroupPositions = new ArrayList<Integer>();
-                            for (int groupPos : groupPositions) {
-                                if (groupPositionList.contains(groupPos)) {
-                                    hiddenGroupPositions.add(groupPos);
-                                }
-                            }
+                            MutableIntList hiddenGroupPositions = groupPositions.select(groupPos -> groupPositionList.contains(groupPos));
 
                             // update the positionList as we
                             // handled the hidden columns
@@ -2495,11 +2489,11 @@
                             groupPositions.removeAll(hiddenGroupPositions);
                             if (groupPositions.size() > 0) {
                                 group.setVisibleSpan(groupPositions.size());
-                                for (int i : hiddenGroupPositions) {
+                                hiddenGroupPositions.forEach(i -> {
                                     if (group.getVisibleStartPosition() == i) {
                                         group.setVisibleStartIndex(getPositionLayer().getColumnIndexByPosition(i - groupPositionList.size()));
                                     }
-                                }
+                                });
                             } else {
                                 group.setVisibleStartIndex(-1);
                                 group.setVisibleSpan(0);
@@ -2575,7 +2569,7 @@
                     // column at the edge of a group, remove the
                     // position from the group
                     if (fromColumnPosition == toColumnPosition
-                            && (!ColumnGroupUtils.isGroupReordered(fromColumnGroup, fromColumnPositions) || (fromColumnGroup.isCollapsed() && fromColumnGroup.getMembers().size() > 1))
+                            && (!ColumnGroupUtils.isGroupReordered(fromColumnGroup, fromColumnPositions) || (fromColumnGroup.isCollapsed() && fromColumnGroup.getMembers().length > 1))
                             && (fromColumnGroup.isGroupStart(fromColumnPosition)
                                     || fromColumnGroup.isGroupEnd(fromColumnPosition))) {
                         if (MoveDirectionEnum.RIGHT == moveDirection) {
@@ -2589,7 +2583,7 @@
                                     groupModel,
                                     fromColumnGroup,
                                     pos,
-                                    reorderEvent.getBeforeFromColumnIndexes(),
+                                    reorderEvent.getBeforeFromColumnIndexesArray(),
                                     fromColumnPosition,
                                     moveDirection);
                         } else {
@@ -2597,7 +2591,7 @@
                                     groupModel,
                                     fromColumnGroup,
                                     fromColumnPositions,
-                                    reorderEvent.getBeforeFromColumnIndexes(),
+                                    reorderEvent.getBeforeFromColumnIndexesArray(),
                                     fromColumnPosition,
                                     moveDirection);
                         }
@@ -2613,7 +2607,7 @@
                         int newStartIndex = getPositionLayer().getColumnIndexByPosition(fromColumnGroup.getVisibleStartPosition());
                         fromColumnGroup.setVisibleStartIndex(newStartIndex);
                         if (!fromColumnGroup.isCollapsed()
-                                || fromColumnGroup.getStaticIndexes().contains(Integer.valueOf(fromColumnGroup.getStartIndex()))) {
+                                || fromColumnGroup.containsStaticIndex(fromColumnGroup.getStartIndex())) {
                             // if we move inside a collapsed group, there are
                             // static indexes, and therefore we do not update
                             // the start index, as the start index should be the
@@ -2627,7 +2621,7 @@
                             groupModel,
                             toColumnGroup,
                             fromColumnPositions,
-                            reorderEvent.getBeforeFromColumnIndexes(),
+                            reorderEvent.getBeforeFromColumnIndexesArray(),
                             toColumnPosition,
                             moveDirection);
                 } else if (fromColumnGroup != null
@@ -2637,7 +2631,7 @@
                             groupModel,
                             fromColumnGroup,
                             fromColumnPositions,
-                            reorderEvent.getBeforeFromColumnIndexes(),
+                            reorderEvent.getBeforeFromColumnIndexesArray(),
                             fromColumnPosition,
                             moveDirection);
                 } else if (fromColumnGroup == null
@@ -2655,27 +2649,27 @@
                                 groupModel,
                                 adjacentColumnGroup,
                                 fromColumnPositions,
-                                reorderEvent.getBeforeFromColumnIndexes(),
+                                reorderEvent.getBeforeFromColumnIndexesArray(),
                                 (adjacentColumnGroup.isCollapsed() && moveDirection != MoveDirectionEnum.RIGHT) ? toColumnPosition : adjacentPos,
                                 moveDirection);
                     }
                 } else if (fromColumnGroup != null
                         && toColumnGroup != null
                         && !fromColumnGroup.equals(toColumnGroup)
-                        && (!ColumnGroupUtils.isGroupReordered(fromColumnGroup, fromColumnPositions) || (!fromColumnGroup.isCollapsed() && fromColumnGroup.getVisiblePositions().size() == 1))) {
+                        && (!ColumnGroupUtils.isGroupReordered(fromColumnGroup, fromColumnPositions) || (!fromColumnGroup.isCollapsed() && fromColumnGroup.getVisiblePositions().length == 1))) {
 
                     removePositionsFromGroup(
                             groupModel,
                             fromColumnGroup,
                             fromColumnPositions,
-                            reorderEvent.getBeforeFromColumnIndexes(),
+                            reorderEvent.getBeforeFromColumnIndexesArray(),
                             fromColumnPositions[0],
                             moveDirection);
                     addPositionsToGroup(
                             groupModel,
                             toColumnGroup,
                             fromColumnPositions,
-                            reorderEvent.getBeforeFromColumnIndexes(),
+                            reorderEvent.getBeforeFromColumnIndexesArray(),
                             toColumnPosition,
                             moveDirection);
                 }
@@ -2688,7 +2682,7 @@
                 GroupModel groupModel,
                 Group group,
                 int[] fromColumnPositions,
-                Collection<Integer> fromColumnIndexes,
+                int[] fromColumnIndexes,
                 int toPosition,
                 MoveDirectionEnum moveDirection) {
 
@@ -2716,9 +2710,9 @@
                     // update collapsed state
                     UpdateColumnGroupCollapseCommand cmd = new UpdateColumnGroupCollapseCommand(groupModel, group);
                     if (group.isGroupStart(toPosition)) {
-                        cmd.getIndexesToHide().addAll(group.getMembers());
+                        cmd.addIndexesToHide(group.getMembers());
 
-                        if (!group.getStaticIndexes().isEmpty()) {
+                        if (group.getStaticIndexes().length > 0) {
                             group.setVisibleSpan(group.getVisibleSpan() + fromColumnPositions.length);
                         }
 
@@ -2729,7 +2723,7 @@
                         // on the correct positions
                         group.updateVisibleStartPosition();
                     } else {
-                        cmd.getIndexesToHide().addAll(fromColumnIndexes);
+                        cmd.addIndexesToHide(fromColumnIndexes);
                         group.setVisibleSpan(group.getVisibleSpan() + fromColumnPositions.length);
                     }
                     getPositionLayer().doCommand(cmd);
@@ -2745,7 +2739,7 @@
                 GroupModel groupModel,
                 Group group,
                 int[] fromColumnPositions,
-                Collection<Integer> fromColumnIndexes,
+                int[] fromColumnIndexes,
                 int fromColumnPosition,
                 MoveDirectionEnum moveDirection) {
 
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/GroupModel.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/GroupModel.java
index 3099b7b..beae5c2 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/GroupModel.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/GroupModel.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2019 Dirk Fauth.
+ * Copyright (c) 2019, 2020 Dirk Fauth.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -10,7 +10,6 @@
  ******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.group.performance;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -21,9 +20,13 @@
 import java.util.Set;
 import java.util.StringTokenizer;
 
+import org.eclipse.collections.api.list.primitive.MutableIntList;
+import org.eclipse.collections.api.set.primitive.IntSet;
+import org.eclipse.collections.api.set.primitive.MutableIntSet;
+import org.eclipse.collections.impl.factory.primitive.IntLists;
+import org.eclipse.collections.impl.factory.primitive.IntSets;
 import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer;
 import org.eclipse.nebula.widgets.nattable.persistence.IPersistable;
-import org.eclipse.nebula.widgets.nattable.util.ObjectUtils;
 
 /**
  * Model implementation to track groups of columns/rows.
@@ -177,11 +180,7 @@
             strBuilder.append(group.unbreakable ? "unbreakable" : "breakable"); //$NON-NLS-1$ //$NON-NLS-2$
 
             if (!group.staticIndexes.isEmpty()) {
-                strBuilder.append(':');
-                for (Integer member : group.staticIndexes) {
-                    strBuilder.append(member);
-                    strBuilder.append(',');
-                }
+                strBuilder.append(':').append(group.staticIndexes.toSortedList().makeString(IPersistable.VALUE_SEPARATOR));
             }
 
             strBuilder.append('|');
@@ -208,19 +207,19 @@
                 String[] groupProperties = groupToken.substring(separatorIndex + 1).split(":"); //$NON-NLS-1$
 
                 String state = groupProperties[0];
-                int startIndex = Integer.valueOf(state);
+                int startIndex = Integer.parseInt(state);
 
                 state = groupProperties[1];
-                int visibleStartIndex = Integer.valueOf(state);
+                int visibleStartIndex = Integer.parseInt(state);
 
                 state = groupProperties[2];
-                int visibleStartPosition = Integer.valueOf(state);
+                int visibleStartPosition = Integer.parseInt(state);
 
                 state = groupProperties[3];
-                int originalSpan = Integer.valueOf(state);
+                int originalSpan = Integer.parseInt(state);
 
                 state = groupProperties[4];
-                int visibleSpan = Integer.valueOf(state);
+                int visibleSpan = Integer.parseInt(state);
 
                 Group group = new Group(groupName, startIndex, originalSpan);
                 this.groups.add(group);
@@ -263,7 +262,7 @@
                     String statics = groupProperties[8];
                     StringTokenizer staticTokenizer = new StringTokenizer(statics, ","); //$NON-NLS-1$
                     while (staticTokenizer.hasMoreTokens()) {
-                        Integer index = Integer.valueOf(staticTokenizer.nextToken());
+                        int index = Integer.parseInt(staticTokenizer.nextToken());
                         group.staticIndexes.add(index);
                     }
                 }
@@ -285,8 +284,8 @@
             Arrays.sort(positions);
 
             // separate position arrays by start position
-            List<Integer> beforeStartPosition = new ArrayList<Integer>();
-            List<Integer> afterStartPosition = new ArrayList<Integer>();
+            MutableIntList beforeStartPosition = IntLists.mutable.empty();
+            MutableIntList afterStartPosition = IntLists.mutable.empty();
             for (int pos : positions) {
                 if (pos < group.getVisibleStartPosition()) {
                     beforeStartPosition.add(pos);
@@ -320,7 +319,7 @@
             }
 
             // iterate forward after group end
-            for (int pos : afterStartPosition) {
+            for (int pos : afterStartPosition.toArray()) {
                 int nextPos = group.getVisibleStartPosition() + group.getVisibleSpan();
                 // check for gap
                 if (pos == nextPos) {
@@ -359,18 +358,19 @@
     public void removePositionsFromGroup(Group group, int... positions) {
         if (group != null && !group.isUnbreakable()) {
             Arrays.sort(positions);
+            IntSet visiblePositions = IntSets.immutable.of(group.getVisiblePositions());
             for (int i = positions.length - 1; i >= 0; i--) {
                 int pos = positions[i];
-                if (group.getVisiblePositions().contains(Integer.valueOf(pos))) {
+                if (visiblePositions.contains(pos)) {
                     int index = getIndexByPosition(pos);
                     if (index == group.getStartIndex()) {
                         // the start index was removed, we need to update the
                         // start index
                         group.setStartIndex(getIndexByPosition(pos + 1));
-                        group.members.remove(Integer.valueOf(index));
-                        group.staticIndexes.remove(Integer.valueOf(index));
+                        group.members.remove(index);
+                        group.staticIndexes.remove(index);
                     } else {
-                        Integer memberIndex = getIndexByPosition(Integer.valueOf(group.getVisibleStartPosition() + group.getVisibleSpan() - 1));
+                        int memberIndex = getIndexByPosition(group.getVisibleStartPosition() + group.getVisibleSpan() - 1);
                         group.members.remove(memberIndex);
                         group.staticIndexes.remove(memberIndex);
                     }
@@ -429,10 +429,10 @@
                     // the start index was removed, we need to update the
                     // start index
                     group.setStartIndex(getIndexByPosition(pos + 1));
-                    group.members.remove(Integer.valueOf(index));
-                    group.staticIndexes.remove(Integer.valueOf(index));
+                    group.members.remove(index);
+                    group.staticIndexes.remove(index);
                 } else {
-                    Integer memberIndex = getIndexByPosition(Integer.valueOf(group.getVisibleStartPosition() + group.getVisibleSpan() - 1));
+                    int memberIndex = getIndexByPosition(group.getVisibleStartPosition() + group.getVisibleSpan() - 1);
                     group.members.remove(memberIndex);
                     group.staticIndexes.remove(memberIndex);
                 }
@@ -511,18 +511,15 @@
      */
     public void addStaticIndexesToGroup(Group group, int... indexes) {
 
-        LinkedList<Integer> staticIndexes = new LinkedList<Integer>();
+        int[] staticIndexes = Arrays.stream(indexes)
+                .map(this::getPositionByIndex)
+                .filter(pos -> {
+                    return (pos >= group.getVisibleStartPosition()
+                            && pos < (group.getVisibleStartPosition() + group.getVisibleSpan()));
+                })
+                .toArray();
 
-        for (int index : indexes) {
-            int pos = getPositionByIndex(index);
-            // Check if the index belongs to the group
-            if (pos >= group.getVisibleStartPosition()
-                    && (pos < group.getVisibleStartPosition() + group.getVisibleSpan())) {
-                staticIndexes.add(index);
-            }
-        }
-
-        if (!staticIndexes.isEmpty()) {
+        if (staticIndexes.length > 0) {
             group.staticIndexes.addAll(staticIndexes);
         }
     }
@@ -582,7 +579,7 @@
      */
     public Group getGroupByStaticIndex(int staticIndex) {
         for (Group group : this.groups) {
-            if (group.staticIndexes.contains(Integer.valueOf(staticIndex))) {
+            if (group.staticIndexes.contains(staticIndex)) {
                 return group;
             }
         }
@@ -930,7 +927,7 @@
         /**
          * The indexes that remain visible when collapsing this group.
          */
-        private final Collection<Integer> staticIndexes = new HashSet<Integer>();
+        private final MutableIntSet staticIndexes = IntSets.mutable.empty();
 
         /**
          * Flag to configure whether this group can be collapsed or not.
@@ -955,7 +952,7 @@
          * hide operations performed at the end of a table lead to an
          * inconsistent group state.
          */
-        private final Collection<Integer> members = new HashSet<Integer>();
+        private final MutableIntSet members = IntSets.mutable.empty();
 
         /**
          *
@@ -1158,20 +1155,12 @@
          */
         void consistencyCheck(boolean updateStartIndex) {
             // check if the member indexes are all visible
-            int hidden = 0;
-            int smallestPosition = -1;
-            for (int member : this.members) {
-                int pos = getPositionByIndex(member);
-                if (pos == -1) {
-                    hidden++;
-                } else {
-                    if (smallestPosition == -1) {
-                        smallestPosition = pos;
-                    } else {
-                        smallestPosition = Math.min(smallestPosition, pos);
-                    }
-                }
-            }
+            MutableIntList memberPositions = this.members
+                    .collectInt(member -> getPositionByIndex(member), IntLists.mutable.empty());
+
+            int hidden = memberPositions.count(pos -> pos == -1);
+            int smallestPosition = memberPositions.select(pos -> pos >= 0).minIfEmpty(-1);
+
             setVisibleSpan(this.originalSpan - hidden);
 
             int smallestIndex = getIndexByPosition(smallestPosition);
@@ -1188,8 +1177,8 @@
          * @return The indexes of the members in this collection. Not modifiable
          *         to avoid side effects from the outside of the GroupModel.
          */
-        Collection<Integer> getMembers() {
-            return Collections.unmodifiableCollection(this.members);
+        int[] getMembers() {
+            return this.members.toSortedArray();
         }
 
         /**
@@ -1200,7 +1189,7 @@
          *            The indexes of the positions that should be added to the
          *            local group members.
          */
-        void addMembers(Collection<Integer> memberIndexes) {
+        void addMembers(int... memberIndexes) {
             this.members.addAll(memberIndexes);
         }
 
@@ -1212,7 +1201,7 @@
          *            The indexes of the positions that should be removed from
          *            the local group members.
          */
-        void removeMembers(Collection<Integer> memberIndexes) {
+        void removeMembers(int... memberIndexes) {
             this.members.removeAll(memberIndexes);
         }
 
@@ -1225,7 +1214,7 @@
          *         {@link Group}, <code>false</code> if not.
          */
         public boolean hasMember(int memberIndex) {
-            return this.members.contains(Integer.valueOf(memberIndex));
+            return this.members.contains(memberIndex);
         }
 
         /**
@@ -1286,9 +1275,7 @@
          *            The static indexes to add.
          */
         public void addStaticIndexes(int... indexes) {
-            for (int index : indexes) {
-                this.staticIndexes.add(Integer.valueOf(index));
-            }
+            this.staticIndexes.addAll(indexes);
         }
 
         /**
@@ -1298,27 +1285,42 @@
          *            The static indexes to remove.
          */
         public void removeStaticIndexes(int... indexes) {
-            for (int index : indexes) {
-                this.staticIndexes.remove(Integer.valueOf(index));
-            }
+            this.staticIndexes.removeAll(indexes);
+        }
+
+        /**
+         * Checks if the given index is a static index of this group.
+         *
+         * @param index
+         *            The index to check.
+         * @return <code>true</code> if the given index is configured as static
+         *         index of this group, <code>false</code> if not.
+         * @since 2.0
+         */
+        public boolean containsStaticIndex(int index) {
+            return this.staticIndexes.contains(index);
         }
 
         /**
          * @return The indexes that remain visible when collapsing this group.
+         * @since 2.0
          */
-        public Collection<Integer> getStaticIndexes() {
-            return Collections.unmodifiableCollection(this.staticIndexes);
+        public int[] getStaticIndexes() {
+            return this.staticIndexes.toSortedArray();
         }
 
         /**
          *
          * @return The positions of the visible items in the group matching the
          *         position layer of the GroupModel.
+         * @since 2.0
          */
-        public Collection<Integer> getVisiblePositions() {
-            List<Integer> groupPositions = new ArrayList<>();
-            for (int i = this.visibleStartPosition; i < (this.visibleStartPosition + this.visibleSpan); i++) {
-                groupPositions.add(i);
+        public int[] getVisiblePositions() {
+            int[] groupPositions = new int[this.visibleSpan];
+            int i = 0;
+            for (int pos = this.visibleStartPosition; pos < (this.visibleStartPosition + this.visibleSpan); pos++) {
+                groupPositions[i] = pos;
+                i++;
             }
             return groupPositions;
         }
@@ -1326,11 +1328,14 @@
         /**
          *
          * @return The indexes of the positions that are currently visible.
+         * @since 2.0
          */
-        public Collection<Integer> getVisibleIndexes() {
-            List<Integer> groupIndexes = new ArrayList<>();
-            for (int i = this.visibleStartPosition; i < (this.visibleStartPosition + this.visibleSpan); i++) {
-                groupIndexes.add(getIndexByPosition(i));
+        public int[] getVisibleIndexes() {
+            int[] groupIndexes = new int[this.visibleSpan];
+            int i = 0;
+            for (int pos = this.visibleStartPosition; pos < (this.visibleStartPosition + this.visibleSpan); pos++) {
+                groupIndexes[i] = getIndexByPosition(pos);
+                i++;
             }
             return groupIndexes;
         }
@@ -1349,10 +1354,9 @@
          * @since 2.0
          */
         public int getGroupEndPosition(IUniqueIndexLayer layer) {
-            return this.members.stream()
-                    .mapToInt(layer::getColumnPositionByIndex)
-                    .max()
-                    .orElse(-1);
+            return this.members
+                    .collectInt(layer::getColumnPositionByIndex, IntSets.mutable.empty())
+                    .maxIfEmpty(-1);
         }
 
         /**
@@ -1394,7 +1398,7 @@
                     + "\n\t collapseable: " + this.collapseable //$NON-NLS-1$
                     + "\n\t collapsed: " + this.collapsed //$NON-NLS-1$
                     + "\n\t unbreakable: " + this.unbreakable //$NON-NLS-1$
-                    + "\n\t staticIndexes: " + ObjectUtils.toString(this.staticIndexes) + "\n"; //$NON-NLS-1$ //$NON-NLS-2$
+                    + "\n\t staticIndexes: [ " + this.staticIndexes.makeString(", ") + " ]\n"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
         }
     }
 
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/RowGroupExpandCollapseLayer.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/RowGroupExpandCollapseLayer.java
index 756255d..8469ef8 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/RowGroupExpandCollapseLayer.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/RowGroupExpandCollapseLayer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2019 Dirk Fauth.
+ * Copyright (c) 2019, 2020 Dirk Fauth.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -12,13 +12,15 @@
 
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
+import java.util.stream.Collectors;
 
+import org.eclipse.collections.api.factory.Maps;
+import org.eclipse.collections.api.list.primitive.MutableIntList;
+import org.eclipse.collections.api.map.MutableMap;
+import org.eclipse.collections.api.set.primitive.MutableIntSet;
+import org.eclipse.collections.impl.factory.primitive.IntLists;
+import org.eclipse.collections.impl.factory.primitive.IntSets;
 import org.eclipse.nebula.widgets.nattable.command.ILayerCommand;
 import org.eclipse.nebula.widgets.nattable.group.RowGroupHeaderLayer;
 import org.eclipse.nebula.widgets.nattable.group.performance.GroupModel.Group;
@@ -39,7 +41,7 @@
  */
 public class RowGroupExpandCollapseLayer extends AbstractRowHideShowLayer {
 
-    private final Map<Group, Collection<Integer>> hidden = new HashMap<Group, Collection<Integer>>();
+    private final MutableMap<Group, MutableIntSet> hidden = Maps.mutable.empty();
 
     public RowGroupExpandCollapseLayer(IUniqueIndexLayer underlyingLayer) {
         super(underlyingLayer);
@@ -50,7 +52,7 @@
         if (command instanceof RowGroupExpandCommand) {
             List<Group> groups = ((RowGroupExpandCommand) command).getGroups();
 
-            Set<Integer> shownIndexes = new TreeSet<Integer>();
+            MutableIntSet shownIndexes = IntSets.mutable.empty();
 
             for (Group group : groups) {
                 // if group is not collapseable return without any further
@@ -61,7 +63,7 @@
 
                 if (group.isCollapsed()) {
                     group.setCollapsed(false);
-                    Collection<Integer> rowIndexes = this.hidden.get(group);
+                    MutableIntSet rowIndexes = this.hidden.get(group);
                     this.hidden.remove(group);
                     shownIndexes.addAll(rowIndexes);
                 }
@@ -69,7 +71,7 @@
 
             if (!shownIndexes.isEmpty()) {
                 invalidateCache();
-                fireLayerEvent(new ShowRowPositionsEvent(this, getRowPositionsByIndexes(shownIndexes)));
+                fireLayerEvent(new ShowRowPositionsEvent(this, getRowPositionsByIndexes(shownIndexes.toArray())));
             } else {
                 fireLayerEvent(new VisualRefreshEvent(this));
             }
@@ -78,16 +80,12 @@
         } else if (command instanceof RowGroupCollapseCommand) {
             GroupModel groupModel = ((RowGroupCollapseCommand) command).getGroupModel();
             List<Group> groups = ((RowGroupCollapseCommand) command).getGroups();
-            Collections.sort(groups, new Comparator<Group>() {
-
-                @Override
-                public int compare(Group o1, Group o2) {
-                    return o2.getVisibleStartPosition() - o1.getVisibleStartPosition();
-                }
+            Collections.sort(groups, (Group o1, Group o2) -> {
+                return o2.getVisibleStartPosition() - o1.getVisibleStartPosition();
             });
 
-            Set<Integer> hiddenPositions = new TreeSet<Integer>();
-            Set<Integer> hiddenIndexes = new TreeSet<Integer>();
+            MutableIntSet hiddenPositions = IntSets.mutable.empty();
+            MutableIntSet hiddenIndexes = IntSets.mutable.empty();
 
             for (Group group : groups) {
                 // if group is not collapseable return without any further
@@ -96,7 +94,7 @@
                     continue;
                 }
 
-                Set<Integer> rowIndexes = new TreeSet<Integer>();
+                MutableIntSet rowIndexes = IntSets.mutable.empty();
                 if (!group.isCollapsed()) {
                     rowIndexes.addAll(group.getVisibleIndexes());
                     group.setCollapsed(true);
@@ -112,13 +110,13 @@
                 modifyForVisible(group, rowIndexes);
                 this.hidden.put(group, rowIndexes);
 
-                hiddenPositions.addAll(getRowPositionsByIndexes(rowIndexes));
+                hiddenPositions.addAll(getRowPositionsByIndexes(rowIndexes.toArray()));
                 hiddenIndexes.addAll(rowIndexes);
             }
 
             if (!hiddenPositions.isEmpty()) {
                 invalidateCache();
-                fireLayerEvent(new HideRowPositionsEvent(this, hiddenPositions, hiddenIndexes));
+                fireLayerEvent(new HideRowPositionsEvent(this, hiddenPositions.toArray(), hiddenIndexes.toArray()));
             } else {
                 fireLayerEvent(new VisualRefreshEvent(this));
             }
@@ -127,10 +125,10 @@
         } else if (command instanceof UpdateRowGroupCollapseCommand) {
             UpdateRowGroupCollapseCommand cmd = (UpdateRowGroupCollapseCommand) command;
             Group group = cmd.getGroup();
-            Collection<Integer> hiddenRowIndexes = this.hidden.get(group);
-            if (group.getVisibleIndexes().size() + hiddenRowIndexes.size() <= group.getOriginalSpan()) {
-                Collection<Integer> indexesToHide = cmd.getIndexesToHide();
-                Collection<Integer> indexesToShow = cmd.getIndexesToShow();
+            MutableIntSet hiddenRowIndexes = this.hidden.get(group);
+            if (group.getVisibleIndexes().length + hiddenRowIndexes.size() <= group.getOriginalSpan()) {
+                MutableIntSet indexesToHide = IntSets.mutable.of(cmd.getIndexesToHide());
+                MutableIntSet indexesToShow = IntSets.mutable.of(cmd.getIndexesToShow());
 
                 // remove already hidden indexes
                 indexesToHide.removeAll(hiddenRowIndexes);
@@ -138,14 +136,14 @@
                 // remove static indexes
                 modifyForVisible(group, indexesToHide);
 
-                Collection<Integer> hiddenPositions = getRowPositionsByIndexes(indexesToHide);
+                int[] hiddenPositions = getRowPositionsByIndexes(indexesToHide.toArray());
 
                 hiddenRowIndexes.addAll(indexesToHide);
                 hiddenRowIndexes.removeAll(indexesToShow);
 
                 invalidateCache();
 
-                fireLayerEvent(new HideRowPositionsEvent(this, hiddenPositions, indexesToHide));
+                fireLayerEvent(new HideRowPositionsEvent(this, hiddenPositions, indexesToHide.toArray()));
             }
 
             return true;
@@ -163,9 +161,9 @@
      * @param rowIndexes
      *            The collection of indexes that should be hidden.
      */
-    private void modifyForVisible(Group group, Collection<Integer> rowIndexes) {
-        Collection<Integer> staticIndexes = group.getStaticIndexes();
-        if (staticIndexes.isEmpty()) {
+    private void modifyForVisible(Group group, MutableIntSet rowIndexes) {
+        int[] staticIndexes = group.getStaticIndexes();
+        if (staticIndexes.length == 0) {
             // keep the first row
             rowIndexes.remove(group.getVisibleStartIndex());
         } else {
@@ -176,21 +174,30 @@
 
     @Override
     public boolean isRowIndexHidden(int rowIndex) {
-        for (Collection<Integer> indexes : this.hidden.values()) {
-            if (indexes.contains(Integer.valueOf(rowIndex))) {
-                return true;
-            }
-        }
-        return false;
+        MutableIntSet found = this.hidden.detect(indexes -> indexes.contains(rowIndex));
+        return found != null;
     }
 
     @Override
     public Collection<Integer> getHiddenRowIndexes() {
-        Set<Integer> hiddenRowIndexes = new TreeSet<Integer>();
-        for (Collection<Integer> indexes : this.hidden.values()) {
+        MutableIntList hiddenRowIndexes = IntLists.mutable.empty();
+        for (MutableIntSet indexes : this.hidden.values()) {
             hiddenRowIndexes.addAll(indexes);
         }
-        return hiddenRowIndexes;
+        return hiddenRowIndexes.distinct().toSortedList().primitiveStream().boxed().collect(Collectors.toList());
     }
 
+    @Override
+    public int[] getHiddenRowIndexesArray() {
+        MutableIntList hiddenRowIndexes = IntLists.mutable.empty();
+        for (MutableIntSet indexes : this.hidden.values()) {
+            hiddenRowIndexes.addAll(indexes);
+        }
+        return hiddenRowIndexes.distinct().toSortedArray();
+    }
+
+    @Override
+    public boolean hasHiddenRows() {
+        return !this.hidden.isEmpty();
+    }
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/RowGroupHeaderLayer.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/RowGroupHeaderLayer.java
index f9f685c..901eef2 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/RowGroupHeaderLayer.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/RowGroupHeaderLayer.java
@@ -11,9 +11,8 @@
 package org.eclipse.nebula.widgets.nattable.group.performance;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -21,6 +20,8 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.eclipse.collections.api.list.primitive.MutableIntList;
+import org.eclipse.collections.impl.factory.primitive.IntLists;
 import org.eclipse.nebula.widgets.nattable.NatTable;
 import org.eclipse.nebula.widgets.nattable.command.ILayerCommand;
 import org.eclipse.nebula.widgets.nattable.coordinate.PositionUtil;
@@ -28,7 +29,6 @@
 import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
 import org.eclipse.nebula.widgets.nattable.grid.layer.DimensionallyDependentIndexLayer;
 import org.eclipse.nebula.widgets.nattable.grid.layer.DimensionallyDependentLayer;
-import org.eclipse.nebula.widgets.nattable.group.ColumnGroupUtils;
 import org.eclipse.nebula.widgets.nattable.group.RowGroupUtils;
 import org.eclipse.nebula.widgets.nattable.group.command.RowGroupExpandCollapseCommand;
 import org.eclipse.nebula.widgets.nattable.group.performance.GroupModel.Group;
@@ -75,7 +75,6 @@
 import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
 import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer.MoveDirectionEnum;
 import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
-import org.eclipse.nebula.widgets.nattable.util.ArrayUtil;
 import org.eclipse.swt.graphics.Rectangle;
 
 /**
@@ -620,7 +619,7 @@
             return true;
         } else if (command instanceof MultiColumnResizeCommand && command.convertToTargetLayer(this)) {
             MultiColumnResizeCommand columnResizeCommand = (MultiColumnResizeCommand) command;
-            for (int column : columnResizeCommand.getColumnPositions()) {
+            for (int column : columnResizeCommand.getColumnPositionsArray()) {
                 int newColumnWidth = columnResizeCommand.downScaleValue()
                         ? this.columnWidthConfig.downScale(columnResizeCommand.getColumnWidth(column))
                         : columnResizeCommand.getColumnWidth(column);
@@ -1194,7 +1193,7 @@
         int sizeOfGroup = group.getVisibleSpan();
 
         if (group.isCollapsed()) {
-            int sizeOfStaticRows = group.getStaticIndexes().size();
+            int sizeOfStaticRows = group.getStaticIndexes().length;
             if (sizeOfStaticRows == 0) {
                 return 1;
             } else {
@@ -2253,7 +2252,7 @@
                 GroupMultiRowReorderCommand command =
                         new GroupMultiRowReorderCommand(
                                 getPositionLayer(),
-                                new ArrayList<Integer>(group.getMembers()),
+                                group.getMembers(),
                                 underlyingTo);
                 Group toBottom = groupModel.getGroupByPosition(toPosition);
                 if (toBottom != null) {
@@ -2311,7 +2310,7 @@
                 Collection<StructuralDiff> rowDiffs = changeEvent.getRowDiffs();
                 if (rowDiffs != null && !rowDiffs.isEmpty()) {
 
-                    List<Integer> deletedPositions = getDeletedPositions(rowDiffs);
+                    int[] deletedPositions = getDeletedPositions(rowDiffs);
                     if (deletedPositions != null) {
                         // check if the number of positions are the same as the
                         // number of indexes, otherwise trigger a consistency
@@ -2319,9 +2318,9 @@
                         // always in a valid range, and that could cause a loss
                         // of hidden positions on conversion
                         if (event instanceof RowStructuralChangeEvent
-                                && ((RowStructuralChangeEvent) event).getRowIndexes().size() > deletedPositions.size()) {
+                                && ((RowStructuralChangeEvent) event).getRowIndexes().length > deletedPositions.length) {
                             // this triggers a consistency check
-                            handleDeleteDiffs(new ArrayList<Integer>());
+                            handleDeleteDiffs(new int[0]);
                         } else {
                             handleDeleteDiffs(deletedPositions);
                         }
@@ -2349,9 +2348,9 @@
                                                 cmd = new UpdateRowGroupCollapseCommand(groupModel, group);
                                                 collapseUpdates.put(group, cmd);
                                             }
-                                            if (!group.getStaticIndexes().contains(Integer.valueOf(newStartIndex))) {
-                                                cmd.getIndexesToShow().add(newStartIndex);
-                                                cmd.getIndexesToHide().add(getPositionLayer().getRowIndexByPosition(i) + 1);
+                                            if (!group.containsStaticIndex(newStartIndex)) {
+                                                cmd.addIndexesToShow(newStartIndex);
+                                                cmd.addIndexesToHide(getPositionLayer().getRowIndexByPosition(i) + 1);
                                             }
                                             if (group.getVisibleSpan() == 0) {
                                                 group.setVisibleSpan(1);
@@ -2366,18 +2365,18 @@
                                         Group topGroup = groupModel.getGroupByPosition(i - 1);
                                         if (topGroup != null
                                                 && topGroup.getVisibleSpan() < topGroup.getOriginalSpan()
-                                                && topGroup.getMembers().contains(Integer.valueOf(newStartIndex))) {
+                                                && topGroup.hasMember(newStartIndex)) {
                                             if (!topGroup.isCollapsed()) {
                                                 topGroup.setVisibleSpan(topGroup.getVisibleSpan() + 1);
                                             } else {
-                                                if (!topGroup.getStaticIndexes().contains(Integer.valueOf(newStartIndex))) {
+                                                if (!topGroup.containsStaticIndex(newStartIndex)) {
                                                     // update collapsed state
                                                     UpdateRowGroupCollapseCommand cmd = collapseUpdates.get(topGroup);
                                                     if (cmd == null) {
                                                         cmd = new UpdateRowGroupCollapseCommand(groupModel, topGroup);
                                                         collapseUpdates.put(topGroup, cmd);
                                                     }
-                                                    cmd.getIndexesToHide().add(getPositionLayer().getRowIndexByPosition(i));
+                                                    cmd.addIndexesToHide(getPositionLayer().getRowIndexByPosition(i));
                                                 } else {
                                                     topGroup.setVisibleSpan(topGroup.getVisibleSpan() + 1);
                                                 }
@@ -2398,7 +2397,7 @@
                                                 // member
                                                 for (int e = diff.getAfterPositionRange().end; e > diff.getAfterPositionRange().start; e--) {
                                                     g = groupModel.getGroupByPosition(e);
-                                                    if (g != null && g.getMembers().contains(newStartIndex)) {
+                                                    if (g != null && g.hasMember(newStartIndex)) {
                                                         g.setStartIndex(newStartIndex);
                                                         g.setVisibleStartIndex(newStartIndex);
                                                         g.setVisibleSpan(g.getVisibleSpan() + 1);
@@ -2431,55 +2430,48 @@
             }
         }
 
-        private List<Integer> getDeletedPositions(Collection<StructuralDiff> rowDiffs) {
-            List<Integer> result = new ArrayList<Integer>();
+        private int[] getDeletedPositions(Collection<StructuralDiff> rowDiffs) {
+            MutableIntList result = IntLists.mutable.empty();
             boolean deleteDiffFound = false;
             for (StructuralDiff diff : rowDiffs) {
                 if (diff.getDiffType() == DiffTypeEnum.DELETE) {
                     deleteDiffFound = true;
                     int[] positions = PositionUtil.getPositions(diff.getBeforePositionRange());
-                    result.addAll(ArrayUtil.asIntegerList(positions));
+                    result.addAll(positions);
                 }
             }
             if (deleteDiffFound) {
                 // we need to handle the deleted positions backwards
-                Collections.sort(result, new Comparator<Integer>() {
-                    @Override
-                    public int compare(Integer o1, Integer o2) {
-                        return o2.compareTo(o1);
-                    }
-                });
-                return result;
+                return result.sortThis().reverseThis().toArray();
             } else {
                 // there was no DELETE diff, so we return null
                 return null;
             }
         }
 
-        private void handleDeleteDiffs(List<Integer> positionList) {
-            if (positionList.size() > 0) {
+        private void handleDeleteDiffs(int[] positionList) {
+            if (positionList.length > 0) {
                 for (GroupModel groupModel : RowGroupHeaderLayer.this.model) {
-                    List<Integer> groupPositionList = new ArrayList<Integer>(positionList);
+                    // we need to create the MutableIntList on a copy of the
+                    // positionList array, because modifications on the
+                    // MutableIntList will be traversed to the the underlying
+                    // array
+                    MutableIntList groupPositionList = IntLists.mutable.of(Arrays.copyOf(positionList, positionList.length));
                     while (!groupPositionList.isEmpty()) {
                         // find group and update visible span
                         // we need to iterate because one could hide the
                         // last row in one group and the first of
                         // another group at once
-                        Integer pos = groupPositionList.get(0);
+                        int pos = groupPositionList.get(0);
                         Group group = groupModel.getGroupByPosition(pos);
                         if (group != null) {
                             // find all positions belonging to
                             // the group
-                            List<Integer> groupPositions = new ArrayList<Integer>(group.getVisiblePositions());
+                            MutableIntList groupPositions = IntLists.mutable.of(group.getVisiblePositions());
 
                             // find the positions in the group
                             // that are hidden
-                            List<Integer> hiddenGroupPositions = new ArrayList<Integer>();
-                            for (int groupPos : groupPositions) {
-                                if (groupPositionList.contains(groupPos)) {
-                                    hiddenGroupPositions.add(groupPos);
-                                }
-                            }
+                            MutableIntList hiddenGroupPositions = groupPositions.select(groupPos -> groupPositionList.contains(groupPos));
 
                             // update the positionList as we
                             // handled the hidden rows
@@ -2488,11 +2480,11 @@
                             groupPositions.removeAll(hiddenGroupPositions);
                             if (groupPositions.size() > 0) {
                                 group.setVisibleSpan(groupPositions.size());
-                                for (int i : hiddenGroupPositions) {
+                                hiddenGroupPositions.forEach(i -> {
                                     if (group.getVisibleStartPosition() == i) {
                                         group.setVisibleStartIndex(getPositionLayer().getRowIndexByPosition(i - groupPositionList.size()));
                                     }
-                                }
+                                });
                             } else {
                                 group.setVisibleStartIndex(-1);
                                 group.setVisibleSpan(0);
@@ -2569,7 +2561,7 @@
                     // position from the group
                     if (fromRowPosition == toRowPosition
                             && (!RowGroupUtils.isGroupReordered(fromRowGroup, fromRowPositions)
-                                    || (fromRowGroup.isCollapsed() && fromRowGroup.getMembers().size() > 1))
+                                    || (fromRowGroup.isCollapsed() && fromRowGroup.getMembers().length > 1))
                             && (fromRowGroup.isGroupStart(fromRowPosition)
                                     || fromRowGroup.isGroupEnd(fromRowPosition))) {
                         if (MoveDirectionEnum.DOWN == moveDirection) {
@@ -2583,7 +2575,7 @@
                                     groupModel,
                                     fromRowGroup,
                                     pos,
-                                    reorderEvent.getBeforeFromRowIndexes(),
+                                    reorderEvent.getBeforeFromRowIndexesArray(),
                                     fromRowPosition,
                                     moveDirection);
                         } else {
@@ -2591,7 +2583,7 @@
                                     groupModel,
                                     fromRowGroup,
                                     fromRowPositions,
-                                    reorderEvent.getBeforeFromRowIndexes(),
+                                    reorderEvent.getBeforeFromRowIndexesArray(),
                                     fromRowPosition,
                                     moveDirection);
                         }
@@ -2607,7 +2599,7 @@
                         int newStartIndex = getPositionLayer().getRowIndexByPosition(fromRowGroup.getVisibleStartPosition());
                         fromRowGroup.setVisibleStartIndex(newStartIndex);
                         if (!fromRowGroup.isCollapsed()
-                                || fromRowGroup.getStaticIndexes().contains(Integer.valueOf(fromRowGroup.getStartIndex()))) {
+                                || fromRowGroup.containsStaticIndex(fromRowGroup.getStartIndex())) {
                             // if we move inside a collapsed group, there are
                             // static indexes, and therefore we do not update
                             // the start index, as the start index should be the
@@ -2621,17 +2613,17 @@
                             groupModel,
                             toRowGroup,
                             fromRowPositions,
-                            reorderEvent.getBeforeFromRowIndexes(),
+                            reorderEvent.getBeforeFromRowIndexesArray(),
                             toRowPosition,
                             moveDirection);
                 } else if (fromRowGroup != null
                         && toRowGroup == null
-                        && !ColumnGroupUtils.isGroupReordered(fromRowGroup, fromRowPositions)) {
+                        && !RowGroupUtils.isGroupReordered(fromRowGroup, fromRowPositions)) {
                     removePositionsFromGroup(
                             groupModel,
                             fromRowGroup,
                             fromRowPositions,
-                            reorderEvent.getBeforeFromRowIndexes(),
+                            reorderEvent.getBeforeFromRowIndexesArray(),
                             fromRowPosition,
                             moveDirection);
                 } else if (fromRowGroup == null
@@ -2649,7 +2641,7 @@
                                 groupModel,
                                 adjacentRowGroup,
                                 fromRowPositions,
-                                reorderEvent.getBeforeFromRowIndexes(),
+                                reorderEvent.getBeforeFromRowIndexesArray(),
                                 (adjacentRowGroup.isCollapsed() && moveDirection != MoveDirectionEnum.DOWN) ? toRowPosition : adjacentPos,
                                 moveDirection);
                     }
@@ -2657,20 +2649,20 @@
                         && toRowGroup != null
                         && !fromRowGroup.equals(toRowGroup)
                         && (!RowGroupUtils.isGroupReordered(fromRowGroup, fromRowPositions)
-                                || (!fromRowGroup.isCollapsed() && fromRowGroup.getVisiblePositions().size() == 1))) {
+                                || (!fromRowGroup.isCollapsed() && fromRowGroup.getVisiblePositions().length == 1))) {
 
                     removePositionsFromGroup(
                             groupModel,
                             fromRowGroup,
                             fromRowPositions,
-                            reorderEvent.getBeforeFromRowIndexes(),
+                            reorderEvent.getBeforeFromRowIndexesArray(),
                             fromRowPositions[0],
                             moveDirection);
                     addPositionsToGroup(
                             groupModel,
                             toRowGroup,
                             fromRowPositions,
-                            reorderEvent.getBeforeFromRowIndexes(),
+                            reorderEvent.getBeforeFromRowIndexesArray(),
                             toRowPosition,
                             moveDirection);
                 }
@@ -2683,7 +2675,7 @@
                 GroupModel groupModel,
                 Group group,
                 int[] fromRowPositions,
-                Collection<Integer> fromRowIndexes,
+                int[] fromRowIndexes,
                 int toPosition,
                 MoveDirectionEnum moveDirection) {
 
@@ -2711,9 +2703,9 @@
                     // update collapsed state
                     UpdateRowGroupCollapseCommand cmd = new UpdateRowGroupCollapseCommand(groupModel, group);
                     if (group.isGroupStart(toPosition)) {
-                        cmd.getIndexesToHide().addAll(group.getMembers());
+                        cmd.addIndexesToHide(group.getMembers());
 
-                        if (!group.getStaticIndexes().isEmpty()) {
+                        if (group.getStaticIndexes().length > 0) {
                             group.setVisibleSpan(group.getVisibleSpan() + fromRowPositions.length);
                         }
 
@@ -2724,7 +2716,7 @@
                         // on the correct positions
                         group.updateVisibleStartPosition();
                     } else {
-                        cmd.getIndexesToHide().addAll(fromRowIndexes);
+                        cmd.addIndexesToHide(fromRowIndexes);
                         group.setVisibleSpan(group.getVisibleSpan() + fromRowPositions.length);
                     }
                     getPositionLayer().doCommand(cmd);
@@ -2740,7 +2732,7 @@
                 GroupModel groupModel,
                 Group group,
                 int[] fromRowPositions,
-                Collection<Integer> fromRowIndexes,
+                int[] fromRowIndexes,
                 int fromRowPosition,
                 MoveDirectionEnum moveDirection) {
 
@@ -2770,7 +2762,7 @@
                 group.removeMembers(fromRowIndexes);
 
                 // remove static index if removed position was a static index
-                group.removeStaticIndexes(ArrayUtil.asIntArray(fromRowIndexes));
+                group.removeStaticIndexes(fromRowIndexes);
 
                 if (group.getOriginalSpan() > 0) {
                     group.updateVisibleStartPosition();
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/command/ColumnGroupsCommandHandler.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/command/ColumnGroupsCommandHandler.java
index ffd6706..6351085 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/command/ColumnGroupsCommandHandler.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/command/ColumnGroupsCommandHandler.java
@@ -10,15 +10,12 @@
  ******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.group.performance.command;
 
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
 
+import org.eclipse.collections.api.iterator.MutableIntIterator;
+import org.eclipse.collections.api.list.primitive.MutableIntList;
+import org.eclipse.collections.impl.factory.primitive.IntLists;
 import org.eclipse.nebula.widgets.nattable.Messages;
 import org.eclipse.nebula.widgets.nattable.command.AbstractLayerCommandHandler;
 import org.eclipse.nebula.widgets.nattable.group.command.CreateColumnGroupCommand;
@@ -101,7 +98,7 @@
         // transformation
         GroupModel model = this.contextLayer.getGroupModel();
 
-        Set<Integer> positionsToGroup = new TreeSet<Integer>();
+        MutableIntList positionsToGroup = IntLists.mutable.empty();
         if (fullySelectedColumns != null && fullySelectedColumns.length > 0) {
             for (int column : fullySelectedColumns) {
                 // convert to position layer
@@ -113,8 +110,8 @@
                 }
             }
 
-            Set<Group> existingGroups = new HashSet<Group>();
-            for (Iterator<Integer> it = positionsToGroup.iterator(); it.hasNext();) {
+            HashSet<Group> existingGroups = new HashSet<Group>();
+            for (MutableIntIterator it = positionsToGroup.intIterator(); it.hasNext();) {
                 int column = it.next();
                 Group group = model.getGroupByPosition(column);
                 if (group != null) {
@@ -139,17 +136,19 @@
                 }
             }
 
-            List<Integer> selectedPositions = new ArrayList<Integer>(positionsToGroup);
+            // bring it to the correct order and remove duplicates
+            positionsToGroup.sortThis();
+            MutableIntList selectedPositions = IntLists.mutable.ofAll(positionsToGroup.distinct());
 
             if (selectedPositions.size() > 1) {
                 // if a group is created for more than one column, reorder so
                 // the positions are consecutive which is necessary for grouping
                 this.selectionLayer.doCommand(
-                        new MultiColumnReorderCommand(this.selectionLayer, selectedPositions, selectedPositions.get(0)));
+                        new MultiColumnReorderCommand(this.selectionLayer, selectedPositions.toArray(), selectedPositions.get(0)));
             }
 
             // create the column group
-            this.contextLayer.addGroup(columnGroupName, this.selectionLayer.getColumnIndexByPosition(selectedPositions.get(0)), positionsToGroup.size());
+            this.contextLayer.addGroup(columnGroupName, this.selectionLayer.getColumnIndexByPosition(selectedPositions.get(0)), selectedPositions.size());
 
             this.selectionLayer.clear();
 
@@ -191,7 +190,7 @@
         int[] fullySelectedColumns = this.selectionLayer.getFullySelectedColumnPositions();
 
         if (fullySelectedColumns != null && fullySelectedColumns.length > 0) {
-            Set<Integer> positionsToUngroup = new TreeSet<Integer>();
+            MutableIntList positionsToUngroup = IntLists.mutable.empty();
             for (int column : fullySelectedColumns) {
                 // convert to position layer
                 // needed because the group model takes the positions based on
@@ -205,16 +204,16 @@
             // we operate on the GroupModel directly to avoid the position
             // transformation
             GroupModel model = this.contextLayer.getGroupModel();
-            Map<Group, List<Integer>> toRemove = new HashMap<Group, List<Integer>>();
-            for (int pos : positionsToUngroup) {
+            HashMap<Group, MutableIntList> toRemove = new HashMap<>();
+            positionsToUngroup.forEach(pos -> {
                 Group group = model.getGroupByPosition(pos);
                 if (group != null) {
                     int endPos = group.getVisibleStartPosition() + group.getVisibleSpan();
                     if (pos < endPos && !group.isGroupStart(pos)) {
                         // remember position to remove
-                        List<Integer> remove = toRemove.get(group);
+                        MutableIntList remove = toRemove.get(group);
                         if (remove == null) {
-                            remove = new ArrayList<Integer>();
+                            remove = IntLists.mutable.empty();
                             toRemove.put(group, remove);
                         }
                         remove.add(pos);
@@ -222,16 +221,16 @@
                         model.removePositionsFromGroup(group, pos);
                     }
                 }
-            }
+            });
 
             if (!toRemove.isEmpty()) {
-                for (Map.Entry<Group, List<Integer>> entry : toRemove.entrySet()) {
+                toRemove.entrySet().forEach(entry -> {
                     Group group = entry.getKey();
                     int endPos = group.getVisibleStartPosition() + group.getVisibleSpan();
 
-                    this.selectionLayer.doCommand(new MultiColumnReorderCommand(this.selectionLayer, entry.getValue(), endPos));
+                    this.selectionLayer.doCommand(new MultiColumnReorderCommand(this.selectionLayer, entry.getValue().toArray(), endPos));
 
-                    List<Integer> value = entry.getValue();
+                    MutableIntList value = entry.getValue();
                     int start = endPos - value.size();
                     int[] positionsToRemove = new int[value.size()];
                     for (int i = 0; i < entry.getValue().size(); i++) {
@@ -239,7 +238,7 @@
                     }
 
                     model.removePositionsFromGroup(group, positionsToRemove);
-                }
+                });
             }
 
             this.selectionLayer.clear();
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/command/GroupMultiColumnReorderCommand.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/command/GroupMultiColumnReorderCommand.java
index c815850..dbfdf0e 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/command/GroupMultiColumnReorderCommand.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/command/GroupMultiColumnReorderCommand.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2019 Dirk Fauth.
+ * Copyright (c) 2019, 2020 Dirk Fauth.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -65,6 +65,46 @@
             int toColumnPosition,
             boolean reorderToLeftEdge) {
 
+        super(layer, fromColumnPositions.stream().mapToInt(Integer::intValue).toArray(), toColumnPosition, reorderToLeftEdge);
+    }
+
+    /**
+     *
+     * @param layer
+     *            The layer to which the column positions match.
+     * @param fromColumnPositions
+     *            The column positions to reorder.
+     * @param toColumnPosition
+     *            The target column position to reorder to.
+     * @since 2.0
+     */
+    public GroupMultiColumnReorderCommand(ILayer layer, int[] fromColumnPositions, int toColumnPosition) {
+        this(layer,
+                fromColumnPositions,
+                toColumnPosition < layer.getColumnCount() ? toColumnPosition : toColumnPosition - 1,
+                toColumnPosition < layer.getColumnCount());
+    }
+
+    /**
+     *
+     * @param layer
+     *            The layer to which the column positions match.
+     * @param fromColumnPositions
+     *            The column positions to reorder.
+     * @param toColumnPosition
+     *            The target column position to reorder to.
+     * @param reorderToLeftEdge
+     *            <code>true</code> if the reorder operation should be done on
+     *            the left edge of the toColumnPosition, <code>false</code> if
+     *            it should be reordered to the right edge.
+     * @since 2.0
+     */
+    public GroupMultiColumnReorderCommand(
+            ILayer layer,
+            int[] fromColumnPositions,
+            int toColumnPosition,
+            boolean reorderToLeftEdge) {
+
         super(layer, fromColumnPositions, toColumnPosition, reorderToLeftEdge);
     }
 
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/command/GroupMultiRowReorderCommand.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/command/GroupMultiRowReorderCommand.java
index 8727153..c26a162 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/command/GroupMultiRowReorderCommand.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/command/GroupMultiRowReorderCommand.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2019 Dirk Fauth.
+ * Copyright (c) 2019, 2020 Dirk Fauth.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -65,6 +65,46 @@
             int toRowPosition,
             boolean reorderToTopEdge) {
 
+        super(layer, fromRowPositions.stream().mapToInt(Integer::intValue).toArray(), toRowPosition, reorderToTopEdge);
+    }
+
+    /**
+     *
+     * @param layer
+     *            The layer to which the row positions match.
+     * @param fromRowPositions
+     *            The row positions to reorder.
+     * @param toRowPosition
+     *            The target row position to reorder to.
+     * @since 2.0
+     */
+    public GroupMultiRowReorderCommand(ILayer layer, int[] fromRowPositions, int toRowPosition) {
+        this(layer,
+                fromRowPositions,
+                toRowPosition < layer.getRowCount() ? toRowPosition : toRowPosition - 1,
+                toRowPosition < layer.getRowCount());
+    }
+
+    /**
+     *
+     * @param layer
+     *            The layer to which the row positions match.
+     * @param fromRowPositions
+     *            The row positions to reorder.
+     * @param toRowPosition
+     *            The target row position to reorder to.
+     * @param reorderToTopEdge
+     *            <code>true</code> if the reorder operation should be done on
+     *            the top edge of the toRowPosition, <code>false</code> if it
+     *            should be reordered to the bottom edge.
+     * @since 2.0
+     */
+    public GroupMultiRowReorderCommand(
+            ILayer layer,
+            int[] fromRowPositions,
+            int toRowPosition,
+            boolean reorderToTopEdge) {
+
         super(layer, fromRowPositions, toRowPosition, reorderToTopEdge);
     }
 
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/command/RowGroupsCommandHandler.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/command/RowGroupsCommandHandler.java
index c89ea29..84bab56 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/command/RowGroupsCommandHandler.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/command/RowGroupsCommandHandler.java
@@ -10,15 +10,12 @@
  ******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.group.performance.command;
 
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
 
+import org.eclipse.collections.api.iterator.MutableIntIterator;
+import org.eclipse.collections.api.list.primitive.MutableIntList;
+import org.eclipse.collections.impl.factory.primitive.IntLists;
 import org.eclipse.nebula.widgets.nattable.Messages;
 import org.eclipse.nebula.widgets.nattable.command.AbstractLayerCommandHandler;
 import org.eclipse.nebula.widgets.nattable.group.command.CreateRowGroupCommand;
@@ -101,7 +98,7 @@
         // transformation
         GroupModel model = this.contextLayer.getGroupModel();
 
-        Set<Integer> positionsToGroup = new TreeSet<Integer>();
+        MutableIntList positionsToGroup = IntLists.mutable.empty();
         if (fullySelectedRows != null && fullySelectedRows.length > 0) {
             for (int row : fullySelectedRows) {
                 // convert to position layer
@@ -113,8 +110,8 @@
                 }
             }
 
-            Set<Group> existingGroups = new HashSet<Group>();
-            for (Iterator<Integer> it = positionsToGroup.iterator(); it.hasNext();) {
+            HashSet<Group> existingGroups = new HashSet<Group>();
+            for (MutableIntIterator it = positionsToGroup.intIterator(); it.hasNext();) {
                 int row = it.next();
                 Group group = model.getGroupByPosition(row);
                 if (group != null) {
@@ -139,17 +136,18 @@
                 }
             }
 
-            List<Integer> selectedPositions = new ArrayList<Integer>(positionsToGroup);
+            positionsToGroup.sortThis();
+            MutableIntList selectedPositions = IntLists.mutable.ofAll(positionsToGroup.distinct());
 
             if (selectedPositions.size() > 1) {
                 // if a group is created for more than one row, reorder so
                 // the positions are consecutive which is necessary for grouping
                 this.selectionLayer.doCommand(
-                        new MultiRowReorderCommand(this.selectionLayer, selectedPositions, selectedPositions.get(0)));
+                        new MultiRowReorderCommand(this.selectionLayer, selectedPositions.toArray(), selectedPositions.get(0)));
             }
 
             // create the row group
-            this.contextLayer.addGroup(rowGroupName, this.selectionLayer.getRowIndexByPosition(selectedPositions.get(0)), positionsToGroup.size());
+            this.contextLayer.addGroup(rowGroupName, this.selectionLayer.getRowIndexByPosition(selectedPositions.get(0)), selectedPositions.size());
 
             this.selectionLayer.clear();
 
@@ -190,7 +188,7 @@
         int[] fullySelectedRows = this.selectionLayer.getFullySelectedRowPositions();
 
         if (fullySelectedRows != null && fullySelectedRows.length > 0) {
-            Set<Integer> positionsToUngroup = new TreeSet<Integer>();
+            MutableIntList positionsToUngroup = IntLists.mutable.empty();
             for (int row : fullySelectedRows) {
                 // convert to position layer
                 // needed because the group model takes the positions based on
@@ -204,16 +202,16 @@
             // we operate on the GroupModel directly to avoid the position
             // transformation
             GroupModel model = this.contextLayer.getGroupModel();
-            Map<Group, List<Integer>> toRemove = new HashMap<Group, List<Integer>>();
-            for (int pos : positionsToUngroup) {
+            HashMap<Group, MutableIntList> toRemove = new HashMap<>();
+            positionsToUngroup.forEach(pos -> {
                 Group group = model.getGroupByPosition(pos);
                 if (group != null) {
                     int endPos = group.getVisibleStartPosition() + group.getVisibleSpan();
                     if (pos < endPos && !group.isGroupStart(pos)) {
                         // remember position to remove
-                        List<Integer> remove = toRemove.get(group);
+                        MutableIntList remove = toRemove.get(group);
                         if (remove == null) {
-                            remove = new ArrayList<Integer>();
+                            remove = IntLists.mutable.empty();
                             toRemove.put(group, remove);
                         }
                         remove.add(pos);
@@ -221,16 +219,16 @@
                         model.removePositionsFromGroup(group, pos);
                     }
                 }
-            }
+            });
 
             if (!toRemove.isEmpty()) {
-                for (Map.Entry<Group, List<Integer>> entry : toRemove.entrySet()) {
+                toRemove.entrySet().forEach(entry -> {
                     Group group = entry.getKey();
                     int endPos = group.getVisibleStartPosition() + group.getVisibleSpan();
 
-                    this.selectionLayer.doCommand(new MultiRowReorderCommand(this.selectionLayer, entry.getValue(), endPos));
+                    this.selectionLayer.doCommand(new MultiRowReorderCommand(this.selectionLayer, entry.getValue().toArray(), endPos));
 
-                    List<Integer> value = entry.getValue();
+                    MutableIntList value = entry.getValue();
                     int start = endPos - value.size();
                     int[] positionsToRemove = new int[value.size()];
                     for (int i = 0; i < entry.getValue().size(); i++) {
@@ -238,7 +236,7 @@
                     }
 
                     model.removePositionsFromGroup(group, positionsToRemove);
-                }
+                });
             }
 
             this.selectionLayer.clear();
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/command/UpdateColumnGroupCollapseCommand.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/command/UpdateColumnGroupCollapseCommand.java
index 188036e..405e055 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/command/UpdateColumnGroupCollapseCommand.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/command/UpdateColumnGroupCollapseCommand.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2019 Dirk Fauth.
+ * Copyright (c) 2019, 2020 Dirk Fauth.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -10,9 +10,8 @@
  ******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.group.performance.command;
 
-import java.util.Collection;
-import java.util.TreeSet;
-
+import org.eclipse.collections.api.set.primitive.MutableIntSet;
+import org.eclipse.collections.impl.factory.primitive.IntSets;
 import org.eclipse.nebula.widgets.nattable.command.AbstractContextFreeCommand;
 import org.eclipse.nebula.widgets.nattable.group.performance.GroupModel;
 import org.eclipse.nebula.widgets.nattable.group.performance.GroupModel.Group;
@@ -27,14 +26,29 @@
 
     private final GroupModel groupModel;
     private final Group group;
-    private final Collection<Integer> indexesToHide = new TreeSet<Integer>();
-    private final Collection<Integer> indexesToShow = new TreeSet<Integer>();
 
+    private final MutableIntSet indexesToHide = IntSets.mutable.empty();
+    private final MutableIntSet indexesToShow = IntSets.mutable.empty();
+
+    /**
+     *
+     * @param groupModel
+     *            The {@link GroupModel} to which the {@link Group} belongs that
+     *            should be updated.
+     * @param group
+     *            The {@link Group} that should be updated.
+     */
     public UpdateColumnGroupCollapseCommand(GroupModel groupModel, Group group) {
         this.groupModel = groupModel;
         this.group = group;
     }
 
+    /**
+     * Clone constructor.
+     *
+     * @param command
+     *            The command to clone.
+     */
     protected UpdateColumnGroupCollapseCommand(UpdateColumnGroupCollapseCommand command) {
         this.groupModel = command.groupModel;
         this.group = command.group;
@@ -47,20 +61,61 @@
         return new UpdateColumnGroupCollapseCommand(this);
     }
 
+    /**
+     *
+     * @return The {@link GroupModel} to which the {@link Group} belongs that
+     *         should be updated.
+     */
     public GroupModel getGroupModel() {
         return this.groupModel;
     }
 
+    /**
+     *
+     * @return The {@link Group} that should be updated.
+     */
     public Group getGroup() {
         return this.group;
     }
 
-    public Collection<Integer> getIndexesToHide() {
-        return this.indexesToHide;
+    /**
+     *
+     * @return The indexes to hide.
+     * @since 2.0
+     */
+    public int[] getIndexesToHide() {
+        return this.indexesToHide.toSortedArray();
     }
 
-    public Collection<Integer> getIndexesToShow() {
-        return this.indexesToShow;
+    /**
+     *
+     * @return The indexes to show.
+     * @since 2.0
+     */
+    public int[] getIndexesToShow() {
+        return this.indexesToShow.toSortedArray();
+    }
+
+    /**
+     * Add the given indexes to the indexes to hide.
+     *
+     * @param indexes
+     *            The indexes that should be added to the indexes to hide.
+     * @since 2.0
+     */
+    public void addIndexesToHide(int... indexes) {
+        this.indexesToHide.addAll(indexes);
+    }
+
+    /**
+     * Add the given indexes to the indexes to show.
+     *
+     * @param indexes
+     *            The indexes that should be added to the indexes to show.
+     * @since 2.0
+     */
+    public void addIndexesToShow(int... indexes) {
+        this.indexesToShow.addAll(indexes);
     }
 
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/command/UpdateRowGroupCollapseCommand.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/command/UpdateRowGroupCollapseCommand.java
index 0416789..eb1b4e6 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/command/UpdateRowGroupCollapseCommand.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/group/performance/command/UpdateRowGroupCollapseCommand.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2019 Dirk Fauth.
+ * Copyright (c) 2019, 2020 Dirk Fauth.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -10,16 +10,15 @@
  ******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.group.performance.command;
 
-import java.util.Collection;
-import java.util.TreeSet;
-
+import org.eclipse.collections.api.set.primitive.MutableIntSet;
+import org.eclipse.collections.impl.factory.primitive.IntSets;
 import org.eclipse.nebula.widgets.nattable.command.AbstractContextFreeCommand;
 import org.eclipse.nebula.widgets.nattable.group.performance.GroupModel;
 import org.eclipse.nebula.widgets.nattable.group.performance.GroupModel.Group;
 
 /**
  * Command that is used to update the hidden indexes for a row group in the
- * RowGroupExpandCollapseLayer.
+ * RowGroupExpandCollapseLayer. Internally used to sync grouping related layers.
  *
  * @since 1.6
  */
@@ -27,14 +26,29 @@
 
     private final GroupModel groupModel;
     private final Group group;
-    private final Collection<Integer> indexesToHide = new TreeSet<Integer>();
-    private final Collection<Integer> indexesToShow = new TreeSet<Integer>();
 
+    private final MutableIntSet indexesToHide = IntSets.mutable.empty();
+    private final MutableIntSet indexesToShow = IntSets.mutable.empty();
+
+    /**
+     *
+     * @param groupModel
+     *            The {@link GroupModel} to which the {@link Group} belongs that
+     *            should be updated.
+     * @param group
+     *            The {@link Group} that should be updated.
+     */
     public UpdateRowGroupCollapseCommand(GroupModel groupModel, Group group) {
         this.groupModel = groupModel;
         this.group = group;
     }
 
+    /**
+     * Clone constructor.
+     *
+     * @param command
+     *            The command to clone.
+     */
     protected UpdateRowGroupCollapseCommand(UpdateRowGroupCollapseCommand command) {
         this.groupModel = command.groupModel;
         this.group = command.group;
@@ -47,20 +61,61 @@
         return new UpdateRowGroupCollapseCommand(this);
     }
 
+    /**
+     *
+     * @return The {@link GroupModel} to which the {@link Group} belongs that
+     *         should be updated.
+     */
     public GroupModel getGroupModel() {
         return this.groupModel;
     }
 
+    /**
+     *
+     * @return The {@link Group} that should be updated.
+     */
     public Group getGroup() {
         return this.group;
     }
 
-    public Collection<Integer> getIndexesToHide() {
-        return this.indexesToHide;
+    /**
+     *
+     * @return The indexes to hide.
+     * @since 2.0
+     */
+    public int[] getIndexesToHide() {
+        return this.indexesToHide.toSortedArray();
     }
 
-    public Collection<Integer> getIndexesToShow() {
-        return this.indexesToShow;
+    /**
+     *
+     * @return The indexes to show.
+     * @since 2.0
+     */
+    public int[] getIndexesToShow() {
+        return this.indexesToShow.toSortedArray();
+    }
+
+    /**
+     * Add the given indexes to the indexes to hide.
+     *
+     * @param indexes
+     *            The indexes that should be added to the indexes to hide.
+     * @since 2.0
+     */
+    public void addIndexesToHide(int... indexes) {
+        this.indexesToHide.addAll(indexes);
+    }
+
+    /**
+     * Add the given indexes to the indexes to show.
+     *
+     * @param indexes
+     *            The indexes that should be added to the indexes to show.
+     * @since 2.0
+     */
+    public void addIndexesToShow(int... indexes) {
+        this.indexesToShow.addAll(indexes);
     }
 
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/AbstractColumnHideShowLayer.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/AbstractColumnHideShowLayer.java
index 411d384..af3bbb5 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/AbstractColumnHideShowLayer.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/AbstractColumnHideShowLayer.java
@@ -11,6 +11,7 @@
 package org.eclipse.nebula.widgets.nattable.hideshow;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashSet;
 
@@ -39,7 +40,7 @@
     private MutableIntIntMap cachedVisibleColumnIndexPositionMapping;
     private MutableIntIntMap cachedVisibleColumnPositionIndexMapping;
     private MutableIntIntMap cachedHiddenColumnIndexPositionMapping;
-    private final MutableIntIntMap startXCache = IntIntMaps.mutable.empty();
+    private MutableIntIntMap startXCache = IntIntMaps.mutable.empty();
 
     /**
      * Constructor.
@@ -62,7 +63,7 @@
 
     @Override
     public void handleLayerEvent(ILayerEvent event) {
-        if (!getHiddenColumnIndexes().isEmpty() && event instanceof ColumnReorderEvent) {
+        if (hasHiddenColumns() && event instanceof ColumnReorderEvent) {
             // we need to convert the before positions in the event BEFORE the
             // local states are changed, otherwise we are not able to convert
             // the before positions as the changed layer states would return
@@ -105,7 +106,7 @@
         } else if (event instanceof VisualRefreshEvent) {
             // visual change, e.g. font change, the startXCache needs to be
             // cleared in order to re-render correctly
-            this.startXCache.clear();
+            this.startXCache = IntIntMaps.mutable.empty();
         }
         super.handleLayerEvent(event);
     }
@@ -116,7 +117,7 @@
 
     @Override
     public int getColumnCount() {
-        if (getHiddenColumnIndexes().isEmpty()) {
+        if (!hasHiddenColumns()) {
             return super.getColumnCount();
         }
         return getCachedVisibleColumnIndexPositionMapping().size();
@@ -128,7 +129,7 @@
             return -1;
         }
 
-        if (getHiddenColumnIndexes().isEmpty()) {
+        if (!hasHiddenColumns()) {
             return super.getColumnIndexByPosition(columnPosition);
         }
 
@@ -137,13 +138,24 @@
 
     @Override
     public int getColumnPositionByIndex(int columnIndex) {
-        if (getHiddenColumnIndexes().isEmpty()) {
+        if (!hasHiddenColumns()) {
             return getUnderlyingLayer().getColumnPositionByIndex(columnIndex);
         }
 
         return getCachedVisibleColumnIndexPositionMapping().getIfAbsent(columnIndex, -1);
     }
 
+    /**
+     * Get the local column positions for the given column indexes.
+     *
+     * @param columnIndexes
+     *            The column indexes for which the local column positions are
+     *            requested.
+     * @return The local column positions for the given column indexes.
+     * @deprecated Use {@link #getColumnPositionsByIndexes(int...)} using
+     *             primitive values to avoid unnecessary boxing.
+     */
+    @Deprecated
     public Collection<Integer> getColumnPositionsByIndexes(Collection<Integer> columnIndexes) {
         Collection<Integer> columnPositions = new HashSet<>();
         for (int columnIndex : columnIndexes) {
@@ -152,13 +164,29 @@
         return columnPositions;
     }
 
+    /**
+     * Get the local column positions for the given column indexes.
+     *
+     * @param columnIndexes
+     *            The column indexes for which the local column positions are
+     *            requested.
+     * @return The local column positions for the given column indexes.
+     *
+     * @since 2.0
+     */
+    public int[] getColumnPositionsByIndexes(int... columnIndexes) {
+        return (columnIndexes != null && columnIndexes.length > 0)
+                ? Arrays.stream(columnIndexes).map(this::getColumnPositionByIndex).toArray()
+                : new int[0];
+    }
+
     @Override
     public int localToUnderlyingColumnPosition(int localColumnPosition) {
         if (localColumnPosition < 0 || localColumnPosition >= getColumnCount()) {
             return -1;
         }
 
-        if (getHiddenColumnIndexes().isEmpty()) {
+        if (!hasHiddenColumns()) {
             return localColumnPosition;
         }
 
@@ -168,7 +196,7 @@
 
     @Override
     public int underlyingToLocalColumnPosition(ILayer sourceUnderlyingLayer, int underlyingColumnPosition) {
-        if (getHiddenColumnIndexes().isEmpty()) {
+        if (!hasHiddenColumns()) {
             return underlyingColumnPosition;
         }
 
@@ -285,7 +313,7 @@
             return -1;
         }
 
-        for (int hiddenIndex : getHiddenColumnIndexes()) {
+        for (int hiddenIndex : getHiddenColumnIndexesArray()) {
             int hiddenPosition = underlyingLayer.getColumnPositionByIndex(hiddenIndex);
             // if the hidden position is -1, it is hidden in the underlying
             // layer therefore the underlying layer should handle the
@@ -326,14 +354,46 @@
 
     /**
      * Will collect and return all indexes of the columns that are hidden in
-     * this layer. Note: It is not intended that it also collects the column
-     * indexes of underlying layers. This would cause issues on calculating
-     * positions as every layer is responsible for those calculations itself.
+     * this layer.
+     * <p>
+     * <b>Note:</b> It is not intended that it also collects the column indexes
+     * of underlying layers. This would cause issues on calculating positions,
+     * as every layer is responsible for those calculations itself.
+     * </p>
+     * <p>
+     * Since 2.0 it is recommended to use {@link #getHiddenColumnIndexesArray()}
+     * to avoid unnecessary autoboxing operations.
+     * </p>
      *
      * @return Collection of all column indexes that are hidden in this layer.
      */
     public abstract Collection<Integer> getHiddenColumnIndexes();
 
+    /**
+     * Will collect and return all indexes of the columns that are hidden in
+     * this layer.
+     * <p>
+     * <b>Note:</b> It is not intended that it also collects the column indexes
+     * of underlying layers. This would cause issues on calculating positions,
+     * as every layer is responsible for those calculations itself.
+     * </p>
+     *
+     * @return All column indexes that are hidden in this layer.
+     *
+     * @since 2.0
+     */
+    public abstract int[] getHiddenColumnIndexesArray();
+
+    /**
+     * Check if this layer actively hides columns.
+     *
+     * @return <code>true</code> if columns are hidden by this layer,
+     *         <code>false</code> if not.
+     *
+     * @since 2.0
+     */
+    public abstract boolean hasHiddenColumns();
+
     @Override
     public ILayerCell getCellByPosition(int columnPosition, int rowPosition) {
         ILayerCell cell = super.getCellByPosition(columnPosition, rowPosition);
@@ -366,7 +426,7 @@
         this.cachedVisibleColumnIndexPositionMapping = null;
         this.cachedVisibleColumnPositionIndexMapping = null;
         this.cachedHiddenColumnIndexPositionMapping = null;
-        this.startXCache.clear();
+        this.startXCache = IntIntMaps.mutable.empty();
     }
 
     private synchronized IntIntMap getCachedVisibleColumnIndexPositionMapping() {
@@ -399,10 +459,10 @@
         this.cachedVisibleColumnIndexPositionMapping = IntIntMaps.mutable.empty();
         this.cachedVisibleColumnPositionIndexMapping = IntIntMaps.mutable.empty();
         this.cachedHiddenColumnIndexPositionMapping = IntIntMaps.mutable.empty();
-        this.startXCache.clear();
+        this.startXCache = IntIntMaps.mutable.empty();
 
         // only build up a cache if it is necessary
-        if (!getHiddenColumnIndexes().isEmpty()) {
+        if (hasHiddenColumns()) {
             ILayer underlyingLayer = getUnderlyingLayer();
             int columnPosition = 0;
             for (int parentColumnPosition = 0; parentColumnPosition < underlyingLayer.getColumnCount(); parentColumnPosition++) {
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/AbstractRowHideShowLayer.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/AbstractRowHideShowLayer.java
index 5b7a000..1407524 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/AbstractRowHideShowLayer.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/AbstractRowHideShowLayer.java
@@ -11,6 +11,7 @@
 package org.eclipse.nebula.widgets.nattable.hideshow;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashSet;
 
@@ -38,7 +39,7 @@
     private MutableIntIntMap cachedVisibleRowIndexPositionMapping;
     private MutableIntIntMap cachedVisibleRowPositionIndexMapping;
     private MutableIntIntMap cachedHiddenRowIndexPositionMapping;
-    private final MutableIntIntMap startYCache = IntIntMaps.mutable.empty();
+    private MutableIntIntMap startYCache = IntIntMaps.mutable.empty();
 
     /**
      * Constructor.
@@ -61,7 +62,7 @@
 
     @Override
     public void handleLayerEvent(ILayerEvent event) {
-        if (!getHiddenRowIndexes().isEmpty() && event instanceof RowReorderEvent) {
+        if (hasHiddenRows() && event instanceof RowReorderEvent) {
             // we need to convert the before positions in the event BEFORE the
             // local states are changed, otherwise we are not able to convert
             // the before positions as the changed layer states would return
@@ -105,7 +106,7 @@
         } else if (event instanceof VisualRefreshEvent) {
             // visual change, e.g. font change, the startYCache needs to be
             // cleared in order to re-render correctly
-            this.startYCache.clear();
+            this.startYCache = IntIntMaps.mutable.empty();
         }
         super.handleLayerEvent(event);
     }
@@ -125,7 +126,7 @@
 
     @Override
     public int getRowCount() {
-        if (getHiddenRowIndexes().isEmpty()) {
+        if (!hasHiddenRows()) {
             return super.getRowCount();
         }
         return getCachedVisibleRowIndexPositionMapping().size();
@@ -137,7 +138,7 @@
             return -1;
         }
 
-        if (getHiddenRowIndexes().isEmpty()) {
+        if (!hasHiddenRows()) {
             return super.getRowIndexByPosition(rowPosition);
         }
 
@@ -146,13 +147,24 @@
 
     @Override
     public int getRowPositionByIndex(int rowIndex) {
-        if (getHiddenRowIndexes().isEmpty()) {
+        if (!hasHiddenRows()) {
             return getUnderlyingLayer().getRowPositionByIndex(rowIndex);
         }
 
         return getCachedVisibleRowIndexPositionMapping().getIfAbsent(rowIndex, -1);
     }
 
+    /**
+     * Get the local row positions for the given row indexes.
+     *
+     * @param rowIndexes
+     *            The row indexes for which the local row positions are
+     *            requested.
+     * @return The local row positions for the given row indexes.
+     * @deprecated Use {@link #getRowPositionsByIndexes(int...)} using primitive
+     *             values to avoid unnecessary boxing.
+     */
+    @Deprecated
     public Collection<Integer> getRowPositionsByIndexes(Collection<Integer> rowIndexes) {
         Collection<Integer> rowPositions = new HashSet<>();
         for (int rowIndex : rowIndexes) {
@@ -161,13 +173,29 @@
         return rowPositions;
     }
 
+    /**
+     * Get the local row positions for the given row indexes.
+     *
+     * @param rowIndexes
+     *            The row indexes for which the local row positions are
+     *            requested.
+     * @return The local row positions for the given row indexes.
+     *
+     * @since 2.0
+     */
+    public int[] getRowPositionsByIndexes(int... rowIndexes) {
+        return (rowIndexes != null && rowIndexes.length > 0)
+                ? Arrays.stream(rowIndexes).map(this::getRowPositionByIndex).toArray()
+                : new int[0];
+    }
+
     @Override
     public int localToUnderlyingRowPosition(int localRowPosition) {
         if (localRowPosition < 0 || localRowPosition >= getRowCount()) {
             return -1;
         }
 
-        if (getHiddenRowIndexes().isEmpty()) {
+        if (!hasHiddenRows()) {
             return localRowPosition;
         }
 
@@ -177,7 +205,7 @@
 
     @Override
     public int underlyingToLocalRowPosition(ILayer sourceUnderlyingLayer, int underlyingRowPosition) {
-        if (getHiddenRowIndexes().isEmpty()) {
+        if (!hasHiddenRows()) {
             return underlyingRowPosition;
         }
 
@@ -282,7 +310,7 @@
             return -1;
         }
 
-        for (int hiddenIndex : getHiddenRowIndexes()) {
+        for (int hiddenIndex : getHiddenRowIndexesArray()) {
             int hiddenPosition = underlyingLayer.getRowPositionByIndex(hiddenIndex);
             // if the hidden position is -1, it is hidden in the underlying
             // layer therefore the underlying layer should handle the
@@ -312,14 +340,46 @@
 
     /**
      * Will collect and return all indexes of the rows that are hidden in this
-     * layer. Note: It is not intended that it also collects the row indexes of
-     * underlying layers. This would cause issues on calculating positions as
+     * layer.
+     * <p>
+     * <b>Note:</b> It is not intended that it also collects the row indexes of
+     * underlying layers. This would cause issues on calculating positions, as
      * every layer is responsible for those calculations itself.
+     * </p>
+     * <p>
+     * Since 2.0 it is recommended to use {@link #getHiddenRowIndexesArray()} to
+     * avoid unnecessary autoboxing operations.
+     * </p>
      *
      * @return Collection of all row indexes that are hidden in this layer.
      */
     public abstract Collection<Integer> getHiddenRowIndexes();
 
+    /**
+     * Will collect and return all indexes of the rows that are hidden in this
+     * layer.
+     * <p>
+     * <b>Note:</b> It is not intended that it also collects the row indexes of
+     * underlying layers. This would cause issues on calculating positions, as
+     * every layer is responsible for those calculations itself.
+     * </p>
+     *
+     * @return All row indexes that are hidden in this layer.
+     *
+     * @since 2.0
+     */
+    public abstract int[] getHiddenRowIndexesArray();
+
+    /**
+     * Check if this layer actively hides rows.
+     *
+     * @return <code>true</code> if rows are hidden by this layer,
+     *         <code>false</code> if not.
+     *
+     * @since 2.0
+     */
+    public abstract boolean hasHiddenRows();
+
     @Override
     public ILayerCell getCellByPosition(int columnPosition, int rowPosition) {
         ILayerCell cell = super.getCellByPosition(columnPosition, rowPosition);
@@ -352,7 +412,7 @@
         this.cachedVisibleRowIndexPositionMapping = null;
         this.cachedVisibleRowPositionIndexMapping = null;
         this.cachedHiddenRowIndexPositionMapping = null;
-        this.startYCache.clear();
+        this.startYCache = IntIntMaps.mutable.empty();
     }
 
     private synchronized IntIntMap getCachedVisibleRowIndexPositionMapping() {
@@ -380,13 +440,14 @@
      * Build up the row caches.
      */
     protected synchronized void cacheVisibleRowIndexes() {
+
         this.cachedVisibleRowIndexPositionMapping = IntIntMaps.mutable.empty();
         this.cachedVisibleRowPositionIndexMapping = IntIntMaps.mutable.empty();
         this.cachedHiddenRowIndexPositionMapping = IntIntMaps.mutable.empty();
-        this.startYCache.clear();
+        this.startYCache = IntIntMaps.mutable.empty();
 
         // only build up a cache if it is necessary
-        if (!getHiddenRowIndexes().isEmpty()) {
+        if (hasHiddenRows()) {
             ILayer underlyingLayer = getUnderlyingLayer();
             int rowPosition = 0;
             for (int parentRowPosition = 0; parentRowPosition < underlyingLayer.getRowCount(); parentRowPosition++) {
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/ColumnHideShowLayer.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/ColumnHideShowLayer.java
index 9f7472d..5ed6724 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/ColumnHideShowLayer.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/ColumnHideShowLayer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2019 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -10,17 +10,16 @@
  ******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.hideshow;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
 import java.util.Properties;
-import java.util.Set;
 import java.util.StringTokenizer;
-import java.util.TreeSet;
 import java.util.stream.Collectors;
 
+import org.eclipse.collections.api.list.primitive.MutableIntList;
+import org.eclipse.collections.api.set.primitive.MutableIntSet;
+import org.eclipse.collections.impl.factory.primitive.IntLists;
+import org.eclipse.collections.impl.factory.primitive.IntSets;
 import org.eclipse.nebula.widgets.nattable.hideshow.command.ColumnHideCommandHandler;
 import org.eclipse.nebula.widgets.nattable.hideshow.command.ColumnShowCommandHandler;
 import org.eclipse.nebula.widgets.nattable.hideshow.command.MultiColumnHideCommandHandler;
@@ -50,11 +49,10 @@
 
     public static final String PERSISTENCE_KEY_HIDDEN_COLUMN_INDEXES = ".hiddenColumnIndexes"; //$NON-NLS-1$
 
-    private final Set<Integer> hiddenColumnIndexes;
+    private MutableIntSet hiddenColumnIndexes = IntSets.mutable.empty();
 
     public ColumnHideShowLayer(IUniqueIndexLayer underlyingLayer) {
         super(underlyingLayer);
-        this.hiddenColumnIndexes = new TreeSet<Integer>();
 
         registerCommandHandler(new MultiColumnHideCommandHandler(this));
         registerCommandHandler(new ColumnHideCommandHandler(this));
@@ -72,10 +70,16 @@
 
                 if (columnDiffs != null && !columnDiffs.isEmpty()
                         && !StructuralChangeEventHelper.isReorder(columnDiffs)) {
-                    StructuralChangeEventHelper.handleColumnDelete(columnDiffs,
-                            this.underlyingLayer, this.hiddenColumnIndexes, false);
-                    StructuralChangeEventHelper.handleColumnInsert(columnDiffs,
-                            this.underlyingLayer, this.hiddenColumnIndexes, false);
+                    StructuralChangeEventHelper.handleColumnDelete(
+                            columnDiffs,
+                            this.underlyingLayer,
+                            this.hiddenColumnIndexes,
+                            false);
+                    StructuralChangeEventHelper.handleColumnInsert(
+                            columnDiffs,
+                            this.underlyingLayer,
+                            this.hiddenColumnIndexes,
+                            false);
                 }
             }
         }
@@ -87,14 +91,9 @@
     @Override
     public void saveState(String prefix, Properties properties) {
         if (this.hiddenColumnIndexes.size() > 0) {
-            StringBuilder strBuilder = new StringBuilder();
-            for (Integer index : this.hiddenColumnIndexes) {
-                strBuilder.append(index);
-                strBuilder.append(IPersistable.VALUE_SEPARATOR);
-            }
             properties.setProperty(
                     prefix + PERSISTENCE_KEY_HIDDEN_COLUMN_INDEXES,
-                    strBuilder.toString());
+                    this.hiddenColumnIndexes.toSortedList().makeString(IPersistable.VALUE_SEPARATOR));
         } else {
             properties.remove(prefix + PERSISTENCE_KEY_HIDDEN_COLUMN_INDEXES);
         }
@@ -106,13 +105,13 @@
     public void loadState(String prefix, Properties properties) {
         // Bug 396925: always clear the state of the hidden columns, whether
         // there is a state saved or not
-        this.hiddenColumnIndexes.clear();
+        this.hiddenColumnIndexes = IntSets.mutable.empty();
         String property = properties.getProperty(prefix + PERSISTENCE_KEY_HIDDEN_COLUMN_INDEXES);
         if (property != null) {
             StringTokenizer tok = new StringTokenizer(property, IPersistable.VALUE_SEPARATOR);
             while (tok.hasMoreTokens()) {
                 String index = tok.nextToken();
-                this.hiddenColumnIndexes.add(Integer.valueOf(index));
+                this.hiddenColumnIndexes.add(Integer.parseInt(index));
             }
         }
 
@@ -144,12 +143,22 @@
 
     @Override
     public boolean isColumnIndexHidden(int columnIndex) {
-        return this.hiddenColumnIndexes.contains(Integer.valueOf(columnIndex));
+        return this.hiddenColumnIndexes.contains(columnIndex);
     }
 
     @Override
     public Collection<Integer> getHiddenColumnIndexes() {
-        return this.hiddenColumnIndexes;
+        return this.hiddenColumnIndexes.toSortedList().primitiveStream().boxed().collect(Collectors.toList());
+    }
+
+    @Override
+    public int[] getHiddenColumnIndexesArray() {
+        return this.hiddenColumnIndexes.toSortedArray();
+    }
+
+    @Override
+    public boolean hasHiddenColumns() {
+        return !this.hiddenColumnIndexes.isEmpty();
     }
 
     /**
@@ -159,34 +168,34 @@
      */
     @Override
     public void hideColumnPositions(int... columnPositions) {
-        hideColumnPositions(Arrays.stream(columnPositions).boxed().collect(Collectors.toList()));
+        int[] columnIndexes = Arrays.stream(columnPositions)
+                .map(this::getColumnIndexByPosition)
+                .sorted()
+                .toArray();
+        this.hiddenColumnIndexes.addAll(columnIndexes);
+        invalidateCache();
+        fireLayerEvent(new HideColumnPositionsEvent(this, columnPositions, columnIndexes));
     }
 
     @Override
     public void hideColumnPositions(Collection<Integer> columnPositions) {
-        Set<Integer> columnIndexes = new HashSet<Integer>();
-        for (Integer columnPosition : columnPositions) {
-            columnIndexes.add(getColumnIndexByPosition(columnPosition));
-        }
-        this.hiddenColumnIndexes.addAll(columnIndexes);
-        invalidateCache();
-        fireLayerEvent(new HideColumnPositionsEvent(this, columnPositions, columnIndexes));
+        hideColumnPositions(columnPositions.stream().mapToInt(Integer::intValue).toArray());
     }
 
     @Override
     public void hideColumnIndexes(int... columnIndexes) {
-        hideColumnIndexes(Arrays.stream(columnIndexes).boxed().collect(Collectors.toList()));
+        int[] columnPositions = Arrays.stream(columnIndexes)
+                .map(this::getColumnPositionByIndex)
+                .sorted()
+                .toArray();
+        this.hiddenColumnIndexes.addAll(columnIndexes);
+        invalidateCache();
+        fireLayerEvent(new HideColumnPositionsEvent(this, columnPositions, columnIndexes));
     }
 
     @Override
     public void hideColumnIndexes(Collection<Integer> columnIndexes) {
-        Set<Integer> columnPositions = new HashSet<Integer>();
-        for (Integer columnIndex : columnIndexes) {
-            columnPositions.add(getColumnPositionByIndex(columnIndex));
-        }
-        this.hiddenColumnIndexes.addAll(columnIndexes);
-        invalidateCache();
-        fireLayerEvent(new HideColumnPositionsEvent(this, columnPositions, columnIndexes));
+        hideColumnIndexes(columnIndexes.stream().mapToInt(Integer::intValue).toArray());
     }
 
     /**
@@ -196,25 +205,25 @@
      */
     @Override
     public void showColumnIndexes(int... columnIndexes) {
-        showColumnIndexes(Arrays.stream(columnIndexes).boxed().collect(Collectors.toList()));
-    }
-
-    @Override
-    public void showColumnIndexes(Collection<Integer> columnIndexes) {
-        List<Integer> toProcess = new ArrayList<Integer>(columnIndexes);
+        MutableIntList toProcess = IntLists.mutable.of(columnIndexes);
 
         // only handle column indexes that are hidden
         toProcess.retainAll(this.hiddenColumnIndexes);
 
         this.hiddenColumnIndexes.removeAll(toProcess);
         invalidateCache();
-        Collection<Integer> positions = getColumnPositionsByIndexes(toProcess);
+        int[] positions = getColumnPositionsByIndexes(toProcess.toArray());
         fireLayerEvent(new ShowColumnPositionsEvent(this, positions));
     }
 
     @Override
+    public void showColumnIndexes(Collection<Integer> columnIndexes) {
+        showColumnIndexes(columnIndexes.stream().mapToInt(Integer::intValue).toArray());
+    }
+
+    @Override
     public void showColumnPosition(int columnPosition, boolean showToLeft, boolean showAll) {
-        Set<Integer> columnIndexes = new HashSet<Integer>();
+        MutableIntSet columnIndexes = IntSets.mutable.empty();
         int underlyingPosition = localToUnderlyingColumnPosition(columnPosition);
         if (showToLeft) {
             int leftColumnIndex = this.underlyingLayer.getColumnIndexByPosition(underlyingPosition - 1);
@@ -243,16 +252,16 @@
         }
 
         if (!columnIndexes.isEmpty()) {
-            showColumnIndexes(columnIndexes);
+            showColumnIndexes(columnIndexes.toArray());
         }
     }
 
     @Override
     public void showAllColumns() {
-        Collection<Integer> hiddenColumns = new ArrayList<Integer>(this.hiddenColumnIndexes);
-        this.hiddenColumnIndexes.clear();
+        int[] hidden = this.hiddenColumnIndexes.toSortedArray();
+        this.hiddenColumnIndexes = IntSets.mutable.empty();
         invalidateCache();
-        fireLayerEvent(new ShowColumnPositionsEvent(this, getColumnPositionsByIndexes(hiddenColumns)));
+        fireLayerEvent(new ShowColumnPositionsEvent(this, getColumnPositionsByIndexes(hidden)));
     }
 
     @Override
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/RowHideShowLayer.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/RowHideShowLayer.java
index 2ee7d47..3ede680 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/RowHideShowLayer.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/RowHideShowLayer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2019 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -10,16 +10,16 @@
  ******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.hideshow;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.HashSet;
 import java.util.Properties;
-import java.util.Set;
 import java.util.StringTokenizer;
-import java.util.TreeSet;
 import java.util.stream.Collectors;
 
+import org.eclipse.collections.api.list.primitive.MutableIntList;
+import org.eclipse.collections.api.set.primitive.MutableIntSet;
+import org.eclipse.collections.impl.factory.primitive.IntLists;
+import org.eclipse.collections.impl.factory.primitive.IntSets;
 import org.eclipse.nebula.widgets.nattable.hideshow.command.MultiRowHideCommandHandler;
 import org.eclipse.nebula.widgets.nattable.hideshow.command.MultiRowShowCommandHandler;
 import org.eclipse.nebula.widgets.nattable.hideshow.command.RowHideCommandHandler;
@@ -41,11 +41,10 @@
 
     public static final String PERSISTENCE_KEY_HIDDEN_ROW_INDEXES = ".hiddenRowIndexes"; //$NON-NLS-1$
 
-    private final Set<Integer> hiddenRowIndexes;
+    private MutableIntSet hiddenRowIndexes = IntSets.mutable.empty();
 
     public RowHideShowLayer(IUniqueIndexLayer underlyingLayer) {
         super(underlyingLayer);
-        this.hiddenRowIndexes = new TreeSet<Integer>();
 
         registerCommandHandler(new MultiRowHideCommandHandler(this));
         registerCommandHandler(new RowHideCommandHandler(this));
@@ -84,12 +83,9 @@
     @Override
     public void saveState(String prefix, Properties properties) {
         if (this.hiddenRowIndexes.size() > 0) {
-            StringBuilder strBuilder = new StringBuilder();
-            for (Integer index : this.hiddenRowIndexes) {
-                strBuilder.append(index);
-                strBuilder.append(IPersistable.VALUE_SEPARATOR);
-            }
-            properties.setProperty(prefix + PERSISTENCE_KEY_HIDDEN_ROW_INDEXES, strBuilder.toString());
+            properties.setProperty(
+                    prefix + PERSISTENCE_KEY_HIDDEN_ROW_INDEXES,
+                    this.hiddenRowIndexes.toSortedList().makeString(IPersistable.VALUE_SEPARATOR));
         }
 
         super.saveState(prefix, properties);
@@ -97,13 +93,13 @@
 
     @Override
     public void loadState(String prefix, Properties properties) {
-        this.hiddenRowIndexes.clear();
+        this.hiddenRowIndexes = IntSets.mutable.empty();
         String property = properties.getProperty(prefix + PERSISTENCE_KEY_HIDDEN_ROW_INDEXES);
         if (property != null) {
             StringTokenizer tok = new StringTokenizer(property, IPersistable.VALUE_SEPARATOR);
             while (tok.hasMoreTokens()) {
                 String index = tok.nextToken();
-                this.hiddenRowIndexes.add(Integer.valueOf(index));
+                this.hiddenRowIndexes.add(Integer.parseInt(index));
             }
         }
 
@@ -135,62 +131,77 @@
 
     @Override
     public boolean isRowIndexHidden(int rowIndex) {
-        return this.hiddenRowIndexes.contains(Integer.valueOf(rowIndex));
+        return this.hiddenRowIndexes.contains(rowIndex);
     }
 
     @Override
     public Collection<Integer> getHiddenRowIndexes() {
-        return this.hiddenRowIndexes;
+        return this.hiddenRowIndexes.toSortedList().primitiveStream().boxed().collect(Collectors.toList());
+    }
+
+    @Override
+    public int[] getHiddenRowIndexesArray() {
+        return this.hiddenRowIndexes.toSortedArray();
+    }
+
+    @Override
+    public boolean hasHiddenRows() {
+        return !this.hiddenRowIndexes.isEmpty();
     }
 
     @Override
     public void hideRowPositions(int... rowPositions) {
-        hideRowPositions(Arrays.stream(rowPositions).boxed().collect(Collectors.toList()));
+        int[] rowIndexes = Arrays.stream(rowPositions)
+                .map(this::getRowIndexByPosition)
+                .sorted()
+                .toArray();
+        this.hiddenRowIndexes.addAll(rowIndexes);
+        invalidateCache();
+        fireLayerEvent(new HideRowPositionsEvent(this, rowPositions, rowIndexes));
     }
 
     @Override
     public void hideRowPositions(Collection<Integer> rowPositions) {
-        Set<Integer> rowIndexes = new HashSet<Integer>();
-        for (Integer rowPosition : rowPositions) {
-            rowIndexes.add(getRowIndexByPosition(rowPosition));
-        }
-        this.hiddenRowIndexes.addAll(rowIndexes);
-        invalidateCache();
-        fireLayerEvent(new HideRowPositionsEvent(this, rowPositions, rowIndexes));
+        hideRowPositions(rowPositions.stream().mapToInt(Integer::intValue).toArray());
     }
 
     @Override
     public void hideRowIndexes(int... rowIndexes) {
-        hideRowIndexes(Arrays.stream(rowIndexes).boxed().collect(Collectors.toList()));
-    }
-
-    @Override
-    public void hideRowIndexes(Collection<Integer> rowIndexes) {
-        Set<Integer> rowPositions = new HashSet<Integer>();
-        for (Integer rowIndex : rowIndexes) {
-            rowPositions.add(getRowPositionByIndex(rowIndex));
-        }
+        int[] rowPositions = Arrays.stream(rowIndexes)
+                .map(this::getRowPositionByIndex)
+                .sorted()
+                .toArray();
         this.hiddenRowIndexes.addAll(rowIndexes);
         invalidateCache();
         fireLayerEvent(new HideRowPositionsEvent(this, rowPositions, rowIndexes));
     }
 
     @Override
-    public void showRowIndexes(int... rowIndexes) {
-        showRowIndexes(Arrays.stream(rowIndexes).boxed().collect(Collectors.toList()));
+    public void hideRowIndexes(Collection<Integer> rowIndexes) {
+        hideRowIndexes(rowIndexes.stream().mapToInt(Integer::intValue).toArray());
     }
 
     @Override
-    public void showRowIndexes(Collection<Integer> rowIndexes) {
-        this.hiddenRowIndexes.removeAll(rowIndexes);
+    public void showRowIndexes(int... rowIndexes) {
+        MutableIntList toProcess = IntLists.mutable.of(rowIndexes);
+
+        // only handle row indexes that are hidden
+        toProcess.retainAll(this.hiddenRowIndexes);
+
+        this.hiddenRowIndexes.removeAll(toProcess);
         invalidateCache();
-        Collection<Integer> positions = getRowPositionsByIndexes(rowIndexes);
+        int[] positions = getRowPositionsByIndexes(toProcess.toArray());
         fireLayerEvent(new ShowRowPositionsEvent(this, positions));
     }
 
     @Override
+    public void showRowIndexes(Collection<Integer> rowIndexes) {
+        showRowIndexes(rowIndexes.stream().mapToInt(Integer::intValue).toArray());
+    }
+
+    @Override
     public void showRowPosition(int rowPosition, boolean showToTop, boolean showAll) {
-        Set<Integer> rowIndexes = new HashSet<Integer>();
+        MutableIntSet rowIndexes = IntSets.mutable.empty();
         int underlyingPosition = localToUnderlyingRowPosition(rowPosition);
         if (showToTop) {
             int topRowIndex = this.underlyingLayer.getRowIndexByPosition(underlyingPosition - 1);
@@ -219,16 +230,16 @@
         }
 
         if (!rowIndexes.isEmpty()) {
-            showRowIndexes(rowIndexes);
+            showRowIndexes(rowIndexes.toArray());
         }
     }
 
     @Override
     public void showAllRows() {
-        Collection<Integer> hiddenRows = new ArrayList<Integer>(this.hiddenRowIndexes);
-        this.hiddenRowIndexes.clear();
+        int[] hidden = this.hiddenRowIndexes.toSortedArray();
+        this.hiddenRowIndexes = IntSets.mutable.empty();
         invalidateCache();
-        fireLayerEvent(new ShowRowPositionsEvent(this, getRowPositionsByIndexes(hiddenRows)));
+        fireLayerEvent(new ShowRowPositionsEvent(this, getRowPositionsByIndexes(hidden)));
     }
 
     @Override
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/RowIdHideShowLayer.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/RowIdHideShowLayer.java
index ff96452..7c2c477 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/RowIdHideShowLayer.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/RowIdHideShowLayer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2018, 2019 Dirk Fauth.
+ * Copyright (c) 2018, 2020 Dirk Fauth.
 
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
@@ -12,8 +12,6 @@
 package org.eclipse.nebula.widgets.nattable.hideshow;
 
 import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -24,6 +22,8 @@
 import java.util.TreeMap;
 import java.util.stream.Collectors;
 
+import org.eclipse.collections.api.set.primitive.MutableIntSet;
+import org.eclipse.collections.impl.factory.primitive.IntSets;
 import org.eclipse.nebula.widgets.nattable.command.ILayerCommand;
 import org.eclipse.nebula.widgets.nattable.data.IRowDataProvider;
 import org.eclipse.nebula.widgets.nattable.data.IRowIdAccessor;
@@ -192,22 +192,28 @@
 
     @Override
     public Collection<Integer> getHiddenRowIndexes() {
-        Set<Integer> result = new HashSet<Integer>();
-        for (Map.Entry<Serializable, T> entry : this.hiddenRows.entrySet()) {
-            result.add(getRowIndexById(entry.getKey()));
-        }
-        return result;
+        return this.hiddenRows.entrySet().stream()
+                .map(entry -> getRowIndexById(entry.getKey()))
+                .collect(Collectors.toSet());
+    }
+
+    @Override
+    public int[] getHiddenRowIndexesArray() {
+        return this.hiddenRows.entrySet().stream()
+                .mapToInt(entry -> getRowIndexById(entry.getKey()))
+                .sorted()
+                .toArray();
+    }
+
+    @Override
+    public boolean hasHiddenRows() {
+        return !this.hiddenRows.isEmpty();
     }
 
     @Override
     public void hideRowPositions(int... rowPositions) {
-        hideRowPositions(Arrays.stream(rowPositions).boxed().collect(Collectors.toList()));
-    }
-
-    @Override
-    public void hideRowPositions(Collection<Integer> rowPositions) {
         Map<Serializable, T> toHide = new HashMap<Serializable, T>();
-        for (Integer rowPosition : rowPositions) {
+        for (int rowPosition : rowPositions) {
             T rowObject = getRowObjectByPosition(rowPosition);
             toHide.put(this.rowIdAccessor.getRowId(rowObject), rowObject);
         }
@@ -217,30 +223,30 @@
     }
 
     @Override
-    public void hideRowIndexes(int... rowIndexes) {
-        hideRowIndexes(Arrays.stream(rowIndexes).boxed().collect(Collectors.toList()));
+    public void hideRowPositions(Collection<Integer> rowPositions) {
+        hideRowPositions(rowPositions.stream().mapToInt(Integer::intValue).toArray());
     }
 
     @Override
-    public void hideRowIndexes(Collection<Integer> rowIndexes) {
-        Set<Integer> rowPositions = new HashSet<Integer>();
-        for (Integer rowIndex : rowIndexes) {
+    public void hideRowIndexes(int... rowIndexes) {
+        MutableIntSet rowPositions = IntSets.mutable.empty();
+        for (int rowIndex : rowIndexes) {
             rowPositions.add(getRowPositionByIndex(rowIndex));
             T rowObject = getRowObjectByIndex(rowIndex);
             this.hiddenRows.put(this.rowIdAccessor.getRowId(rowObject), rowObject);
         }
         invalidateCache();
-        fireLayerEvent(new HideRowPositionsEvent(this, rowPositions));
+        fireLayerEvent(new HideRowPositionsEvent(this, rowPositions.toArray()));
+    }
+
+    @Override
+    public void hideRowIndexes(Collection<Integer> rowIndexes) {
+        hideRowIndexes(rowIndexes.stream().mapToInt(Integer::intValue).toArray());
     }
 
     @Override
     public void showRowIndexes(int... rowIndexes) {
-        showRowIndexes(Arrays.stream(rowIndexes).boxed().collect(Collectors.toList()));
-    }
-
-    @Override
-    public void showRowIndexes(Collection<Integer> rowIndexes) {
-        for (Integer rowIndex : rowIndexes) {
+        for (int rowIndex : rowIndexes) {
             T rowObject = getRowObjectByIndex(rowIndex);
             this.hiddenRows.remove(this.rowIdAccessor.getRowId(rowObject));
         }
@@ -249,8 +255,13 @@
     }
 
     @Override
+    public void showRowIndexes(Collection<Integer> rowIndexes) {
+        showRowIndexes(rowIndexes.stream().mapToInt(Integer::intValue).toArray());
+    }
+
+    @Override
     public void showRowPosition(int rowPosition, boolean showToTop, boolean showAll) {
-        Set<Integer> rowIndexes = new HashSet<Integer>();
+        MutableIntSet rowIndexes = IntSets.mutable.empty();
         int underlyingPosition = localToUnderlyingRowPosition(rowPosition);
         if (showToTop) {
             int topRowIndex = this.underlyingLayer.getRowIndexByPosition(underlyingPosition - 1);
@@ -279,16 +290,16 @@
         }
 
         if (!rowIndexes.isEmpty()) {
-            showRowIndexes(rowIndexes);
+            showRowIndexes(rowIndexes.toArray());
         }
     }
 
     @Override
     public void showAllRows() {
-        Collection<Integer> hiddenRows = new ArrayList<Integer>(getHiddenRowIndexes());
+        int[] hidden = getHiddenRowIndexesArray();
         this.hiddenRows.clear();
         invalidateCache();
-        fireLayerEvent(new ShowRowPositionsEvent(this, hiddenRows));
+        fireLayerEvent(new ShowRowPositionsEvent(this, hidden));
     }
 
     @Override
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiColumnHideCommandHandler.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiColumnHideCommandHandler.java
index 11ef55a..c79b9bc 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiColumnHideCommandHandler.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiColumnHideCommandHandler.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2017 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -46,7 +46,7 @@
 
     @Override
     protected boolean doCommand(MultiColumnHideCommand command) {
-        this.columnHideShowLayer.hideColumnPositions(command.getColumnPositions());
+        this.columnHideShowLayer.hideColumnPositions(command.getColumnPositionsArray());
         return true;
     }
 
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiColumnShowCommand.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiColumnShowCommand.java
index 6d94b2e..adc6737 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiColumnShowCommand.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiColumnShowCommand.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2013 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -10,8 +10,9 @@
  ******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.hideshow.command;
 
-import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.stream.Collectors;
 
 import org.eclipse.nebula.widgets.nattable.command.AbstractContextFreeCommand;
 
@@ -23,7 +24,7 @@
     /**
      * The indexes of the columns that should be showed again.
      */
-    private final Collection<Integer> columnIndexes;
+    private final int[] columnIndexes;
 
     /**
      *
@@ -31,6 +32,16 @@
      *            The indexes of the columns that should be showed again.
      */
     public MultiColumnShowCommand(Collection<Integer> columnIndexes) {
+        this.columnIndexes = columnIndexes.stream().mapToInt(Integer::intValue).toArray();
+    }
+
+    /**
+     *
+     * @param columnIndexes
+     *            The indexes of the columns that should be showed again.
+     * @since 2.0
+     */
+    public MultiColumnShowCommand(int... columnIndexes) {
         this.columnIndexes = columnIndexes;
     }
 
@@ -39,12 +50,21 @@
      * @return The indexes of the columns that should be showed again.
      */
     public Collection<Integer> getColumnIndexes() {
+        return Arrays.stream(this.columnIndexes).boxed().collect(Collectors.toList());
+    }
+
+    /**
+     *
+     * @return The indexes of the columns that should be showed again.
+     *
+     * @since 2.0
+     */
+    public int[] getColumnIndexesArray() {
         return this.columnIndexes;
     }
 
     @Override
     public MultiColumnShowCommand cloneCommand() {
-        return new MultiColumnShowCommand(new ArrayList<Integer>(
-                this.columnIndexes));
+        return new MultiColumnShowCommand(Arrays.copyOf(this.columnIndexes, this.columnIndexes.length));
     }
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiColumnShowCommandHandler.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiColumnShowCommandHandler.java
index bdf9f8f..e6cd01b 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiColumnShowCommandHandler.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiColumnShowCommandHandler.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2017 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -46,7 +46,7 @@
 
     @Override
     protected boolean doCommand(MultiColumnShowCommand command) {
-        this.columnHideShowLayer.showColumnIndexes(command.getColumnIndexes());
+        this.columnHideShowLayer.showColumnIndexes(command.getColumnIndexesArray());
         return true;
     }
 
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiRowHideCommandHandler.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiRowHideCommandHandler.java
index b347389..209981f 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiRowHideCommandHandler.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiRowHideCommandHandler.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2019 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -36,7 +36,7 @@
 
     @Override
     protected boolean doCommand(MultiRowHideCommand command) {
-        this.rowHideShowLayer.hideRowPositions(command.getRowPositions());
+        this.rowHideShowLayer.hideRowPositions(command.getRowPositionsArray());
         return true;
     }
 
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiRowShowCommand.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiRowShowCommand.java
index 43eaf66..f3fab0a 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiRowShowCommand.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiRowShowCommand.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2013 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -10,8 +10,9 @@
  ******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.hideshow.command;
 
-import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.stream.Collectors;
 
 import org.eclipse.nebula.widgets.nattable.command.AbstractContextFreeCommand;
 
@@ -23,7 +24,7 @@
     /**
      * The indexes of the rows that should be showed again.
      */
-    private final Collection<Integer> rowIndexes;
+    private final int[] rowIndexes;
 
     /**
      *
@@ -31,6 +32,16 @@
      *            The indexes of the rows that should be showed again.
      */
     public MultiRowShowCommand(Collection<Integer> rowIndexes) {
+        this.rowIndexes = rowIndexes.stream().mapToInt(Integer::intValue).toArray();
+    }
+
+    /**
+     *
+     * @param rowIndexes
+     *            The indexes of the rows that should be showed again.
+     * @since 2.0
+     */
+    public MultiRowShowCommand(int... rowIndexes) {
         this.rowIndexes = rowIndexes;
     }
 
@@ -39,11 +50,21 @@
      * @return The indexes of the rows that should be showed again.
      */
     public Collection<Integer> getRowIndexes() {
+        return Arrays.stream(this.rowIndexes).boxed().collect(Collectors.toList());
+    }
+
+    /**
+     *
+     * @return The indexes of the rows that should be showed again.
+     *
+     * @since 2.0
+     */
+    public int[] getRowIndexesArray() {
         return this.rowIndexes;
     }
 
     @Override
     public MultiRowShowCommand cloneCommand() {
-        return new MultiRowShowCommand(new ArrayList<Integer>(this.rowIndexes));
+        return new MultiRowShowCommand(Arrays.copyOf(this.rowIndexes, this.rowIndexes.length));
     }
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiRowShowCommandHandler.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiRowShowCommandHandler.java
index 5fdc578..f1c6c12 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiRowShowCommandHandler.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/MultiRowShowCommandHandler.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2019 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -13,8 +13,7 @@
 import org.eclipse.nebula.widgets.nattable.command.AbstractLayerCommandHandler;
 import org.eclipse.nebula.widgets.nattable.hideshow.IRowHideShowLayer;
 
-public class MultiRowShowCommandHandler extends
-        AbstractLayerCommandHandler<MultiRowShowCommand> {
+public class MultiRowShowCommandHandler extends AbstractLayerCommandHandler<MultiRowShowCommand> {
 
     private final IRowHideShowLayer rowHideShowLayer;
 
@@ -36,7 +35,7 @@
 
     @Override
     protected boolean doCommand(MultiRowShowCommand command) {
-        this.rowHideShowLayer.showRowIndexes(command.getRowIndexes());
+        this.rowHideShowLayer.showRowIndexes(command.getRowIndexesArray());
         return true;
     }
 
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/RowHideCommandHandler.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/RowHideCommandHandler.java
index cf1494f..894ba98 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/RowHideCommandHandler.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/command/RowHideCommandHandler.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2019 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -10,8 +10,6 @@
  ******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.hideshow.command;
 
-import static java.util.Arrays.asList;
-
 import org.eclipse.nebula.widgets.nattable.command.AbstractLayerCommandHandler;
 import org.eclipse.nebula.widgets.nattable.hideshow.IRowHideShowLayer;
 
@@ -37,7 +35,7 @@
 
     @Override
     protected boolean doCommand(RowHideCommand command) {
-        this.rowHideShowLayer.hideRowPositions(asList(command.getRowPosition()));
+        this.rowHideShowLayer.hideRowPositions(command.getRowPosition());
         return true;
     }
 }
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/event/HideColumnPositionsEvent.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/event/HideColumnPositionsEvent.java
index 08b8e7e..777c12d 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/event/HideColumnPositionsEvent.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/event/HideColumnPositionsEvent.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2019 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -29,7 +29,10 @@
      *            The ILayer to which the given column positions match.
      * @param columnPositions
      *            The positions of the columns that have changed.
+     * @deprecated Use {@link #HideColumnPositionsEvent(ILayer, int...)} with
+     *             primitive types to avoid autoboxing.
      */
+    @Deprecated
     public HideColumnPositionsEvent(ILayer layer, Collection<Integer> columnPositions) {
         super(layer, PositionUtil.getRanges(columnPositions));
     }
@@ -41,18 +44,50 @@
      *            The ILayer to which the given column positions match.
      * @param columnPositions
      *            The positions of the columns that have changed.
+     * @since 2.0
+     */
+    public HideColumnPositionsEvent(ILayer layer, int... columnPositions) {
+        super(layer, PositionUtil.getRanges(columnPositions));
+    }
+
+    /**
+     * Creates a new HideColumnPositionsEvent based on the given information.
+     *
+     * @param layer
+     *            The ILayer to which the given column positions match.
+     * @param columnPositions
+     *            The positions of the columns that have changed.
      * @param columnIndexes
      *            The indexes of the columns that have changed.
      *
      * @since 1.6
+     * @deprecated Use {@link #HideColumnPositionsEvent(ILayer, int[], int[])}
+     *             with primitive types to avoid autoboxing.
      */
+    @Deprecated
     public HideColumnPositionsEvent(ILayer layer, Collection<Integer> columnPositions, Collection<Integer> columnIndexes) {
         super(layer, PositionUtil.getRanges(columnPositions), columnIndexes);
     }
 
     /**
+     * Creates a new HideColumnPositionsEvent based on the given information.
+     *
+     * @param layer
+     *            The ILayer to which the given column positions match.
+     * @param columnPositions
+     *            The positions of the columns that have changed.
+     * @param columnIndexes
+     *            The indexes of the columns that have changed.
+     *
+     * @since 2.0
+     */
+    public HideColumnPositionsEvent(ILayer layer, int[] columnPositions, int[] columnIndexes) {
+        super(layer, PositionUtil.getRanges(columnPositions), columnIndexes);
+    }
+
+    /**
      * Clone constructor.
-     * 
+     *
      * @param event
      *            The event to clone.
      */
@@ -68,7 +103,7 @@
     @Override
     public Collection<StructuralDiff> getColumnDiffs() {
         Collection<StructuralDiff> columnDiffs =
-                new ArrayList<StructuralDiff>(getColumnPositionRanges().size());
+                new ArrayList<>(getColumnPositionRanges().size());
 
         for (Range range : getColumnPositionRanges()) {
             StructuralDiff diff = new StructuralDiff(
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/event/HideRowPositionsEvent.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/event/HideRowPositionsEvent.java
index fff4caa..ddef1c0 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/event/HideRowPositionsEvent.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/event/HideRowPositionsEvent.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2019 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -29,7 +29,10 @@
      *            The ILayer to which the given row positions match.
      * @param rowPositions
      *            The positions of the rows that have changed.
+     * @deprecated Use {@link #HideRowPositionsEvent(ILayer, int...)} with
+     *             primitive types to avoid autoboxing.
      */
+    @Deprecated
     public HideRowPositionsEvent(ILayer layer, Collection<Integer> rowPositions) {
         super(layer, PositionUtil.getRanges(rowPositions));
     }
@@ -41,16 +44,48 @@
      *            The ILayer to which the given row positions match.
      * @param rowPositions
      *            The positions of the rows that have changed.
+     * @since 2.0
+     */
+    public HideRowPositionsEvent(ILayer layer, int... rowPositions) {
+        super(layer, PositionUtil.getRanges(rowPositions));
+    }
+
+    /**
+     * Creates a new HideRowPositionsEvent based on the given information.
+     *
+     * @param layer
+     *            The ILayer to which the given row positions match.
+     * @param rowPositions
+     *            The positions of the rows that have changed.
      * @param rowIndexes
      *            The indexes of the rows that have changed.
      *
      * @since 1.6
+     * @deprecated Use {@link #HideRowPositionsEvent(ILayer, int[], int[])} with
+     *             primitive types to avoid autoboxing.
      */
+    @Deprecated
     public HideRowPositionsEvent(ILayer layer, Collection<Integer> rowPositions, Collection<Integer> rowIndexes) {
         super(layer, PositionUtil.getRanges(rowPositions), rowIndexes);
     }
 
     /**
+     * Creates a new HideRowPositionsEvent based on the given information.
+     *
+     * @param layer
+     *            The ILayer to which the given row positions match.
+     * @param rowPositions
+     *            The positions of the rows that have changed.
+     * @param rowIndexes
+     *            The indexes of the rows that have changed.
+     *
+     * @since 2.0
+     */
+    public HideRowPositionsEvent(ILayer layer, int[] rowPositions, int[] rowIndexes) {
+        super(layer, PositionUtil.getRanges(rowPositions), rowIndexes);
+    }
+
+    /**
      * Clone constructor.
      *
      * @param event
@@ -68,7 +103,7 @@
     @Override
     public Collection<StructuralDiff> getRowDiffs() {
         Collection<StructuralDiff> rowDiffs =
-                new ArrayList<StructuralDiff>(getRowPositionRanges().size());
+                new ArrayList<>(getRowPositionRanges().size());
 
         for (Range range : getRowPositionRanges()) {
             StructuralDiff diff = new StructuralDiff(
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/event/ShowColumnPositionsEvent.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/event/ShowColumnPositionsEvent.java
index e2c0a92..3e8913b 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/event/ShowColumnPositionsEvent.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/event/ShowColumnPositionsEvent.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2018 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -21,13 +21,45 @@
 import org.eclipse.nebula.widgets.nattable.layer.event.StructuralDiff;
 import org.eclipse.nebula.widgets.nattable.layer.event.StructuralDiff.DiffTypeEnum;
 
+/**
+ * Structural change event to indicate that columns are made visible again.
+ */
 public class ShowColumnPositionsEvent extends ColumnStructuralChangeEvent {
 
+    /**
+     * Constructor.
+     *
+     * @param layer
+     *            The layer to which the given column positions match.
+     * @param columnPositions
+     *            The column positions that are made visible again.
+     * @deprecated Use {@link #ShowColumnPositionsEvent(ILayer, int...)} with
+     *             primitive types to avoid autoboxing.
+     */
+    @Deprecated
     public ShowColumnPositionsEvent(IUniqueIndexLayer layer, Collection<Integer> columnPositions) {
         super(layer, PositionUtil.getRanges(columnPositions));
     }
 
-    // Copy constructor
+    /**
+     * Constructor.
+     *
+     * @param layer
+     *            The layer to which the given column positions match.
+     * @param columnPositions
+     *            The column positions that are made visible again.
+     * @since 2.0
+     */
+    public ShowColumnPositionsEvent(ILayer layer, int... columnPositions) {
+        super(layer, PositionUtil.getRanges(columnPositions));
+    }
+
+    /**
+     * Clone constructor.
+     *
+     * @param event
+     *            The {@link ShowColumnPositionsEvent} to clone.
+     */
     public ShowColumnPositionsEvent(ShowColumnPositionsEvent event) {
         super(event);
     }
@@ -35,7 +67,7 @@
     @Override
     public Collection<StructuralDiff> getColumnDiffs() {
         Collection<StructuralDiff> columnDiffs =
-                new ArrayList<StructuralDiff>(getColumnPositionRanges().size());
+                new ArrayList<>(getColumnPositionRanges().size());
 
         int offset = 0;
         for (Range range : getColumnPositionRanges()) {
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/event/ShowRowPositionsEvent.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/event/ShowRowPositionsEvent.java
index 7760936..9bd9d37 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/event/ShowRowPositionsEvent.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hideshow/event/ShowRowPositionsEvent.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2018 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -20,13 +20,45 @@
 import org.eclipse.nebula.widgets.nattable.layer.event.StructuralDiff;
 import org.eclipse.nebula.widgets.nattable.layer.event.StructuralDiff.DiffTypeEnum;
 
+/**
+ * Structural change event to indicate that rows are made visible again.
+ */
 public class ShowRowPositionsEvent extends RowStructuralChangeEvent {
 
+    /**
+     * Constructor.
+     *
+     * @param layer
+     *            The layer to which the given row positions match.
+     * @param rowPositions
+     *            The row positions that are made visible again.
+     * @deprecated Use {@link #ShowRowPositionsEvent(ILayer, int...)} with
+     *             primitive types to avoid autoboxing.
+     */
+    @Deprecated
     public ShowRowPositionsEvent(ILayer layer, Collection<Integer> rowPositions) {
         super(layer, PositionUtil.getRanges(rowPositions));
     }
 
-    // Copy constructor
+    /**
+     * Constructor.
+     *
+     * @param layer
+     *            The layer to which the given row positions match.
+     * @param rowPositions
+     *            The row positions that are made visible again.
+     * @since 2.0
+     */
+    public ShowRowPositionsEvent(ILayer layer, int... rowPositions) {
+        super(layer, PositionUtil.getRanges(rowPositions));
+    }
+
+    /**
+     * Clone constructor.
+     *
+     * @param event
+     *            The {@link ShowRowPositionsEvent} to clone.
+     */
     protected ShowRowPositionsEvent(ShowRowPositionsEvent event) {
         super(event);
     }
@@ -34,7 +66,7 @@
     @Override
     public Collection<StructuralDiff> getRowDiffs() {
         Collection<StructuralDiff> rowDiffs =
-                new ArrayList<StructuralDiff>(getRowPositionRanges().size());
+                new ArrayList<>(getRowPositionRanges().size());
 
         int offset = 0;
         for (Range range : getRowPositionRanges()) {
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hierarchical/HierarchicalTreeLayer.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hierarchical/HierarchicalTreeLayer.java
index c957337..3f775e7 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hierarchical/HierarchicalTreeLayer.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/hierarchical/HierarchicalTreeLayer.java
@@ -1,5 +1,5 @@
 /*****************************************************************************
- * Copyright (c) 2018, 2019 Dirk Fauth.
+ * Copyright (c) 2018, 2020 Dirk Fauth.
  *
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
@@ -21,10 +21,14 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.TreeSet;
+import java.util.stream.Collectors;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.eclipse.collections.api.list.primitive.MutableIntList;
+import org.eclipse.collections.api.set.primitive.MutableIntSet;
+import org.eclipse.collections.impl.factory.primitive.IntLists;
+import org.eclipse.collections.impl.factory.primitive.IntSets;
 import org.eclipse.nebula.widgets.nattable.command.ILayerCommand;
 import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
 import org.eclipse.nebula.widgets.nattable.coordinate.PositionCoordinate;
@@ -138,7 +142,7 @@
      * Collection of all row indexes that are hidden if tree nodes are
      * collapsed.
      */
-    private final Set<Integer> hiddenRowIndexes = new TreeSet<Integer>();
+    private MutableIntSet hiddenRowIndexes = IntSets.mutable.empty();
     /**
      * The index of the first column that shows the leaf level.
      */
@@ -436,7 +440,7 @@
                     // we need to increase the column position by 1 to handle
                     // the tree level header
                     return super.doCommand(
-                            new MultiColumnReorderCommand(this, crCommand.getFromColumnPositions(), crCommand.getToColumnPosition() + 1));
+                            new MultiColumnReorderCommand(this, crCommand.getFromColumnPositionsArray(), crCommand.getToColumnPosition() + 1));
                 }
             } else if (command instanceof ColumnHideCommand) {
                 if (isLevelHeaderColumn(((ColumnHideCommand) command).getColumnPosition())) {
@@ -518,12 +522,12 @@
                 this.collapsedNodes.addAll(updatedCollapsedNodes);
 
                 // recalculate hidden rows based on updated collapsed nodes
-                Set<Integer> updatedHiddenRows = new HashSet<Integer>();
-                for (HierarchicalTreeNode node : this.collapsedNodes) {
-                    updatedHiddenRows.addAll(getChildIndexes(node.columnIndex, node.rowIndex));
-                }
-                getHiddenRowIndexes().clear();
-                getHiddenRowIndexes().addAll(updatedHiddenRows);
+                int[] updatedHiddenRows = this.collapsedNodes.stream()
+                        .map(node -> getChildIndexes(node.columnIndex, node.rowIndex))
+                        .flatMapToInt(children -> Arrays.stream(children))
+                        .toArray();
+
+                this.hiddenRowIndexes = IntSets.mutable.of(updatedHiddenRows);
             } else if (structuralChangeEvent.isHorizontalStructureChanged()) {
                 // if the column structure was changed we need to recalculate
                 // the header positions, e.g. on column hide or show
@@ -532,9 +536,9 @@
         } else if (event instanceof SearchEvent) {
             PositionCoordinate coord = ((SearchEvent) event).getCellCoordinate();
             if (coord != null) {
-                Integer foundIndex = coord.getLayer().getRowIndexByPosition(coord.rowPosition);
+                int foundIndex = coord.getLayer().getRowIndexByPosition(coord.rowPosition);
 
-                if (getHiddenRowIndexes().contains(foundIndex)) {
+                if (this.hiddenRowIndexes.contains(foundIndex)) {
                     if (this.expandOnSearch) {
                         // level header positions - 2 because the leaf level is
                         // not collapsible
@@ -551,7 +555,7 @@
                         }
                     } else {
                         // only make the single row visible again
-                        getHiddenRowIndexes().remove(foundIndex);
+                        this.hiddenRowIndexes.remove(foundIndex);
                     }
                 } else {
                     int lvl = getLevelByColumnIndex(coord.getLayer().getColumnIndexByPosition(coord.columnPosition));
@@ -569,7 +573,7 @@
                 }
 
                 invalidateCache();
-                fireLayerEvent(new ShowRowPositionsEvent(this, Arrays.asList(foundIndex)));
+                fireLayerEvent(new ShowRowPositionsEvent(this, foundIndex));
             }
         }
         super.handleLayerEvent(event);
@@ -956,13 +960,22 @@
 
     @Override
     public boolean isRowIndexHidden(int rowIndex) {
-        return this.hiddenRowIndexes.contains(Integer.valueOf(rowIndex))
-                || isHiddenInUnderlyingLayer(rowIndex);
+        return this.hiddenRowIndexes.contains(rowIndex) || isHiddenInUnderlyingLayer(rowIndex);
     }
 
     @Override
     public Collection<Integer> getHiddenRowIndexes() {
-        return this.hiddenRowIndexes;
+        return this.hiddenRowIndexes.toSortedList().primitiveStream().boxed().collect(Collectors.toList());
+    }
+
+    @Override
+    public int[] getHiddenRowIndexesArray() {
+        return this.hiddenRowIndexes.toSortedArray();
+    }
+
+    @Override
+    public boolean hasHiddenRows() {
+        return !this.hiddenRowIndexes.isEmpty();
     }
 
     /**
@@ -1013,7 +1026,7 @@
      *            </p>
      */
     public void expandOrCollapse(int columnIndex, int rowIndex, int toLevel) {
-        List<Integer> toProcess = getChildIndexes(columnIndex, rowIndex);
+        MutableIntList toProcess = IntLists.mutable.of(getChildIndexes(columnIndex, rowIndex)).sortThis();
 
         HierarchicalTreeNode coord = new HierarchicalTreeNode(columnIndex, rowIndex, null);
         if (this.collapsedNodes.contains(coord)) {
@@ -1036,15 +1049,36 @@
                 }
             }
 
-            this.getHiddenRowIndexes().removeAll(toProcess);
+            this.hiddenRowIndexes.removeAll(toProcess);
             invalidateCache();
-            fireLayerEvent(new ShowRowPositionsEvent(this, toProcess));
+            fireLayerEvent(new ShowRowPositionsEvent(
+                    this,
+                    toProcess.primitiveStream()
+                            .map(this::getRowPositionByIndex)
+                            .filter(r -> r >= 0)
+                            .sorted()
+                            .toArray()));
         } else {
+
+            // if the rowPos is negative, it is not visible because of hidden
+            // state in an underlying layer
+            int[] rowPositions = toProcess.primitiveStream()
+                    .map(this::getRowPositionByIndex)
+                    .filter(rowPos -> rowPos >= 0)
+                    .sorted()
+                    .toArray();
+
+            // collect the indexes that where really hidden in this layer
+            int[] hiddenIndexes = Arrays.stream(rowPositions)
+                    .map(this::getRowIndexByPosition)
+                    .sorted()
+                    .toArray();
+
             coord.rowObject = this.underlyingList.get(coord.rowIndex);
             this.collapsedNodes.add(coord);
-            this.getHiddenRowIndexes().addAll(toProcess);
+            this.hiddenRowIndexes.addAll(toProcess);
             invalidateCache();
-            fireLayerEvent(new HideRowPositionsEvent(this, toProcess));
+            fireLayerEvent(new HideRowPositionsEvent(this, rowPositions, hiddenIndexes));
         }
     }
 
@@ -1052,10 +1086,12 @@
      * Collapses all tree nodes.
      */
     public void collapseAll() {
-        List<Integer> rowsToHide = new ArrayList<Integer>();
+        MutableIntList rowsToHide = IntLists.mutable.empty();
 
-        Integer[] nodeColumns = this.nodeColumnMapping.values().toArray(new Integer[0]);
-        Arrays.sort(nodeColumns);
+        int[] nodeColumns = this.nodeColumnMapping.values().stream()
+                .sorted()
+                .mapToInt(Integer::intValue)
+                .toArray();
 
         int columnIndex = nodeColumns[0];
         int columnPosition = getColumnPositionByIndex(columnIndex);
@@ -1086,20 +1122,38 @@
             }
         }
 
-        this.getHiddenRowIndexes().addAll(rowsToHide);
+        // if the rowPos is negative, it is not visible because of hidden
+        // state in an underlying layer
+        int[] rowPositions = rowsToHide.primitiveStream()
+                .map(this::getRowPositionByIndex)
+                .filter(rowPos -> rowPos >= 0)
+                .sorted()
+                .toArray();
+
+        // collect the indexes that where really hidden in this layer
+        int[] hiddenIndexes = Arrays.stream(rowPositions)
+                .map(this::getRowIndexByPosition)
+                .sorted()
+                .toArray();
+
+        this.hiddenRowIndexes.addAll(rowsToHide);
         invalidateCache();
-        fireLayerEvent(new HideRowPositionsEvent(this, rowsToHide));
+        fireLayerEvent(new HideRowPositionsEvent(this, rowPositions, hiddenIndexes));
     }
 
     /**
      * Expands all tree nodes.
      */
     public void expandAll() {
-        List<Integer> rowsToShow = new ArrayList<Integer>(this.hiddenRowIndexes);
-        this.hiddenRowIndexes.clear();
+        MutableIntList rowsToShow = IntLists.mutable.ofAll(this.hiddenRowIndexes);
+        this.hiddenRowIndexes = IntSets.mutable.empty();
         this.collapsedNodes.clear();
         invalidateCache();
-        fireLayerEvent(new ShowRowPositionsEvent(this, rowsToShow));
+        fireLayerEvent(new ShowRowPositionsEvent(this, rowsToShow.primitiveStream()
+                .map(this::getRowPositionByIndex)
+                .filter(r -> r >= 0)
+                .sorted()
+                .toArray()));
     }
 
     /**
@@ -1125,22 +1179,21 @@
         }
 
         // collect all rows of coords that are still collapsed
-        Set<Integer> remain = new HashSet<Integer>();
-        for (HierarchicalTreeNode coord : this.collapsedNodes) {
-            remain.addAll(getChildIndexes(coord.columnIndex, coord.rowIndex));
-        }
+        int[] remain = this.collapsedNodes.stream()
+                .map(node -> getChildIndexes(node.columnIndex, node.rowIndex))
+                .flatMapToInt(children -> Arrays.stream(children))
+                .toArray();
 
         // calculate the indexes that get visible afterwards
-        List<Integer> toProcess = new ArrayList<Integer>(this.hiddenRowIndexes);
+        MutableIntSet toProcess = IntSets.mutable.ofAll(this.hiddenRowIndexes);
         toProcess.removeAll(remain);
 
         // set still collapsed as hidden row indexes
-        this.hiddenRowIndexes.clear();
-        this.hiddenRowIndexes.addAll(remain);
+        this.hiddenRowIndexes = IntSets.mutable.of(remain);
 
         // invalidate cache and fire event for rows that got visible
         invalidateCache();
-        fireLayerEvent(new ShowRowPositionsEvent(this, toProcess));
+        fireLayerEvent(new ShowRowPositionsEvent(this, toProcess.toSortedArray()));
     }
 
     /**
@@ -1152,15 +1205,17 @@
      *            The row index of the node whose children are requested.
      * @return The row indexes for the children of the node at the given
      *         coordinates.
+     *
+     * @since 2.0
      */
-    protected List<Integer> getChildIndexes(int columnIndex, int rowIndex) {
-        List<Integer> children = new ArrayList<Integer>();
+    protected int[] getChildIndexes(int columnIndex, int rowIndex) {
         if (rowIndex >= 0) {
             HierarchicalWrapper rowObject = this.underlyingList.get(rowIndex);
             // find children with same parents and same level object
             int level = getLevelByColumnIndex(columnIndex);
             Object levelObject = rowObject.getObject(level);
 
+            MutableIntList children = IntLists.mutable.empty();
             for (int i = rowIndex + 1; i < this.underlyingList.size(); i++) {
                 HierarchicalWrapper child = this.underlyingList.get(i);
                 // as long as the level objects are the same, we have found a
@@ -1173,8 +1228,9 @@
                     break;
                 }
             }
+            return children.toArray();
         }
-        return children;
+        return new int[0];
     }
 
     /**
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/layer/SizeConfig.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/layer/SizeConfig.java
index 2c3177f..f24caa9 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/layer/SizeConfig.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/layer/SizeConfig.java
@@ -13,15 +13,21 @@
  ******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.layer;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
 import java.util.Properties;
 import java.util.StringTokenizer;
-import java.util.TreeMap;
 
+import org.eclipse.collections.api.iterator.MutableIntIterator;
+import org.eclipse.collections.api.list.primitive.MutableIntList;
+import org.eclipse.collections.api.map.primitive.MutableIntBooleanMap;
+import org.eclipse.collections.api.map.primitive.MutableIntDoubleMap;
+import org.eclipse.collections.api.map.primitive.MutableIntIntMap;
+import org.eclipse.collections.api.tuple.primitive.IntBooleanPair;
+import org.eclipse.collections.api.tuple.primitive.IntDoublePair;
+import org.eclipse.collections.api.tuple.primitive.IntIntPair;
+import org.eclipse.collections.impl.factory.primitive.IntBooleanMaps;
+import org.eclipse.collections.impl.factory.primitive.IntDoubleMaps;
+import org.eclipse.collections.impl.factory.primitive.IntIntMaps;
+import org.eclipse.collections.impl.factory.primitive.IntLists;
 import org.eclipse.nebula.widgets.nattable.persistence.IPersistable;
 
 /**
@@ -69,18 +75,19 @@
      * The global default size of this {@link SizeConfig}.
      */
     protected int defaultSize;
+
     /**
      * Map that contains default sizes per column.
      */
-    protected final Map<Integer, Integer> defaultSizeMap = new TreeMap<Integer, Integer>();
+    protected final MutableIntIntMap defaultSizeMap = IntIntMaps.mutable.empty();
     /**
      * Map that contains sizes per column.
      */
-    protected final Map<Integer, Integer> sizeMap = new TreeMap<Integer, Integer>();
+    protected final MutableIntIntMap sizeMap = IntIntMaps.mutable.empty();
     /**
      * Map that contains the resizable information per row/column.
      */
-    protected final Map<Integer, Boolean> resizablesMap = new TreeMap<Integer, Boolean>();
+    protected final MutableIntBooleanMap resizablesMap = IntBooleanMaps.mutable.empty();
     /**
      * The global resizable information of this {@link SizeConfig}.
      */
@@ -90,11 +97,11 @@
      *
      * @since 1.6
      */
-    protected final Map<Integer, Double> percentageSizeMap = new TreeMap<Integer, Double>();
+    protected final MutableIntDoubleMap percentageSizeMap = IntDoubleMaps.mutable.empty();
     /**
      * Map that contains the percentage sizing information per row/column.
      */
-    protected final Map<Integer, Boolean> percentageSizingMap = new TreeMap<Integer, Boolean>();
+    protected final MutableIntBooleanMap percentageSizingMap = IntBooleanMaps.mutable.empty();
     /**
      * Flag to tell whether the sizing is done for pixel or percentage values.
      */
@@ -107,11 +114,11 @@
      * Map that contains the real pixel size. Will only be used on percentage
      * sizing. This map is not persisted as it will be calculated on resize.
      */
-    protected final Map<Integer, Integer> realSizeMap = new TreeMap<Integer, Integer>();
+    protected final MutableIntIntMap realSizeMap = IntIntMaps.mutable.empty();
     /**
      * Map that contains the cached aggregated sizes.
      */
-    protected final Map<Integer, Integer> aggregatedSizeCacheMap = new HashMap<Integer, Integer>();
+    protected final MutableIntIntMap aggregatedSizeCacheMap = IntIntMaps.mutable.empty();
     /**
      * Flag that indicates if the aggregated size cache is valid or if it needs
      * to get recalculated.
@@ -145,7 +152,7 @@
      *
      * @since 1.6
      */
-    private final Map<Integer, Integer> minSizeMap = new TreeMap<Integer, Integer>();
+    private final MutableIntIntMap minSizeMap = IntIntMaps.mutable.empty();
     /**
      * Flag to configure whether dynamic percentage sized positions should be
      * fixed on any resize or not. This means, if positions are configured for
@@ -180,29 +187,76 @@
     @Override
     public void saveState(String prefix, Properties properties) {
         properties.put(prefix + PERSISTENCE_KEY_DEFAULT_SIZE, String.valueOf(this.defaultSize));
-        saveMap(this.defaultSizeMap, prefix + PERSISTENCE_KEY_DEFAULT_SIZES, properties);
-        saveMap(this.sizeMap, prefix + PERSISTENCE_KEY_SIZES, properties);
+
+        StringBuilder builder = new StringBuilder();
+        if (!this.defaultSizeMap.isEmpty()) {
+            for (IntIntPair pair : this.defaultSizeMap.keyValuesView().toSortedList()) {
+                builder.append(pair.getOne()).append(":").append(pair.getTwo()).append(","); //$NON-NLS-1$//$NON-NLS-2$
+            }
+            properties.setProperty(
+                    prefix + PERSISTENCE_KEY_DEFAULT_SIZES,
+                    builder.toString());
+        }
+
+        if (!this.sizeMap.isEmpty()) {
+            builder = new StringBuilder();
+            for (IntIntPair pair : this.sizeMap.keyValuesView().toSortedList()) {
+                builder.append(pair.getOne()).append(":").append(pair.getTwo()).append(","); //$NON-NLS-1$//$NON-NLS-2$
+            }
+            properties.setProperty(
+                    prefix + PERSISTENCE_KEY_SIZES,
+                    builder.toString());
+
+        }
+
         properties.put(prefix + PERSISTENCE_KEY_RESIZABLE_BY_DEFAULT, String.valueOf(this.resizableByDefault));
-        saveMap(this.resizablesMap, prefix + PERSISTENCE_KEY_RESIZABLE_INDEXES, properties);
+
+        if (!this.resizablesMap.isEmpty()) {
+            builder = new StringBuilder();
+            for (IntBooleanPair pair : this.resizablesMap.keyValuesView().toSortedList()) {
+                builder.append(pair.getOne()).append(":").append(pair.getTwo()).append(","); //$NON-NLS-1$//$NON-NLS-2$
+            }
+            properties.setProperty(
+                    prefix + PERSISTENCE_KEY_RESIZABLE_INDEXES,
+                    builder.toString());
+
+        }
+
         properties.put(prefix + PERSISTENCE_KEY_PERCENTAGE_SIZING, String.valueOf(this.percentageSizing));
-        saveMap(this.percentageSizeMap, prefix + PERSISTENCE_KEY_PERCENTAGE_SIZES, properties);
-        saveMap(this.percentageSizingMap, prefix + PERSISTENCE_KEY_PERCENTAGE_SIZING_INDEXES, properties);
+
+        if (!this.percentageSizeMap.isEmpty()) {
+            builder = new StringBuilder();
+            for (IntDoublePair pair : this.percentageSizeMap.keyValuesView().toSortedList()) {
+                builder.append(pair.getOne()).append(":").append(pair.getTwo()).append(","); //$NON-NLS-1$//$NON-NLS-2$
+            }
+            properties.setProperty(
+                    prefix + PERSISTENCE_KEY_PERCENTAGE_SIZES,
+                    builder.toString());
+        }
+
+        if (!this.percentageSizingMap.isEmpty()) {
+            builder = new StringBuilder();
+            for (IntBooleanPair pair : this.percentageSizingMap.keyValuesView().toSortedList()) {
+                builder.append(pair.getOne()).append(":").append(pair.getTwo()).append(","); //$NON-NLS-1$//$NON-NLS-2$
+            }
+            properties.setProperty(
+                    prefix + PERSISTENCE_KEY_PERCENTAGE_SIZING_INDEXES,
+                    builder.toString());
+        }
+
         properties.put(prefix + PERSISTENCE_KEY_DISTRIBUTE_REMAINING_SPACE, String.valueOf(this.distributeRemainingSpace));
         properties.put(prefix + PERSISTENCE_KEY_DEFAULT_MIN_SIZE, String.valueOf(this.defaultMinSize));
-        saveMap(this.minSizeMap, prefix + PERSISTENCE_KEY_MIN_SIZES, properties);
-    }
 
-    private void saveMap(Map<Integer, ?> map, String key, Properties properties) {
-        if (map.size() > 0) {
-            StringBuilder strBuilder = new StringBuilder();
-            for (Integer index : map.keySet()) {
-                strBuilder.append(index);
-                strBuilder.append(':');
-                strBuilder.append(map.get(index));
-                strBuilder.append(',');
+        if (!this.minSizeMap.isEmpty()) {
+            builder = new StringBuilder();
+            for (IntIntPair pair : this.minSizeMap.keyValuesView().toSortedList()) {
+                builder.append(pair.getOne()).append(":").append(pair.getTwo()).append(","); //$NON-NLS-1$//$NON-NLS-2$
             }
-            properties.setProperty(key, strBuilder.toString());
+            properties.setProperty(
+                    prefix + PERSISTENCE_KEY_MIN_SIZES,
+                    builder.toString());
         }
+
     }
 
     @Override
@@ -224,27 +278,27 @@
 
         String persistedDefaultSize = properties.getProperty(prefix + PERSISTENCE_KEY_DEFAULT_SIZE);
         if (persistedDefaultSize != null && persistedDefaultSize.length() > 0) {
-            this.defaultSize = Integer.valueOf(persistedDefaultSize);
+            this.defaultSize = Integer.parseInt(persistedDefaultSize);
         }
 
         String persistedResizableDefault = properties.getProperty(prefix + PERSISTENCE_KEY_RESIZABLE_BY_DEFAULT);
         if (persistedResizableDefault != null && persistedResizableDefault.length() > 0) {
-            this.resizableByDefault = Boolean.valueOf(persistedResizableDefault);
+            this.resizableByDefault = Boolean.parseBoolean(persistedResizableDefault);
         }
 
         String persistedPercentageSizing = properties.getProperty(prefix + PERSISTENCE_KEY_PERCENTAGE_SIZING);
         if (persistedPercentageSizing != null && persistedPercentageSizing.length() > 0) {
-            this.percentageSizing = Boolean.valueOf(persistedPercentageSizing);
+            this.percentageSizing = Boolean.parseBoolean(persistedPercentageSizing);
         }
 
         String persistedDistributeRemainingSpace = properties.getProperty(prefix + PERSISTENCE_KEY_DISTRIBUTE_REMAINING_SPACE);
         if (persistedDistributeRemainingSpace != null && persistedDistributeRemainingSpace.length() > 0) {
-            this.distributeRemainingSpace = Boolean.valueOf(persistedDistributeRemainingSpace);
+            this.distributeRemainingSpace = Boolean.parseBoolean(persistedDistributeRemainingSpace);
         }
 
         String persistedDefaultMinSize = properties.getProperty(prefix + PERSISTENCE_KEY_DEFAULT_MIN_SIZE);
         if (persistedDefaultMinSize != null && persistedDefaultMinSize.length() > 0) {
-            this.defaultMinSize = Integer.valueOf(persistedDefaultMinSize);
+            this.defaultMinSize = Integer.parseInt(persistedDefaultMinSize);
         }
 
         loadBooleanMap(prefix + PERSISTENCE_KEY_RESIZABLE_INDEXES, properties, this.resizablesMap);
@@ -258,7 +312,7 @@
         calculatePercentages(this.availableSpace, this.realSizeMap.size());
     }
 
-    private void loadIntegerMap(String key, Properties properties, Map<Integer, Integer> map) {
+    private void loadIntegerMap(String key, Properties properties, MutableIntIntMap map) {
         String property = properties.getProperty(key);
         if (property != null) {
             map.clear();
@@ -267,26 +321,13 @@
             while (tok.hasMoreTokens()) {
                 String token = tok.nextToken();
                 int separatorIndex = token.indexOf(':');
-                map.put(Integer.valueOf(token.substring(0, separatorIndex)),
-                        Integer.valueOf(token.substring(separatorIndex + 1)));
+                map.put(Integer.parseInt(token.substring(0, separatorIndex)),
+                        Integer.parseInt(token.substring(separatorIndex + 1)));
             }
         }
     }
 
-    private void loadBooleanMap(String key, Properties properties, Map<Integer, Boolean> map) {
-        String property = properties.getProperty(key);
-        if (property != null) {
-            StringTokenizer tok = new StringTokenizer(property, ","); //$NON-NLS-1$
-            while (tok.hasMoreTokens()) {
-                String token = tok.nextToken();
-                int separatorIndex = token.indexOf(':');
-                map.put(Integer.valueOf(token.substring(0, separatorIndex)),
-                        Boolean.valueOf(token.substring(separatorIndex + 1)));
-            }
-        }
-    }
-
-    private void loadDoubleMap(String key, Properties properties, Map<Integer, Double> map) {
+    private void loadBooleanMap(String key, Properties properties, MutableIntBooleanMap map) {
         String property = properties.getProperty(key);
         if (property != null) {
             map.clear();
@@ -295,8 +336,23 @@
             while (tok.hasMoreTokens()) {
                 String token = tok.nextToken();
                 int separatorIndex = token.indexOf(':');
-                map.put(Integer.valueOf(token.substring(0, separatorIndex)),
-                        Double.valueOf(token.substring(separatorIndex + 1)));
+                map.put(Integer.parseInt(token.substring(0, separatorIndex)),
+                        Boolean.parseBoolean(token.substring(separatorIndex + 1)));
+            }
+        }
+    }
+
+    private void loadDoubleMap(String key, Properties properties, MutableIntDoubleMap map) {
+        String property = properties.getProperty(key);
+        if (property != null) {
+            map.clear();
+
+            StringTokenizer tok = new StringTokenizer(property, ","); //$NON-NLS-1$
+            while (tok.hasMoreTokens()) {
+                String token = tok.nextToken();
+                int separatorIndex = token.indexOf(':');
+                map.put(Integer.parseInt(token.substring(0, separatorIndex)),
+                        Double.parseDouble(token.substring(separatorIndex + 1)));
             }
         }
     }
@@ -335,9 +391,9 @@
     }
 
     private int getDefaultSize(int position) {
-        Integer size = this.defaultSizeMap.get(position);
-        if (size != null) {
-            return size.intValue();
+        int size = this.defaultSizeMap.getIfAbsent(position, -1);
+        if (size != -1) {
+            return size;
         } else {
             return this.defaultSize;
         }
@@ -371,10 +427,10 @@
     }
 
     public int getSize(int position) {
-        Integer size = null;
+        int size = -1;
         if (isPercentageSizing()) {
-            Integer value = this.realSizeMap.get(position);
-            if (value != null) {
+            int value = this.realSizeMap.getIfAbsent(position, -1);
+            if (value != -1) {
                 return value;
             }
         } else {
@@ -382,8 +438,8 @@
                 size = this.sizeMap.get(position);
             }
         }
-        if (size != null) {
-            return upScale(size.intValue());
+        if (size != -1) {
+            return upScale(size);
         } else {
             return upScale(getDefaultSize(position));
         }
@@ -512,8 +568,8 @@
             // check whether the given value should be remembered as is or if it
             // needs to be calculated
             if (!isPercentageSizing(position)) {
-                Integer oldValue = this.sizeMap.get(position);
-                int diff = (oldValue != null) ? size - oldValue : size - getDefaultSize(position);
+                int oldValue = this.sizeMap.getIfAbsent(position, -1);
+                int diff = (oldValue != -1) ? size - oldValue : size - getDefaultSize(position);
                 this.sizeMap.put(position, size);
 
                 // if percentage sizing is enabled and percentage values should
@@ -542,9 +598,9 @@
                     fixPercentageValues(percentageSpace);
                 }
 
-                Double oldValue = this.percentageSizeMap.get(position);
+                double oldValue = this.percentageSizeMap.getIfAbsent(position, -1);
                 double diff = percentage;
-                if (oldValue != null && !minSizeUpdate) {
+                if (oldValue != -1 && !minSizeUpdate) {
                     diff = diff - oldValue;
                 } else if (this.realSizeMap.containsKey(position)) {
                     // there was no percentage value before
@@ -558,14 +614,14 @@
                 // if a min size is configured and the size is set to a lower
                 // value via resize, the min size needs to be adjusted
                 if (minSizeUpdate) {
-                    for (Map.Entry<Integer, Double> entry : this.percentageSizeMap.entrySet()) {
-                        if (entry.getKey() != position && isPercentageSizing(entry.getKey())) {
-                            double calculated = ((double) this.realSizeMap.get(entry.getKey()) * 100) / percentageSpace;
-                            if (calculated < entry.getValue()) {
-                                this.percentageSizeMap.put(entry.getKey(), calculated);
+                    this.percentageSizeMap.forEachKeyValue((key, value) -> {
+                        if (key != position && isPercentageSizing(key)) {
+                            double calculated = ((double) this.realSizeMap.get(key) * 100) / percentageSpace;
+                            if (calculated < value) {
+                                this.percentageSizeMap.put(key, calculated);
                             }
                         }
-                    }
+                    });
                     setMinSize(position, size);
                 }
 
@@ -574,7 +630,7 @@
                 // check the adjacent positions for percentage corrections
                 diff = updateAdjacent(position, diff, percentageSpace);
 
-                if (diff != 0 && oldValue == null) {
+                if (diff != 0 && oldValue == -1) {
                     // if the diff is not 0 and there was no size value set
                     // before we will remove the prior set value again
                     // this is because the position was configured as the
@@ -591,13 +647,12 @@
     }
 
     private void fixPercentageValues(int percentageSpace) {
-        for (Map.Entry<Integer, Integer> entry : this.realSizeMap.entrySet()) {
-            int pos = entry.getKey();
+        this.realSizeMap.forEachKeyValue((pos, value) -> {
             if (isPercentageSizing(pos)) {
                 if (!this.percentageSizeMap.containsKey(pos)) {
                     // position is configured for percentage sizing
                     // but has no fixed percentage value
-                    double calculatedPercentage = ((double) entry.getValue() * 100) / percentageSpace;
+                    double calculatedPercentage = ((double) value * 100) / percentageSpace;
                     this.percentageSizeMap.put(pos, calculatedPercentage);
                 } else {
                     // we have a fixed percentage value
@@ -615,7 +670,7 @@
                     }
                 }
             }
-        }
+        });
     }
 
     /**
@@ -745,7 +800,7 @@
             throw new IllegalArgumentException("percentage < 0"); //$NON-NLS-1$
         }
         if (isPositionResizable(position)) {
-            this.percentageSizingMap.put(position, Boolean.TRUE);
+            this.percentageSizingMap.put(position, true);
             this.percentageSizeMap.put(position, percentage);
             this.realSizeMap.put(position, calculatePercentageValue(percentage, this.availableSpace));
             calculatePercentages(this.availableSpace, this.realSizeMap.size());
@@ -772,11 +827,7 @@
      *         <code>false</code> if not.
      */
     public boolean isPositionResizable(int position) {
-        Boolean resizable = this.resizablesMap.get(position);
-        if (resizable != null) {
-            return resizable.booleanValue();
-        }
-        return this.resizableByDefault;
+        return this.resizablesMap.getIfAbsent(position, this.resizableByDefault);
     }
 
     /**
@@ -819,10 +870,8 @@
      */
     public boolean isPercentageSizing() {
         if (!this.percentageSizingMap.isEmpty()) {
-            for (Boolean pSize : this.percentageSizingMap.values()) {
-                if (pSize) {
-                    return true;
-                }
+            if (this.percentageSizingMap.containsValue(true)) {
+                return true;
             }
         }
         return this.percentageSizing;
@@ -863,11 +912,7 @@
      *         percentage value, <code>false</code> if not.
      */
     public boolean isPercentageSizing(int position) {
-        Boolean percentageSizing = this.percentageSizingMap.get(position);
-        if (percentageSizing != null) {
-            return percentageSizing;
-        }
-        return this.percentageSizing;
+        return this.percentageSizingMap.getIfAbsent(position, this.percentageSizing);
     }
 
     /**
@@ -908,23 +953,23 @@
             int realSum = 0;
             int fixedSum = 0;
             int minSizeIncrease = 0;
-            List<Integer> noInfoPositions = new ArrayList<Integer>();
-            List<Integer> fixedPercentagePositions = new ArrayList<Integer>();
-            Integer positionValue = null;
-            Double positionPercentageValue = null;
+            MutableIntList noInfoPositions = IntLists.mutable.empty();
+            MutableIntList fixedPercentagePositions = IntLists.mutable.empty();
+            int positionValue = -1;
+            double positionPercentageValue = -1;
             for (int i = 0; i < positionCount; i++) {
-                positionValue = this.sizeMap.get(i);
-                positionPercentageValue = this.percentageSizeMap.get(i);
-                if (positionPercentageValue == null && isPercentageSizing(i)) {
+                positionValue = this.sizeMap.getIfAbsent(i, -1);
+                positionPercentageValue = this.percentageSizeMap.getIfAbsent(i, Double.valueOf("-1")); //$NON-NLS-1$
+                if (positionPercentageValue == -1 && isPercentageSizing(i)) {
                     // remember the position for which no size information
                     // exists needed to calculate the size for those positions
                     // dependent on the remaining space
                     noInfoPositions.add(i);
-                } else if (positionValue == null && !isPercentageSizing(i)) {
+                } else if (positionValue == -1 && !isPercentageSizing(i)) {
                     positionValue = getDefaultSize(i);
                 }
 
-                if (positionPercentageValue != null && isPercentageSizing(i)) {
+                if (positionPercentageValue != -1 && isPercentageSizing(i)) {
                     real = calculatePercentageValue(positionPercentageValue, percentageSpace);
                     int minSize = getMinSize(i);
                     if (real < minSize) {
@@ -939,7 +984,7 @@
                     fixedPercentagePositions.add(i);
                     realSum += real;
                     this.realSizeMap.put(i, real);
-                } else if (positionValue != null) {
+                } else if (positionValue != -1) {
                     real = upScale(positionValue);
                     fixedSum += real;
                     realSum += real;
@@ -957,9 +1002,9 @@
             // space is bigger than the available space, check if there is a
             // percentage sized column without min size that needs to be reduced
             if (!fixedPercentagePositions.isEmpty() && realSum > space) {
-                List<Integer> noMinWidth = new ArrayList<Integer>();
+                MutableIntList noMinWidth = IntLists.mutable.empty();
                 int sumMod = 0;
-                for (Iterator<Integer> it = fixedPercentagePositions.iterator(); it.hasNext();) {
+                for (MutableIntIterator it = fixedPercentagePositions.intIterator(); it.hasNext();) {
                     int pos = it.next();
                     if (this.realSizeMap.get(pos) == getMinSize(pos)) {
                         sumMod += this.percentageSizeMap.get(pos);
@@ -968,8 +1013,8 @@
                         noMinWidth.add(pos);
                     }
                 }
-                for (int pos : noMinWidth) {
-                    Double percentage = this.percentageSizeMap.get(pos);
+                for (int pos : noMinWidth.toArray()) {
+                    double percentage = this.percentageSizeMap.get(pos);
                     double ratio = percentage / sum;
                     int dist = (int) Math.round(minSizeIncrease * ratio);
                     int newValue = this.realSizeMap.get(pos) - dist;
@@ -986,9 +1031,9 @@
             // according to the min size that gets applied
             if (!noInfoPositions.isEmpty() && isMinSizeConfigured()) {
                 double remaining = Double.valueOf(space - realSum);
-                Double remainingColSpace = remaining / noInfoPositions.size();
+                double remainingColSpace = remaining / noInfoPositions.size();
 
-                for (Iterator<Integer> it = noInfoPositions.iterator(); it.hasNext();) {
+                for (MutableIntIterator it = noInfoPositions.intIterator(); it.hasNext();) {
                     int position = it.next();
                     int minSize = getMinSize(position);
                     if (minSize > remainingColSpace) {
@@ -1004,16 +1049,16 @@
                 // caused an increase of the real sum, we need to correct the
                 // other positions
                 if (realSum > space && !fixedPercentagePositions.isEmpty()) {
-                    List<Integer> noMinWidth = new ArrayList<Integer>();
-                    for (Iterator<Integer> it = fixedPercentagePositions.iterator(); it.hasNext();) {
+                    MutableIntList noMinWidth = IntLists.mutable.empty();
+                    for (MutableIntIterator it = fixedPercentagePositions.intIterator(); it.hasNext();) {
                         int pos = it.next();
                         if (this.realSizeMap.get(pos) != getMinSize(pos)) {
                             noMinWidth.add(pos);
                         }
                     }
                     int exceed = realSum - space;
-                    for (int pos : noMinWidth) {
-                        Double percentage = this.percentageSizeMap.get(pos);
+                    for (int pos : noMinWidth.toArray()) {
+                        double percentage = this.percentageSizeMap.get(pos);
                         double ratio = percentage / sum;
                         int dist = (int) Math.round(exceed * ratio);
                         int newValue = this.realSizeMap.get(pos) - dist;
@@ -1027,11 +1072,11 @@
             if (!noInfoPositions.isEmpty()) {
                 // now calculate the size for the remaining columns
                 double remaining = Double.valueOf(space - realSum);
-                Double remainingColSpace = remaining / noInfoPositions.size();
-                for (Integer position : noInfoPositions) {
+                double remainingColSpace = remaining / noInfoPositions.size();
+                for (int position : noInfoPositions.toArray()) {
                     sum += (remainingColSpace / space) * 100;
                     int minSize = getMinSize(position);
-                    this.realSizeMap.put(position, remainingColSpace < minSize ? minSize : remainingColSpace.intValue());
+                    this.realSizeMap.put(position, remainingColSpace < minSize ? minSize : Double.valueOf(remainingColSpace).intValue());
                 }
 
                 // If there are positions for which no size information exist,
@@ -1051,14 +1096,14 @@
                 double remaining = Double.valueOf(space - realSum);
                 if (remaining > 0) {
                     // calculate sum of eligible fixed percentage positions
-                    double eligibleSum = 0;
-                    for (int pos : fixedPercentagePositions) {
-                        eligibleSum += this.percentageSizeMap.get(pos);
-                    }
+                    double eligibleSum = fixedPercentagePositions.primitiveStream()
+                            .mapToDouble(pos -> this.percentageSizeMap.get(pos))
+                            .sum();
+
                     // calculate ratio
-                    for (int pos : fixedPercentagePositions) {
+                    for (int pos : fixedPercentagePositions.toArray()) {
                         if (getMinSize(pos) != this.realSizeMap.get(pos)) {
-                            Double percentage = this.percentageSizeMap.get(pos);
+                            double percentage = this.percentageSizeMap.get(pos);
                             double ratio = percentage / eligibleSum;
                             int dist = (int) (remaining * ratio);
                             this.realSizeMap.put(pos, this.realSizeMap.get(pos) + dist);
@@ -1073,12 +1118,7 @@
                 // given space if not distribute the missing pixels to some of
                 // the other columns. this is needed because of rounding issues
                 // on 100% with odd-numbered pixel values
-                int valueSum = 0;
-                int lastPos = -1;
-                for (Map.Entry<Integer, Integer> entry : this.realSizeMap.entrySet()) {
-                    valueSum += entry.getValue();
-                    lastPos = Math.max(lastPos, entry.getKey());
-                }
+                int valueSum = (int) this.realSizeMap.values().sum();
 
                 if (space > 0 && valueSum < space) {
                     // distribute the missing pixels
@@ -1093,13 +1133,13 @@
 
                         // only increase columns that are not configured to
                         // be hidden or fixed size
-                        Integer posValue = this.realSizeMap.get(pos);
-                        while (posValue != null && (posValue == 0 || !isPercentageSizing(pos) || getMinSize(pos) == posValue)) {
+                        int posValue = this.realSizeMap.getIfAbsent(pos, -1);
+                        while (posValue != -1 && (posValue == 0 || !isPercentageSizing(pos) || getMinSize(pos) == posValue)) {
                             pos++;
                             posValue = this.realSizeMap.get(pos);
                         }
 
-                        if (posValue != null) {
+                        if (posValue != -1) {
                             this.realSizeMap.put(pos, posValue + 1);
                             pos++;
                         }
@@ -1152,11 +1192,12 @@
      */
     protected int calculateAvailableSpace(int space) {
         if (!this.percentageSizingMap.isEmpty() && this.percentageSizing) {
-            for (Map.Entry<Integer, Boolean> entry : this.percentageSizingMap.entrySet()) {
-                if (!entry.getValue() && this.sizeMap.containsKey(entry.getKey())) {
-                    space -= upScale(this.sizeMap.get(entry.getKey()));
-                }
-            }
+            long toReduce = this.percentageSizingMap
+                    .select((key, value) -> (!value && this.sizeMap.containsKey(key)))
+                    .keySet()
+                    .collectInt(key -> upScale(this.sizeMap.get(key)), IntLists.mutable.empty())
+                    .sum();
+            space -= toReduce;
         }
         return space;
     }
@@ -1199,17 +1240,17 @@
      * @since 1.6
      */
     protected int[] correctPercentageValues(double sum, int positionCount) {
-        Map<Integer, Integer> toModify = new TreeMap<Integer, Integer>();
+        MutableIntIntMap toModify = IntIntMaps.mutable.empty();
         int fixedSum = 0;
         double modifySum = 0;
         for (int i = 0; i < positionCount; i++) {
-            Integer positionValue = this.sizeMap.get(i);
-            Double positionPercentageValue = this.percentageSizeMap.get(i);
-            if (positionPercentageValue != null && isPercentageSizing(i)
+            int positionValue = this.sizeMap.getIfAbsent(i, -1);
+            double positionPercentageValue = this.percentageSizeMap.getIfAbsent(i, Double.valueOf("-1")); //$NON-NLS-1$
+            if (positionPercentageValue != -1 && isPercentageSizing(i)
                     && (!isMinSizeConfigured(i) || (isMinSizeConfigured(i) && this.realSizeMap.get(i) != getMinSize(i)))) {
                 toModify.put(i, this.realSizeMap.get(i));
                 modifySum += positionPercentageValue;
-            } else if (!isPercentageSizing(i) && positionValue != null) {
+            } else if (!isPercentageSizing(i) && positionValue != -1) {
                 fixedSum += positionValue;
             }
         }
@@ -1225,21 +1266,21 @@
             double newPercentageSum = 0;
             int realSum = 0;
 
-            for (Map.Entry<Integer, Integer> mod : toModify.entrySet()) {
-                double ratio = this.percentageSizeMap.get(mod.getKey()) / modifySum;
+            for (IntIntPair mod : toModify.keyValuesView().toSortedList()) {
+                double ratio = this.percentageSizeMap.get(mod.getOne()) / modifySum;
                 int exc = (int) Math.ceil(excessPixel * ratio);
 
-                int newValue = mod.getValue() - exc;
+                int newValue = mod.getTwo() - exc;
 
-                if (isMinSizeConfigured(mod.getKey()) && newValue < getMinSize(mod.getKey())) {
-                    newValue = getMinSize(mod.getKey());
+                if (isMinSizeConfigured(mod.getOne()) && newValue < getMinSize(mod.getOne())) {
+                    newValue = getMinSize(mod.getOne());
                 }
 
                 double newPercentage = (Double.valueOf(newValue) / Double.valueOf(this.availableSpace - fixedSum)) * 100;
                 newPercentageSum += newPercentage;
 
                 realSum += newValue;
-                this.realSizeMap.put(mod.getKey(), newValue);
+                this.realSizeMap.put(mod.getOne(), newValue);
             }
 
             // if there are no excessPixels but the sum is greater than 100, we
@@ -1264,10 +1305,10 @@
         int resizedColumns = 0;
 
         boolean percentageSizing = isPercentageSizing();
-        Map<Integer, Integer> mapToUse = percentageSizing ? this.realSizeMap : this.sizeMap;
+        MutableIntIntMap mapToUse = percentageSizing ? this.realSizeMap : this.sizeMap;
 
-        for (Integer resizedPosition : mapToUse.keySet()) {
-            if (resizedPosition.intValue() < position) {
+        for (int resizedPosition : mapToUse.keySet().toSortedArray()) {
+            if (resizedPosition < position) {
                 resizedColumns++;
                 int size = mapToUse.get(resizedPosition);
                 resizeAggregate += percentageSizing ? size : upScale(size);
@@ -1277,8 +1318,8 @@
         }
 
         // also take into account the default size configuration per position
-        for (Integer defaultPosition : this.defaultSizeMap.keySet()) {
-            if (defaultPosition.intValue() < position) {
+        for (int defaultPosition : this.defaultSizeMap.keySet().toSortedArray()) {
+            if (defaultPosition < position) {
                 if (!mapToUse.containsKey(defaultPosition)) {
                     resizedColumns++;
                     int size = this.defaultSizeMap.get(defaultPosition);
@@ -1294,16 +1335,15 @@
         return result;
     }
 
-    private int correctExtend(int extend, List<Integer> fixedPercentagePositions) {
+    private int correctExtend(int extend, MutableIntList fixedPercentagePositions) {
         int remainingExtend = extend;
-        double eligibleSum = 0;
-        for (int pos : fixedPercentagePositions) {
-            eligibleSum += this.percentageSizeMap.get(pos);
-        }
+        double eligibleSum = fixedPercentagePositions.primitiveStream()
+                .mapToDouble(pos -> this.percentageSizeMap.get(pos))
+                .sum();
         // calculate ratio
-        for (int pos : fixedPercentagePositions) {
+        for (int pos : fixedPercentagePositions.toArray()) {
             if (remainingExtend > 0 && getMinSize(pos) != this.realSizeMap.get(pos)) {
-                Double percentage = this.percentageSizeMap.get(pos);
+                double percentage = this.percentageSizeMap.get(pos);
                 double ratio = percentage / eligibleSum;
                 int dist = extend == 1 ? 1 : (int) (extend * ratio);
                 int oldValue = this.realSizeMap.get(pos);
@@ -1450,8 +1490,7 @@
      * @since 1.6
      */
     public int getConfiguredSize(int position) {
-        Integer configuredSize = this.sizeMap.get(position);
-        return (configuredSize != null) ? configuredSize : -1;
+        return this.sizeMap.getIfAbsent(position, -1);
     }
 
     /**
@@ -1467,8 +1506,7 @@
      * @since 1.6
      */
     public double getConfiguredPercentageSize(int position) {
-        Double configuredSize = this.percentageSizeMap.get(position);
-        return (configuredSize != null) ? configuredSize : -1;
+        return this.percentageSizeMap.getIfAbsent(position, Double.valueOf("-1")); //$NON-NLS-1$
     }
 
     /**
@@ -1485,8 +1523,7 @@
      * @since 1.6
      */
     public int getConfiguredMinSize(int position) {
-        Integer configuredMinSize = this.minSizeMap.get(position);
-        return (configuredMinSize != null) ? configuredMinSize : -1;
+        return this.minSizeMap.getIfAbsent(position, -1);
     }
 
     /**
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/layer/event/ColumnStructuralChangeEvent.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/layer/event/ColumnStructuralChangeEvent.java
index 15fbbbc..c1a0589 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/layer/event/ColumnStructuralChangeEvent.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/layer/event/ColumnStructuralChangeEvent.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2019 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -60,12 +60,32 @@
      *            The indexes of the columns that have changed.
      *
      * @since 1.6
+     * @deprecated Use
+     *             {@link #ColumnStructuralChangeEvent(ILayer, Collection, int...)}
+     *             with primitive types for row indexes to avoid autoboxing.
      */
+    @Deprecated
     public ColumnStructuralChangeEvent(ILayer layer, Collection<Range> columnPositionRanges, Collection<Integer> columnIndexes) {
         super(layer, columnPositionRanges, columnIndexes);
     }
 
     /**
+     * Creates a new ColumnStructuralChangeEvent based on the given information.
+     *
+     * @param layer
+     *            The ILayer to which the given column positions match.
+     * @param columnPositionRanges
+     *            The column position ranges for the columns that have changed.
+     * @param columnIndexes
+     *            The indexes of the columns that have changed.
+     *
+     * @since 2.0
+     */
+    public ColumnStructuralChangeEvent(ILayer layer, Collection<Range> columnPositionRanges, int... columnIndexes) {
+        super(layer, columnPositionRanges, columnIndexes);
+    }
+
+    /**
      * Creates a new ColumnStructuralChangeEvent based on the given instance.
      * Mainly needed for cloning.
      *
@@ -79,7 +99,7 @@
 
     @Override
     public Collection<Rectangle> getChangedPositionRectangles() {
-        Collection<Rectangle> changedPositionRectangles = new ArrayList<Rectangle>();
+        Collection<Rectangle> changedPositionRectangles = new ArrayList<>();
 
         Collection<Range> ranges = getColumnPositionRanges();
         if (ranges != null && ranges.size() > 0) {
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/layer/event/ColumnVisualChangeEvent.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/layer/event/ColumnVisualChangeEvent.java
index 2eac94d..d82eb09 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/layer/event/ColumnVisualChangeEvent.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/layer/event/ColumnVisualChangeEvent.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2019 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -13,7 +13,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.HashSet;
 
 import org.eclipse.nebula.widgets.nattable.coordinate.PositionUtil;
 import org.eclipse.nebula.widgets.nattable.coordinate.Range;
@@ -38,7 +37,7 @@
     /**
      * The indexes of the columns that have changed.
      */
-    private Collection<Integer> columnIndexes;
+    private int[] columnIndexes;
 
     /**
      * Creates a new ColumnVisualChangeEvent based on the given information.
@@ -87,10 +86,31 @@
      * @param columnIndexes
      *            The indexes of the columns that have changed.
      * @since 1.6
+     * @deprecated Use
+     *             {@link #ColumnVisualChangeEvent(ILayer, Collection, int...)}
+     *             to avoid autoboxing of row indexes.
      */
+    @Deprecated
     public ColumnVisualChangeEvent(ILayer layer, Collection<Range> columnPositionRanges, Collection<Integer> columnIndexes) {
         this.layer = layer;
         this.columnPositionRanges = columnPositionRanges;
+        this.columnIndexes = columnIndexes.stream().mapToInt(Integer::intValue).toArray();
+    }
+
+    /**
+     * Creates a new ColumnVisualChangeEvent based on the given information.
+     *
+     * @param layer
+     *            The ILayer to which the given column positions match.
+     * @param columnPositionRanges
+     *            The column position ranges for the columns that have changed.
+     * @param columnIndexes
+     *            The indexes of the columns that have changed.
+     * @since 2.0
+     */
+    public ColumnVisualChangeEvent(ILayer layer, Collection<Range> columnPositionRanges, int... columnIndexes) {
+        this.layer = layer;
+        this.columnPositionRanges = columnPositionRanges;
         this.columnIndexes = columnIndexes;
     }
 
@@ -135,14 +155,15 @@
     /**
      *
      * @return The indexes of the columns that have changed.
-     * @since 1.6
+     * @since 2.0
      */
-    public Collection<Integer> getColumnIndexes() {
+    public int[] getColumnIndexes() {
         if (this.columnIndexes == null) {
-            this.columnIndexes = new HashSet<Integer>();
             int[] positions = PositionUtil.getPositions(this.columnPositionRanges);
-            for (int pos : positions) {
-                this.columnIndexes.add(this.layer.getColumnIndexByPosition(pos));
+            this.columnIndexes = new int[positions.length];
+            for (int i = 0; i < positions.length; i++) {
+                int pos = positions[i];
+                this.columnIndexes[i] = this.layer.getColumnIndexByPosition(pos);
             }
         }
         return this.columnIndexes;
@@ -160,7 +181,7 @@
     @Override
     public Collection<Rectangle> getChangedPositionRectangles() {
         Collection<Rectangle> changedPositionRectangles =
-                new ArrayList<Rectangle>(this.columnPositionRanges.size());
+                new ArrayList<>(this.columnPositionRanges.size());
 
         int rowCount = this.layer.getRowCount();
         for (Range range : this.columnPositionRanges) {
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/layer/event/RowStructuralChangeEvent.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/layer/event/RowStructuralChangeEvent.java
index 99b2ec3..1946754 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/layer/event/RowStructuralChangeEvent.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/layer/event/RowStructuralChangeEvent.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2019 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -60,12 +60,32 @@
      *            The indexes of the rows that have changed.
      *
      * @since 1.6
+     * @deprecated Use
+     *             {@link #RowStructuralChangeEvent(ILayer, Collection, int...)}
+     *             with primitive types for row indexes to avoid autoboxing.
      */
+    @Deprecated
     public RowStructuralChangeEvent(ILayer layer, Collection<Range> rowPositionRanges, Collection<Integer> rowIndexes) {
         super(layer, rowPositionRanges, rowIndexes);
     }
 
     /**
+     * Creates a new RowStructuralChangeEvent based on the given information.
+     *
+     * @param layer
+     *            The ILayer to which the given row positions match.
+     * @param rowPositionRanges
+     *            The row position ranges for the rows that have changed.
+     * @param rowIndexes
+     *            The indexes of the rows that have changed.
+     *
+     * @since 2.0
+     */
+    public RowStructuralChangeEvent(ILayer layer, Collection<Range> rowPositionRanges, int... rowIndexes) {
+        super(layer, rowPositionRanges, rowIndexes);
+    }
+
+    /**
      * Creates a new RowStructuralChangeEvent based on the given instance.
      * Mainly needed for cloning.
      *
@@ -79,7 +99,7 @@
 
     @Override
     public Collection<Rectangle> getChangedPositionRectangles() {
-        Collection<Rectangle> changedPositionRectangles = new ArrayList<Rectangle>();
+        Collection<Rectangle> changedPositionRectangles = new ArrayList<>();
 
         Collection<Range> ranges = getRowPositionRanges();
         if (ranges != null && ranges.size() > 0) {
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/layer/event/RowVisualChangeEvent.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/layer/event/RowVisualChangeEvent.java
index eacb6f1..805d0b5 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/layer/event/RowVisualChangeEvent.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/layer/event/RowVisualChangeEvent.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2019 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -13,7 +13,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.HashSet;
 
 import org.eclipse.nebula.widgets.nattable.coordinate.PositionUtil;
 import org.eclipse.nebula.widgets.nattable.coordinate.Range;
@@ -33,11 +32,11 @@
      * The row position ranges for the rows that have changed. They are related
      * to the set ILayer.
      */
-    private Collection<Range> rowPositionRanges = new ArrayList<Range>();
+    private Collection<Range> rowPositionRanges;
     /**
      * The indexes of the rows that have changed.
      */
-    private Collection<Integer> rowIndexes;
+    private int[] rowIndexes;
 
     /**
      * Creates a new RowVisualChangeEvent based on the given information.
@@ -86,10 +85,30 @@
      * @param rowIndexes
      *            The indexes of the rows that have changed.
      * @since 1.6
+     * @deprecated Use {@link #RowVisualChangeEvent(ILayer, Collection, int...)}
+     *             to avoid autoboxing of row indexes.
      */
+    @Deprecated
     public RowVisualChangeEvent(ILayer layer, Collection<Range> rowPositionRanges, Collection<Integer> rowIndexes) {
         this.layer = layer;
         this.rowPositionRanges = rowPositionRanges;
+        this.rowIndexes = rowIndexes.stream().mapToInt(Integer::intValue).toArray();
+    }
+
+    /**
+     * Creates a new RowVisualChangeEvent based on the given information.
+     *
+     * @param layer
+     *            The ILayer to which the given column positions match.
+     * @param rowPositionRanges
+     *            The row position ranges for the rows that have changed.
+     * @param rowIndexes
+     *            The indexes of the rows that have changed.
+     * @since 2.0
+     */
+    public RowVisualChangeEvent(ILayer layer, Collection<Range> rowPositionRanges, int... rowIndexes) {
+        this.layer = layer;
+        this.rowPositionRanges = rowPositionRanges;
         this.rowIndexes = rowIndexes;
     }
 
@@ -133,14 +152,15 @@
     /**
      *
      * @return The indexes of the rows that have changed.
-     * @since 1.6
+     * @since 2.0
      */
-    public Collection<Integer> getRowIndexes() {
+    public int[] getRowIndexes() {
         if (this.rowIndexes == null) {
-            this.rowIndexes = new HashSet<Integer>();
             int[] positions = PositionUtil.getPositions(this.rowPositionRanges);
-            for (int pos : positions) {
-                this.rowIndexes.add(this.layer.getRowIndexByPosition(pos));
+            this.rowIndexes = new int[positions.length];
+            for (int i = 0; i < positions.length; i++) {
+                int pos = positions[i];
+                this.rowIndexes[i] = this.layer.getRowIndexByPosition(pos);
             }
         }
         return this.rowIndexes;
@@ -158,7 +178,7 @@
     @Override
     public Collection<Rectangle> getChangedPositionRectangles() {
         Collection<Rectangle> changedPositionRectangles =
-                new ArrayList<Rectangle>(this.rowPositionRanges.size());
+                new ArrayList<>(this.rowPositionRanges.size());
 
         int columnCount = this.layer.getColumnCount();
         for (Range range : this.rowPositionRanges) {
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/layer/event/StructuralChangeEventHelper.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/layer/event/StructuralChangeEventHelper.java
index 277969d..b2113df 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/layer/event/StructuralChangeEventHelper.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/layer/event/StructuralChangeEventHelper.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2013, 2018 Dirk Fauth and others.
+ * Copyright (c) 2013, 2020 Dirk Fauth and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -12,8 +12,10 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.List;
 
+import org.eclipse.collections.api.collection.primitive.MutableIntCollection;
+import org.eclipse.collections.api.list.primitive.MutableIntList;
+import org.eclipse.collections.impl.factory.primitive.IntLists;
 import org.eclipse.nebula.widgets.nattable.coordinate.Range;
 import org.eclipse.nebula.widgets.nattable.layer.ILayer;
 import org.eclipse.nebula.widgets.nattable.layer.event.StructuralDiff.DiffTypeEnum;
@@ -53,7 +55,7 @@
         // the number of all deleted rows that don't have a corresponding index
         // anymore (last row cases)
         int numberOfNoIndex = 0;
-        List<Integer> toRemove = new ArrayList<Integer>();
+        ArrayList<Integer> toRemove = new ArrayList<>();
         for (StructuralDiff rowDiff : rowDiffs) {
             if (rowDiff.getDiffType() != null
                     && rowDiff.getDiffType().equals(DiffTypeEnum.DELETE)) {
@@ -72,7 +74,7 @@
         cachedRowIndexes.removeAll(toRemove);
 
         // modify row indexes regarding the deleted rows
-        List<Integer> modifiedRows = new ArrayList<Integer>();
+        ArrayList<Integer> modifiedRows = new ArrayList<>();
         for (Integer row : cachedRowIndexes) {
             // check number of removed indexes that are lower than the current
             // one
@@ -92,6 +94,74 @@
     }
 
     /**
+     * Will check for events that indicate that rows has been deleted. In that
+     * case the given cached indexes for the given layer need to be updated
+     * because the index of the rows might have changed. E.g. Row with index 3
+     * is hidden in the given layer, deleting row at index 1 will cause the row
+     * at index 3 to be moved at index 2. Without transforming the index
+     * regarding the delete event, the wrong row would be hidden.
+     *
+     * @param rowDiffs
+     *            The collection of {@link StructuralDiff}s to handle
+     * @param underlyingLayer
+     *            The underlying layer of the layer who caches the indexes.
+     *            Needed to translate the transported row positions to indexes,
+     *            because the conversion to the layer who caches the index is
+     *            done before it is fired further in the layer stack
+     * @param cachedRowIndexes
+     *            The collection of indexes that is cached by the layer that
+     *            needs transformation
+     * @param handleNotFound
+     *            flag to tell whether the not found row indexes should be taken
+     *            into account or not. Needed for last row checks
+     *
+     * @since 2.0
+     */
+    public static void handleRowDelete(
+            Collection<StructuralDiff> rowDiffs, ILayer underlyingLayer,
+            MutableIntCollection cachedRowIndexes, boolean handleNotFound) {
+
+        // the number of all deleted rows that don't have a corresponding index
+        // anymore (last row cases)
+        int numberOfNoIndex = 0;
+        MutableIntList toRemove = IntLists.mutable.empty();
+        for (StructuralDiff rowDiff : rowDiffs) {
+            if (rowDiff.getDiffType() != null
+                    && rowDiff.getDiffType().equals(DiffTypeEnum.DELETE)) {
+                Range beforePositionRange = rowDiff.getBeforePositionRange();
+                for (int i = beforePositionRange.start; i < beforePositionRange.end; i++) {
+                    int index = i;// underlyingLayer.getRowIndexByPosition(i);
+                    if (index >= 0) {
+                        toRemove.add(index);
+                    } else {
+                        numberOfNoIndex++;
+                    }
+                }
+            }
+        }
+        final int noIndexCount = numberOfNoIndex;
+
+        // remove the row indexes that are deleted
+        cachedRowIndexes.removeAll(toRemove);
+
+        // modify row indexes regarding the deleted rows
+        MutableIntList modifiedRows = IntLists.mutable.empty();
+        cachedRowIndexes.forEach(row -> {
+            // check number of removed indexes that are lower than the current
+            // one
+            int deletedBefore = handleNotFound ? noIndexCount : 0;
+            deletedBefore += toRemove.count(removed -> removed < row);
+
+            int modRow = row - deletedBefore;
+            if (modRow >= 0) {
+                modifiedRows.add(modRow);
+            }
+        });
+        cachedRowIndexes.clear();
+        cachedRowIndexes.addAll(modifiedRows);
+    }
+
+    /**
      * Will check for events that indicate that rows are added. In that case the
      * given cached indexes need to be updated because the index of the rows
      * might have changed. E.g. Row with index 3 is hidden in the given layer,
@@ -125,7 +195,7 @@
             if (rowDiff.getDiffType() != null
                     && rowDiff.getDiffType().equals(DiffTypeEnum.ADD)) {
                 Range beforePositionRange = rowDiff.getBeforePositionRange();
-                List<Integer> modifiedRows = new ArrayList<Integer>();
+                ArrayList<Integer> modifiedRows = new ArrayList<>();
                 int beforeIndex = underlyingLayer.getRowIndexByPosition(beforePositionRange.start);
                 for (Integer row : cachedRowIndexes) {
                     if (row >= beforeIndex) {
@@ -146,6 +216,61 @@
     }
 
     /**
+     * Will check for events that indicate that rows are added. In that case the
+     * given cached indexes need to be updated because the index of the rows
+     * might have changed. E.g. Row with index 3 is hidden in the given layer,
+     * adding a row at index 1 will cause the row at index 3 to be moved to
+     * index 4. Without transforming the index regarding the add event, the
+     * wrong row would be hidden.
+     *
+     * @param rowDiffs
+     *            The collection of {@link StructuralDiff}s to handle
+     * @param underlyingLayer
+     *            The underlying layer of the layer who caches the indexes.
+     *            Needed to translate the transported row positions to indexes,
+     *            because the conversion to the layer who caches the index is
+     *            done before it is fired further in the layer stack
+     * @param cachedRowIndexes
+     *            The collection of indexes that is cached by the layer that
+     *            needs transformation
+     * @param addToCache
+     *            Flag to configure if the added value should be added to the
+     *            cache or not. This is necessary to differ whether
+     *            cachedRowIndexes are a collection of all indexes that need to
+     *            be updated (e.g. row reordering) or just a collection of
+     *            indexes that are applied for a specific state (e.g. row hide
+     *            state)
+     * @since 2.0
+     */
+    public static void handleRowInsert(
+            Collection<StructuralDiff> rowDiffs, ILayer underlyingLayer,
+            MutableIntCollection cachedRowIndexes, boolean addToCache) {
+
+        for (StructuralDiff rowDiff : rowDiffs) {
+            if (rowDiff.getDiffType() != null
+                    && rowDiff.getDiffType().equals(DiffTypeEnum.ADD)) {
+                Range beforePositionRange = rowDiff.getBeforePositionRange();
+                MutableIntList modifiedRows = IntLists.mutable.empty();
+                int beforeIndex = underlyingLayer.getRowIndexByPosition(beforePositionRange.start);
+                cachedRowIndexes.forEach(row -> {
+                    if (row >= beforeIndex) {
+                        modifiedRows.add(row + 1);
+                    } else {
+                        modifiedRows.add(row);
+                    }
+                });
+
+                if (addToCache) {
+                    modifiedRows.addAtIndex(beforeIndex, beforePositionRange.start);
+                }
+
+                cachedRowIndexes.clear();
+                cachedRowIndexes.addAll(modifiedRows);
+            }
+        }
+    }
+
+    /**
      * Will check for events that indicate that columns has been deleted. In
      * that case the given cached indexes for the given layer need to be updated
      * because the index of the columns might have changed. E.g. Column with
@@ -174,7 +299,7 @@
         // the number of all deleted columns that don't have a corresponding
         // index anymore (last column cases)
         int numberOfNoIndex = 0;
-        List<Integer> toRemove = new ArrayList<Integer>();
+        ArrayList<Integer> toRemove = new ArrayList<>();
         for (StructuralDiff columnDiff : columnDiffs) {
             if (columnDiff.getDiffType() != null
                     && columnDiff.getDiffType().equals(DiffTypeEnum.DELETE)) {
@@ -193,7 +318,7 @@
         cachedColumnIndexes.removeAll(toRemove);
 
         // modify column indexes regarding the deleted columns
-        List<Integer> modifiedColumns = new ArrayList<Integer>();
+        ArrayList<Integer> modifiedColumns = new ArrayList<>();
         for (Integer column : cachedColumnIndexes) {
             // check number of removed indexes that are lower than the current
             // one
@@ -213,6 +338,73 @@
     }
 
     /**
+     * Will check for events that indicate that columns has been deleted. In
+     * that case the given cached indexes for the given layer need to be updated
+     * because the index of the columns might have changed. E.g. Column with
+     * index 3 is hidden in the given layer, deleting column at index 1 will
+     * cause the column at index 3 to be moved at index 2. Without transforming
+     * the index regarding the delete event, the wrong column would be hidden.
+     *
+     * @param columnDiffs
+     *            The collection of {@link StructuralDiff}s to handle
+     * @param underlyingLayer
+     *            The underlying layer of the layer who caches the indexes.
+     *            Needed to translate the transported column positions to
+     *            indexes, because the conversion to the layer who caches the
+     *            index is done before it is fired further in the layer stack
+     * @param cachedColumnIndexes
+     *            The collection of indexes that is cached by the layer that
+     *            needs transformation
+     * @param handleNotFound
+     *            flag to tell whether the not found column indexes should be
+     *            taken into account or not. Needed for last column checks.
+     * @since 2.0
+     */
+    public static void handleColumnDelete(
+            Collection<StructuralDiff> columnDiffs, ILayer underlyingLayer,
+            MutableIntCollection cachedColumnIndexes, boolean handleNotFound) {
+
+        // the number of all deleted columns that don't have a corresponding
+        // index anymore (last column cases)
+        int numberOfNoIndex = 0;
+        MutableIntList toRemove = IntLists.mutable.empty();
+        for (StructuralDiff columnDiff : columnDiffs) {
+            if (columnDiff.getDiffType() != null
+                    && columnDiff.getDiffType().equals(DiffTypeEnum.DELETE)) {
+                Range beforePositionRange = columnDiff.getBeforePositionRange();
+                for (int i = beforePositionRange.start; i < beforePositionRange.end; i++) {
+                    int index = i;// underlyingLayer.getColumnIndexByPosition(i);
+                    if (index >= 0) {
+                        toRemove.add(index);
+                    } else {
+                        numberOfNoIndex++;
+                    }
+                }
+            }
+        }
+        final int noIndexCount = numberOfNoIndex;
+
+        // remove the column indexes that are deleted
+        cachedColumnIndexes.removeAll(toRemove);
+
+        // modify column indexes regarding the deleted columns
+        MutableIntList modifiedColumns = IntLists.mutable.empty();
+        cachedColumnIndexes.forEach(column -> {
+            // check number of removed indexes that are lower than the current
+            // one
+            int deletedBefore = handleNotFound ? noIndexCount : 0;
+            deletedBefore += toRemove.count(removed -> removed < column);
+
+            int modColumn = column - deletedBefore;
+            if (modColumn >= 0) {
+                modifiedColumns.add(modColumn);
+            }
+        });
+        cachedColumnIndexes.clear();
+        cachedColumnIndexes.addAll(modifiedColumns);
+    }
+
+    /**
      * Will check for events that indicate that columns are added. In that case
      * the given cached indexes need to be updated because the index of the
      * columns might have changed. E.g. Column with index 3 is hidden in the
@@ -246,7 +438,7 @@
             if (columnDiff.getDiffType() != null
                     && columnDiff.getDiffType().equals(DiffTypeEnum.ADD)) {
                 Range beforePositionRange = columnDiff.getBeforePositionRange();
-                List<Integer> modifiedColumns = new ArrayList<Integer>();
+                ArrayList<Integer> modifiedColumns = new ArrayList<>();
                 int beforeIndex = underlyingLayer.getColumnIndexByPosition(beforePositionRange.start);
                 for (Integer column : cachedColumnIndexes) {
                     if (column >= beforeIndex) {
@@ -267,6 +459,61 @@
     }
 
     /**
+     * Will check for events that indicate that columns are added. In that case
+     * the given cached indexes need to be updated because the index of the
+     * columns might have changed. E.g. Column with index 3 is hidden in the
+     * given layer, adding a column at index 1 will cause the column at index 3
+     * to be moved to index 4. Without transforming the index regarding the add
+     * event, the wrong column would be hidden.
+     *
+     * @param columnDiffs
+     *            The collection of {@link StructuralDiff}s to handle
+     * @param underlyingLayer
+     *            The underlying layer of the layer who caches the indexes.
+     *            Needed to translate the transported column positions to
+     *            indexes, because the conversion to the layer who caches the
+     *            index is done before it is fired further in the layer stack
+     * @param cachedColumnIndexes
+     *            The collection of indexes that is cached by the layer that
+     *            needs transformation
+     * @param addToCache
+     *            Flag to configure if the added value should be added to the
+     *            cache or not. This is necessary to differ whether
+     *            cachedColumnIndexes are a collection of all indexes that need
+     *            to be updated (e.g. column reordering) or just a collection of
+     *            indexes that are applied for a specific state (e.g. column
+     *            hide state)
+     * @since 2.0
+     */
+    public static void handleColumnInsert(
+            Collection<StructuralDiff> columnDiffs, ILayer underlyingLayer,
+            MutableIntCollection cachedColumnIndexes, boolean addToCache) {
+
+        for (StructuralDiff columnDiff : columnDiffs) {
+            if (columnDiff.getDiffType() != null
+                    && columnDiff.getDiffType().equals(DiffTypeEnum.ADD)) {
+                Range beforePositionRange = columnDiff.getBeforePositionRange();
+                MutableIntList modifiedColumns = IntLists.mutable.empty();
+                int beforeIndex = underlyingLayer.getColumnIndexByPosition(beforePositionRange.start);
+                cachedColumnIndexes.forEach(column -> {
+                    if (column >= beforeIndex) {
+                        modifiedColumns.add(column + 1);
+                    } else {
+                        modifiedColumns.add(column);
+                    }
+                });
+
+                if (addToCache) {
+                    modifiedColumns.addAtIndex(beforeIndex, beforePositionRange.start);
+                }
+
+                cachedColumnIndexes.clear();
+                cachedColumnIndexes.addAll(modifiedColumns);
+            }
+        }
+    }
+
+    /**
      * Method to indicate if the collection of StructuralDiffs marks a reorder
      * event. This is necessary because reordering itself contains out of two
      * diffs, one for deleting columns/rows, one for adding them at another
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/ColumnReorderLayer.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/ColumnReorderLayer.java
index 923a404..08a0fa5 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/ColumnReorderLayer.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/ColumnReorderLayer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2019 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -10,17 +10,19 @@
  ******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.reorder;
 
-import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.Properties;
 import java.util.StringTokenizer;
+import java.util.stream.Collectors;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.eclipse.collections.api.list.primitive.MutableIntList;
+import org.eclipse.collections.api.map.primitive.MutableIntIntMap;
+import org.eclipse.collections.impl.factory.primitive.IntIntMaps;
+import org.eclipse.collections.impl.factory.primitive.IntLists;
 import org.eclipse.nebula.widgets.nattable.coordinate.PositionUtil;
 import org.eclipse.nebula.widgets.nattable.coordinate.Range;
 import org.eclipse.nebula.widgets.nattable.layer.AbstractLayerTransform;
@@ -61,7 +63,7 @@
      * reordering performed by this layer. Position X in the List contains the
      * index of column at position X.
      */
-    protected final List<Integer> columnIndexOrder = new ArrayList<Integer>();
+    protected final MutableIntList columnIndexOrder = IntLists.mutable.empty();
 
     /**
      * The internal mapping of index to position values. Used for performance
@@ -70,9 +72,9 @@
      *
      * @since 1.5
      */
-    protected final Map<Integer, Integer> indexPositionMapping = new HashMap<Integer, Integer>();
+    protected final MutableIntIntMap indexPositionMapping = IntIntMaps.mutable.empty();
 
-    private final Map<Integer, Integer> startXCache = new HashMap<Integer, Integer>();
+    private final MutableIntIntMap startXCache = IntIntMaps.mutable.empty();
 
     private int reorderFromColumnPosition;
 
@@ -152,12 +154,9 @@
     public void saveState(String prefix, Properties properties) {
         super.saveState(prefix, properties);
         if (this.columnIndexOrder.size() > 0) {
-            StringBuilder strBuilder = new StringBuilder();
-            for (Integer index : this.columnIndexOrder) {
-                strBuilder.append(index);
-                strBuilder.append(IPersistable.VALUE_SEPARATOR);
-            }
-            properties.setProperty(prefix + PERSISTENCE_KEY_COLUMN_INDEX_ORDER, strBuilder.toString());
+            properties.setProperty(
+                    prefix + PERSISTENCE_KEY_COLUMN_INDEX_ORDER,
+                    this.columnIndexOrder.makeString(IPersistable.VALUE_SEPARATOR));
         }
     }
 
@@ -167,14 +166,14 @@
         String property = properties.getProperty(prefix + PERSISTENCE_KEY_COLUMN_INDEX_ORDER);
 
         if (property != null) {
-            List<Integer> newColumnIndexOrder = new ArrayList<Integer>();
+            MutableIntList newColumnIndexOrder = IntLists.mutable.empty();
             StringTokenizer tok = new StringTokenizer(property, IPersistable.VALUE_SEPARATOR);
             while (tok.hasMoreTokens()) {
                 String index = tok.nextToken();
-                newColumnIndexOrder.add(Integer.valueOf(index));
+                newColumnIndexOrder.add(Integer.parseInt(index));
             }
 
-            if (isRestoredStateValid(newColumnIndexOrder)) {
+            if (isRestoredStateValid(newColumnIndexOrder.toArray())) {
                 this.columnIndexOrder.clear();
                 this.columnIndexOrder.addAll(newColumnIndexOrder);
                 // refresh index-position mapping
@@ -191,17 +190,18 @@
      *
      * @param newColumnIndexOrder
      *            restored from the properties file.
+     * @since 2.0
      */
-    protected boolean isRestoredStateValid(List<Integer> newColumnIndexOrder) {
-        if (newColumnIndexOrder.size() != getColumnCount()) {
-            LOG.error("Number of persisted columns (" + newColumnIndexOrder.size() + ") " + //$NON-NLS-1$ //$NON-NLS-2$
+    protected boolean isRestoredStateValid(int[] newColumnIndexOrder) {
+        if (newColumnIndexOrder.length != getColumnCount()) {
+            LOG.error("Number of persisted columns (" + newColumnIndexOrder.length + ") " + //$NON-NLS-1$ //$NON-NLS-2$
                     "is not the same as the number of columns in the data source (" //$NON-NLS-1$
                     + getColumnCount() + ").\n" + //$NON-NLS-1$
                     "Skipping restore of column ordering"); //$NON-NLS-1$
             return false;
         }
 
-        for (Integer index : newColumnIndexOrder) {
+        for (int index : newColumnIndexOrder) {
             if (!this.indexPositionMapping.containsKey(index)) {
                 LOG.error("Column index: " + index + " being restored, is not a available in the data soure.\n" + //$NON-NLS-1$ //$NON-NLS-2$
                         "Skipping restore of column ordering"); //$NON-NLS-1$
@@ -218,7 +218,16 @@
      * @return the internal kept ordering of column indexes.
      */
     public List<Integer> getColumnIndexOrder() {
-        return this.columnIndexOrder;
+        return this.columnIndexOrder.primitiveStream().boxed().collect(Collectors.toList());
+    }
+
+    /**
+     *
+     * @return the internal kept ordering of column indexes.
+     * @since 2.0
+     */
+    public int[] getColumnIndexOrderArray() {
+        return this.columnIndexOrder.toArray();
     }
 
     @Override
@@ -232,8 +241,7 @@
 
     @Override
     public int getColumnPositionByIndex(int columnIndex) {
-        Integer result = this.indexPositionMapping.get(columnIndex);
-        return (result != null) ? result : -1;
+        return this.indexPositionMapping.getIfAbsent(columnIndex, -1);
     }
 
     @Override
@@ -250,16 +258,15 @@
 
     @Override
     public Collection<Range> underlyingToLocalColumnPositions(ILayer sourceUnderlyingLayer, Collection<Range> underlyingColumnPositionRanges) {
-        List<Integer> reorderedColumnPositions = new ArrayList<Integer>();
+        MutableIntList reorderedColumnPositions = IntLists.mutable.empty();
         for (Range underlyingColumnPositionRange : underlyingColumnPositionRanges) {
             for (int underlyingColumnPosition = underlyingColumnPositionRange.start; underlyingColumnPosition < underlyingColumnPositionRange.end; underlyingColumnPosition++) {
                 int localColumnPosition = underlyingToLocalColumnPosition(sourceUnderlyingLayer, underlyingColumnPosition);
-                reorderedColumnPositions.add(Integer.valueOf(localColumnPosition));
+                reorderedColumnPositions.add(localColumnPosition);
             }
         }
-        Collections.sort(reorderedColumnPositions);
 
-        return PositionUtil.getRanges(reorderedColumnPositions);
+        return PositionUtil.getRanges(reorderedColumnPositions.toSortedArray());
     }
 
     // X
@@ -271,9 +278,9 @@
 
     @Override
     public int getStartXOfColumnPosition(int targetColumnPosition) {
-        Integer cachedStartX = this.startXCache.get(Integer.valueOf(targetColumnPosition));
-        if (cachedStartX != null) {
-            return cachedStartX.intValue();
+        int cachedStartX = this.startXCache.getIfAbsent(targetColumnPosition, -1);
+        if (cachedStartX != -1) {
+            return cachedStartX;
         }
 
         int aggregateWidth = 0;
@@ -281,7 +288,7 @@
             aggregateWidth += this.underlyingLayer.getColumnWidthByPosition(localToUnderlyingColumnPosition(columnPosition));
         }
 
-        this.startXCache.put(Integer.valueOf(targetColumnPosition), Integer.valueOf(aggregateWidth));
+        this.startXCache.put(targetColumnPosition, aggregateWidth);
         return aggregateWidth;
     }
 
@@ -342,9 +349,9 @@
             toColumnPosition++;
         }
 
-        Integer fromColumnIndex = this.columnIndexOrder.get(fromColumnPosition);
-        this.columnIndexOrder.add(toColumnPosition, fromColumnIndex);
-        this.columnIndexOrder.remove(fromColumnPosition + (fromColumnPosition > toColumnPosition ? 1 : 0));
+        int fromColumnIndex = this.columnIndexOrder.get(fromColumnPosition);
+        this.columnIndexOrder.addAtIndex(toColumnPosition, fromColumnIndex);
+        this.columnIndexOrder.removeAtIndex(fromColumnPosition + (fromColumnPosition > toColumnPosition ? 1 : 0));
 
         // update index-position mapping
         refreshIndexPositionMapping();
@@ -403,6 +410,22 @@
      *            position to move the columns to
      */
     public void reorderMultipleColumnPositions(List<Integer> fromColumnPositions, int toColumnPosition) {
+        reorderMultipleColumnPositions(
+                fromColumnPositions.stream().mapToInt(Integer::intValue).toArray(),
+                toColumnPosition);
+    }
+
+    /**
+     * Reorders the given from-columns to the <b>left</b> edge of the column to
+     * move to.
+     *
+     * @param fromColumnPositions
+     *            column positions to move
+     * @param toColumnPosition
+     *            position to move the columns to
+     * @since 2.0
+     */
+    public void reorderMultipleColumnPositions(int[] fromColumnPositions, int toColumnPosition) {
         boolean reorderToLeftEdge;
         if (toColumnPosition < getColumnCount()) {
             reorderToLeftEdge = true;
@@ -427,37 +450,55 @@
      *            should be positioned to the right
      */
     public void reorderMultipleColumnPositions(List<Integer> fromColumnPositions, int toColumnPosition, boolean reorderToLeftEdge) {
+        reorderMultipleColumnPositions(
+                fromColumnPositions.stream().mapToInt(Integer::intValue).toArray(),
+                toColumnPosition,
+                reorderToLeftEdge);
+    }
+
+    /**
+     * Reorders the given from-columns to the specified edge of the column to
+     * move to and fires a {@link ColumnReorderEvent}.
+     *
+     * @param fromColumnPositions
+     *            column positions to move
+     * @param toColumnPosition
+     *            position to move the columns to
+     * @param reorderToLeftEdge
+     *            <code>true</code> if the columns should be moved to the left
+     *            of the given column to move to, <code>false</code> if they
+     *            should be positioned to the right
+     * @since 2.0
+     */
+    public void reorderMultipleColumnPositions(int[] fromColumnPositions, int toColumnPosition, boolean reorderToLeftEdge) {
         // the position collection needs to be sorted so the move works
         // correctly
-        Collections.sort(fromColumnPositions);
+        Arrays.sort(fromColumnPositions);
 
         // get the indexes before the move operation
-        List<Integer> fromColumnIndexes = new ArrayList<Integer>();
-        for (int fromColumnPosition : fromColumnPositions) {
-            fromColumnIndexes.add(getColumnIndexByPosition(fromColumnPosition));
-        }
+        int[] fromColumnIndexes = Arrays.stream(fromColumnPositions).map(this::getColumnIndexByPosition).toArray();
         int toColumnIndex = getColumnIndexByPosition(toColumnPosition);
 
         // Moving from left to right
-        final int fromColumnPositionsCount = fromColumnPositions.size();
+        final int fromColumnPositionsCount = fromColumnPositions.length;
 
-        if (toColumnPosition > fromColumnPositions.get(fromColumnPositionsCount - 1)) {
-            int firstColumnPosition = fromColumnPositions.get(0).intValue();
+        if (toColumnPosition > fromColumnPositions[fromColumnPositionsCount - 1]) {
+            int firstColumnPosition = fromColumnPositions[0];
 
             int moved = 0;
             for (int columnCount = 0; columnCount < fromColumnPositionsCount; columnCount++) {
-                final int fromColumnPosition = fromColumnPositions.get(columnCount) - moved;
+                final int fromColumnPosition = fromColumnPositions[columnCount] - moved;
                 moveColumn(fromColumnPosition, toColumnPosition, reorderToLeftEdge);
                 moved++;
                 if (fromColumnPosition < firstColumnPosition) {
                     firstColumnPosition = fromColumnPosition;
                 }
             }
-        } else if (toColumnPosition < fromColumnPositions.get(fromColumnPositionsCount - 1).intValue()) {
+        } else if (toColumnPosition < fromColumnPositions[fromColumnPositionsCount - 1]) {
             // Moving from right to left
             int targetColumnPosition = toColumnPosition;
-            for (Integer fromColumnPosition : fromColumnPositions) {
-                final int fromColumnPositionInt = fromColumnPosition.intValue();
+            for (int fromColumnPosition : fromColumnPositions) {
+                final int fromColumnPositionInt = fromColumnPosition;
                 moveColumn(fromColumnPositionInt, targetColumnPosition++, reorderToLeftEdge);
             }
         }
@@ -483,11 +524,32 @@
      * @since 1.6
      */
     public void reorderMultipleColumnIndexes(List<Integer> fromColumnIndexes, int toColumnPosition, boolean reorderToLeftEdge) {
+        reorderMultipleColumnIndexes(
+                fromColumnIndexes.stream().mapToInt(Integer::intValue).toArray(),
+                toColumnPosition,
+                reorderToLeftEdge);
+    }
+
+    /**
+     * Reorders the given from-columns identified by index to the specified edge
+     * of the column to move to and fires a {@link ColumnReorderEvent}. This
+     * method can be used to reorder columns that are hidden in a higher level,
+     * e.g. to reorder a column group that has hidden columns.
+     *
+     * @param fromColumnIndexes
+     *            column indexes to move
+     * @param toColumnPosition
+     *            position to move the columns to
+     * @param reorderToLeftEdge
+     *            <code>true</code> if the columns should be moved to the left
+     *            of the given column to move to, <code>false</code> if they
+     *            should be positioned to the right
+     *
+     * @since 2.0
+     */
+    public void reorderMultipleColumnIndexes(int[] fromColumnIndexes, int toColumnPosition, boolean reorderToLeftEdge) {
         // calculate positions from indexes
-        List<Integer> fromColumnPositions = new ArrayList<Integer>(fromColumnIndexes.size());
-        for (Integer index : fromColumnIndexes) {
-            fromColumnPositions.add(getColumnPositionByIndex(index));
-        }
+        int[] fromColumnPositions = Arrays.stream(fromColumnIndexes).map(this::getColumnPositionByIndex).toArray();
         reorderMultipleColumnPositions(fromColumnPositions, toColumnPosition, reorderToLeftEdge);
     }
 
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/RowReorderLayer.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/RowReorderLayer.java
index 9359abb..c4855e7 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/RowReorderLayer.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/RowReorderLayer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2013, 2019 Dirk Fauth and others.
+ * Copyright (c) 2013, 2020 Dirk Fauth and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -10,17 +10,19 @@
  *******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.reorder;
 
-import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.Properties;
 import java.util.StringTokenizer;
+import java.util.stream.Collectors;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.eclipse.collections.api.list.primitive.MutableIntList;
+import org.eclipse.collections.api.map.primitive.MutableIntIntMap;
+import org.eclipse.collections.impl.factory.primitive.IntIntMaps;
+import org.eclipse.collections.impl.factory.primitive.IntLists;
 import org.eclipse.nebula.widgets.nattable.coordinate.PositionUtil;
 import org.eclipse.nebula.widgets.nattable.coordinate.Range;
 import org.eclipse.nebula.widgets.nattable.layer.AbstractLayerTransform;
@@ -59,7 +61,7 @@
      * performed by this layer. Position Y in the List contains the index of row
      * at position Y.
      */
-    protected final List<Integer> rowIndexOrder = new ArrayList<Integer>();
+    protected final MutableIntList rowIndexOrder = IntLists.mutable.empty();
 
     /**
      * The internal mapping of index to position values. Used for performance
@@ -68,13 +70,13 @@
      *
      * @since 1.5
      */
-    protected final Map<Integer, Integer> indexPositionMapping = new HashMap<Integer, Integer>();
+    protected final MutableIntIntMap indexPositionMapping = IntIntMaps.mutable.empty();
 
     /**
      * Caching of the starting y positions of the rows. Used to reduce
      * calculation time on rendering
      */
-    private final Map<Integer, Integer> startYCache = new HashMap<Integer, Integer>();
+    private final MutableIntIntMap startYCache = IntIntMaps.mutable.empty();
 
     /**
      * Local cached position of the row that is currently reordered.
@@ -157,12 +159,9 @@
     public void saveState(String prefix, Properties properties) {
         super.saveState(prefix, properties);
         if (this.rowIndexOrder.size() > 0) {
-            StringBuilder strBuilder = new StringBuilder();
-            for (Integer index : this.rowIndexOrder) {
-                strBuilder.append(index);
-                strBuilder.append(IPersistable.VALUE_SEPARATOR);
-            }
-            properties.setProperty(prefix + PERSISTENCE_KEY_ROW_INDEX_ORDER, strBuilder.toString());
+            properties.setProperty(
+                    prefix + PERSISTENCE_KEY_ROW_INDEX_ORDER,
+                    this.rowIndexOrder.makeString(IPersistable.VALUE_SEPARATOR));
         }
     }
 
@@ -172,14 +171,14 @@
         String property = properties.getProperty(prefix + PERSISTENCE_KEY_ROW_INDEX_ORDER);
 
         if (property != null) {
-            List<Integer> newRowIndexOrder = new ArrayList<Integer>();
+            MutableIntList newRowIndexOrder = IntLists.mutable.empty();
             StringTokenizer tok = new StringTokenizer(property, IPersistable.VALUE_SEPARATOR);
             while (tok.hasMoreTokens()) {
                 String index = tok.nextToken();
-                newRowIndexOrder.add(Integer.valueOf(index));
+                newRowIndexOrder.add(Integer.parseInt(index));
             }
 
-            if (isRestoredStateValid(newRowIndexOrder)) {
+            if (isRestoredStateValid(newRowIndexOrder.toArray())) {
                 this.rowIndexOrder.clear();
                 this.rowIndexOrder.addAll(newRowIndexOrder);
                 // refresh index-position mapping
@@ -196,17 +195,18 @@
      *
      * @param newRowIndexOrder
      *            restored from the properties file.
+     * @since 2.0
      */
-    protected boolean isRestoredStateValid(List<Integer> newRowIndexOrder) {
-        if (newRowIndexOrder.size() != getRowCount()) {
-            LOG.error("Number of persisted rows (" + newRowIndexOrder.size() + ") " + //$NON-NLS-1$ //$NON-NLS-2$
+    protected boolean isRestoredStateValid(int[] newRowIndexOrder) {
+        if (newRowIndexOrder.length != getRowCount()) {
+            LOG.error("Number of persisted rows (" + newRowIndexOrder.length + ") " + //$NON-NLS-1$ //$NON-NLS-2$
                     "is not the same as the number of rows in the data source (" //$NON-NLS-1$
                     + getRowCount() + ").\n" + //$NON-NLS-1$
                     "Skipping restore of row ordering"); //$NON-NLS-1$
             return false;
         }
 
-        for (Integer index : newRowIndexOrder) {
+        for (int index : newRowIndexOrder) {
             if (!this.indexPositionMapping.containsKey(index)) {
                 LOG.error("Row index: " + index + " being restored, is not a available in the data soure.\n" + //$NON-NLS-1$ //$NON-NLS-2$
                         "Skipping restore of row ordering"); //$NON-NLS-1$
@@ -232,8 +232,8 @@
 
     @Override
     public int getStartYOfRowPosition(int targetRowPosition) {
-        Integer cachedStartY = this.startYCache.get(targetRowPosition);
-        if (cachedStartY != null) {
+        int cachedStartY = this.startYCache.getIfAbsent(targetRowPosition, -1);
+        if (cachedStartY != -1) {
             return cachedStartY;
         }
 
@@ -282,13 +282,21 @@
      * @return The local cache of the row index order.
      */
     public List<Integer> getRowIndexOrder() {
-        return this.rowIndexOrder;
+        return this.rowIndexOrder.primitiveStream().boxed().collect(Collectors.toList());
+    }
+
+    /**
+     * @return The local cache of the row index order.
+     * @since 2.0
+     */
+    public int[] getRowIndexOrderArray() {
+        return this.rowIndexOrder.toArray();
     }
 
     @Override
     public int getRowIndexByPosition(int rowPosition) {
         if (rowPosition >= 0 && rowPosition < this.rowIndexOrder.size()) {
-            return this.rowIndexOrder.get(rowPosition).intValue();
+            return this.rowIndexOrder.get(rowPosition);
         } else {
             return -1;
         }
@@ -296,8 +304,7 @@
 
     @Override
     public int getRowPositionByIndex(int rowIndex) {
-        Integer result = this.indexPositionMapping.get(rowIndex);
-        return (result != null) ? result : -1;
+        return this.indexPositionMapping.getIfAbsent(rowIndex, -1);
     }
 
     @Override
@@ -314,16 +321,15 @@
 
     @Override
     public Collection<Range> underlyingToLocalRowPositions(ILayer sourceUnderlyingLayer, Collection<Range> underlyingRowPositionRanges) {
-        List<Integer> reorderedRowPositions = new ArrayList<Integer>();
+        MutableIntList reorderedRowPositions = IntLists.mutable.empty();
         for (Range underlyingRowPositionRange : underlyingRowPositionRanges) {
             for (int underlyingRowPosition = underlyingRowPositionRange.start; underlyingRowPosition < underlyingRowPositionRange.end; underlyingRowPosition++) {
                 int localRowPosition = underlyingToLocalRowPosition(sourceUnderlyingLayer, underlyingRowPositionRange.start);
                 reorderedRowPositions.add(localRowPosition);
             }
         }
-        Collections.sort(reorderedRowPositions);
 
-        return PositionUtil.getRanges(reorderedRowPositions);
+        return PositionUtil.getRanges(reorderedRowPositions.toSortedArray());
     }
 
     /**
@@ -344,9 +350,9 @@
             toRowPosition++;
         }
 
-        Integer fromRowIndex = this.rowIndexOrder.get(fromRowPosition);
-        this.rowIndexOrder.add(toRowPosition, fromRowIndex);
-        this.rowIndexOrder.remove(fromRowPosition + (fromRowPosition > toRowPosition ? 1 : 0));
+        int fromRowIndex = this.rowIndexOrder.get(fromRowPosition);
+        this.rowIndexOrder.addAtIndex(toRowPosition, fromRowIndex);
+        this.rowIndexOrder.removeAtIndex(fromRowPosition + (fromRowPosition > toRowPosition ? 1 : 0));
 
         // update index-position mapping
         refreshIndexPositionMapping();
@@ -406,6 +412,23 @@
      *            position to move the rows to
      */
     public void reorderMultipleRowPositions(List<Integer> fromRowPositions, int toRowPosition) {
+        reorderMultipleRowPositions(
+                fromRowPositions.stream().mapToInt(Integer::intValue).toArray(),
+                toRowPosition);
+    }
+
+    /**
+     * Reorders the rows at the given from positions to the <i>TOP</i> of the of
+     * the given to position. Will calculate whether the move is done above the
+     * to position or not regarding the position in the NatTable.
+     *
+     * @param fromRowPositions
+     *            row positions to move
+     * @param toRowPosition
+     *            position to move the rows to
+     * @since 2.0
+     */
+    public void reorderMultipleRowPositions(int[] fromRowPositions, int toRowPosition) {
         boolean reorderToTopEdge;
         if (toRowPosition < getRowCount()) {
             reorderToTopEdge = true;
@@ -429,36 +452,53 @@
      *            not
      */
     public void reorderMultipleRowPositions(List<Integer> fromRowPositions, int toRowPosition, boolean reorderToTopEdge) {
+        reorderMultipleRowPositions(
+                fromRowPositions.stream().mapToInt(Integer::intValue).toArray(),
+                toRowPosition,
+                reorderToTopEdge);
+    }
+
+    /**
+     * Reorders the rows at the given from positions to the <i>TOP</i> of the of
+     * the given to position.
+     *
+     * @param fromRowPositions
+     *            row positions to move
+     * @param toRowPosition
+     *            position to move the rows to
+     * @param reorderToTopEdge
+     *            whether the move should be done above the given to position or
+     *            not
+     * @since 2.0
+     */
+    public void reorderMultipleRowPositions(int[] fromRowPositions, int toRowPosition, boolean reorderToTopEdge) {
         // the position collection needs to be sorted so the move works
         // correctly
-        Collections.sort(fromRowPositions);
+        Arrays.sort(fromRowPositions);
 
         // get the indexes before the move operation
-        List<Integer> fromRowIndexes = new ArrayList<Integer>();
-        for (int fromRowPosition : fromRowPositions) {
-            fromRowIndexes.add(getRowIndexByPosition(fromRowPosition));
-        }
+        int[] fromRowIndexes = Arrays.stream(fromRowPositions).map(this::getRowIndexByPosition).toArray();
         int toRowIndex = getRowIndexByPosition(toRowPosition);
 
-        final int fromRowPositionsCount = fromRowPositions.size();
+        final int fromRowPositionsCount = fromRowPositions.length;
 
-        if (toRowPosition > fromRowPositions.get(fromRowPositionsCount - 1)) {
+        if (toRowPosition > fromRowPositions[fromRowPositionsCount - 1]) {
             // Moving from top to bottom
-            int firstRowPosition = fromRowPositions.get(0);
+            int firstRowPosition = fromRowPositions[0];
 
             int moved = 0;
             for (int rowCount = 0; rowCount < fromRowPositionsCount; rowCount++) {
-                final int fromRowPosition = fromRowPositions.get(rowCount) - moved;
+                final int fromRowPosition = fromRowPositions[rowCount] - moved;
                 moveRow(fromRowPosition, toRowPosition, reorderToTopEdge);
                 moved++;
                 if (fromRowPosition < firstRowPosition) {
                     firstRowPosition = fromRowPosition;
                 }
             }
-        } else if (toRowPosition < fromRowPositions.get(fromRowPositionsCount - 1)) {
+        } else if (toRowPosition < fromRowPositions[fromRowPositionsCount - 1]) {
             // Moving from bottom to top
             int targetRowPosition = toRowPosition;
-            for (Integer fromRowPosition : fromRowPositions) {
+            for (int fromRowPosition : fromRowPositions) {
                 final int fromRowPositionInt = fromRowPosition;
                 moveRow(fromRowPositionInt, targetRowPosition++, reorderToTopEdge);
             }
@@ -484,12 +524,32 @@
      * @since 1.6
      */
     public void reorderMultipleRowIndexes(List<Integer> fromRowIndexes, int toRowPosition, boolean reorderToTopEdge) {
+        reorderMultipleRowIndexes(
+                fromRowIndexes.stream().mapToInt(Integer::intValue).toArray(),
+                toRowPosition,
+                reorderToTopEdge);
+    }
+
+    /**
+     * Reorders the given from-rows identified by index to the specified edge of
+     * the row to move to and fires a {@link RowReorderEvent}. This method can
+     * be used to reorder rows that are hidden in a higher level, e.g. to
+     * reorder a row group that has hidden rows.
+     *
+     * @param fromRowIndexes
+     *            row indexes to move
+     * @param toRowPosition
+     *            position to move the rows to
+     * @param reorderToTopEdge
+     *            whether the move should be done above the given to position or
+     *            not
+     *
+     * @since 2.0
+     */
+    public void reorderMultipleRowIndexes(int[] fromRowIndexes, int toRowPosition, boolean reorderToTopEdge) {
         // calculate positions from indexes
-        List<Integer> fromRownPositions = new ArrayList<Integer>(fromRowIndexes.size());
-        for (Integer index : fromRowIndexes) {
-            fromRownPositions.add(getRowPositionByIndex(index));
-        }
-        reorderMultipleRowPositions(fromRownPositions, toRowPosition, reorderToTopEdge);
+        int[] fromRowPositions = Arrays.stream(fromRowIndexes).map(this::getRowPositionByIndex).toArray();
+        reorderMultipleRowPositions(fromRowPositions, toRowPosition, reorderToTopEdge);
     }
 
     /**
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/command/MultiColumnReorderCommand.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/command/MultiColumnReorderCommand.java
index 6f6fda3..ff419cf 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/command/MultiColumnReorderCommand.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/command/MultiColumnReorderCommand.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2019 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -43,7 +43,7 @@
      */
     public MultiColumnReorderCommand(ILayer layer, List<Integer> fromColumnPositions, int toColumnPosition) {
         this(layer,
-                fromColumnPositions,
+                fromColumnPositions.stream().mapToInt(Integer::intValue).toArray(),
                 toColumnPosition < layer.getColumnCount() ? toColumnPosition : toColumnPosition - 1,
                 toColumnPosition < layer.getColumnCount());
     }
@@ -67,10 +67,54 @@
             int toColumnPosition,
             boolean reorderToLeftEdge) {
 
-        this.fromColumnPositionCoordinates = new ArrayList<>(fromColumnPositions.size());
+        this(layer,
+                fromColumnPositions.stream().mapToInt(Integer::intValue).toArray(),
+                toColumnPosition,
+                reorderToLeftEdge);
+    }
+
+    /**
+     *
+     * @param layer
+     *            The layer to which the column positions match.
+     * @param fromColumnPositions
+     *            The column positions to reorder.
+     * @param toColumnPosition
+     *            The target column position to reorder to.
+     *
+     * @since 2.0
+     */
+    public MultiColumnReorderCommand(ILayer layer, int[] fromColumnPositions, int toColumnPosition) {
+        this(layer,
+                fromColumnPositions,
+                toColumnPosition < layer.getColumnCount() ? toColumnPosition : toColumnPosition - 1,
+                toColumnPosition < layer.getColumnCount());
+    }
+
+    /**
+     *
+     * @param layer
+     *            The layer to which the column positions match.
+     * @param fromColumnPositions
+     *            The column positions to reorder.
+     * @param toColumnPosition
+     *            The target column position to reorder to.
+     * @param reorderToLeftEdge
+     *            <code>true</code> if the reorder operation should be done on
+     *            the left edge of the toColumnPosition, <code>false</code> if
+     *            it should be reordered to the right edge.
+     *
+     * @since 2.0
+     */
+    public MultiColumnReorderCommand(
+            ILayer layer,
+            int[] fromColumnPositions,
+            int toColumnPosition,
+            boolean reorderToLeftEdge) {
+
+        this.fromColumnPositionCoordinates = new ArrayList<>(fromColumnPositions.length);
         for (Integer fromColumnPosition : fromColumnPositions) {
-            this.fromColumnPositionCoordinates.add(
-                    new ColumnPositionCoordinate(layer, fromColumnPosition));
+            this.fromColumnPositionCoordinates.add(new ColumnPositionCoordinate(layer, fromColumnPosition));
         }
 
         this.toColumnPositionCoordinate = new ColumnPositionCoordinate(layer, toColumnPosition);
@@ -105,6 +149,21 @@
     }
 
     /**
+     * Returns the column positions that should be reordered on the layer where
+     * this command is processed. Can be the column indexes if
+     * {@link #reorderByIndex} is set to <code>true</code>.
+     *
+     * @return The column positions that should be reordered.
+     * @since 2.0
+     */
+    public int[] getFromColumnPositionsArray() {
+        return this.fromColumnPositionCoordinates.stream()
+                .mapToInt(ColumnPositionCoordinate::getColumnPosition)
+                .sorted()
+                .toArray();
+    }
+
+    /**
      *
      * @return The column position to which the columns should be reordered.
      */
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/command/MultiColumnReorderCommandHandler.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/command/MultiColumnReorderCommandHandler.java
index 00c1cda..89ddadd 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/command/MultiColumnReorderCommandHandler.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/command/MultiColumnReorderCommandHandler.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2019 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -10,8 +10,6 @@
  ******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.reorder.command;
 
-import java.util.List;
-
 import org.eclipse.nebula.widgets.nattable.command.AbstractLayerCommandHandler;
 import org.eclipse.nebula.widgets.nattable.reorder.ColumnReorderLayer;
 
@@ -25,7 +23,7 @@
 
     @Override
     protected boolean doCommand(MultiColumnReorderCommand command) {
-        List<Integer> fromColumnPositions = command.getFromColumnPositions();
+        int[] fromColumnPositions = command.getFromColumnPositionsArray();
         int toColumnPosition = command.getToColumnPosition();
         boolean reorderToLeftEdge = command.isReorderToLeftEdge();
 
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/command/MultiRowReorderCommand.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/command/MultiRowReorderCommand.java
index ed78c41..1d79ba9 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/command/MultiRowReorderCommand.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/command/MultiRowReorderCommand.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2013, 2019 Dirk Fauth and others.
+ * Copyright (c) 2013, 2020 Dirk Fauth and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -57,7 +57,7 @@
      */
     public MultiRowReorderCommand(ILayer layer, List<Integer> fromRowPositions, int toRowPosition) {
         this(layer,
-                fromRowPositions,
+                fromRowPositions.stream().mapToInt(Integer::intValue).toArray(),
                 toRowPosition < layer.getRowCount() ? toRowPosition : toRowPosition - 1,
                 toRowPosition < layer.getRowCount());
     }
@@ -80,13 +80,57 @@
             int toRowPosition,
             boolean reorderToTopEdge) {
 
-        this.fromRowPositionCoordinates = new ArrayList<>(fromRowPositions.size());
-        for (Integer fromRowPosition : fromRowPositions) {
+        this(layer,
+                fromRowPositions.stream().mapToInt(Integer::intValue).toArray(),
+                toRowPosition,
+                reorderToTopEdge);
+    }
+
+    /**
+     *
+     * @param layer
+     *            The layer the positions are related to
+     * @param fromRowPositions
+     *            The positions of the rows that should be reordered
+     * @param toRowPosition
+     *            The position of the row to which the dragged row should be
+     *            dropped
+     *
+     * @since 2.0
+     */
+    public MultiRowReorderCommand(ILayer layer, int[] fromRowPositions, int toRowPosition) {
+        this(layer,
+                fromRowPositions,
+                toRowPosition < layer.getRowCount() ? toRowPosition : toRowPosition - 1,
+                toRowPosition < layer.getRowCount());
+    }
+
+    /**
+     *
+     * @param layer
+     *            The layer the positions are related to
+     * @param fromRowPositions
+     *            The positions of the rows that should be reordered
+     * @param toRowPosition
+     *            The position of the row to which the dragged row should be
+     *            dropped
+     * @param reorderToTopEdge
+     *            Flag to indicate if the row is dragged to the top edge of the
+     *            layer
+     *
+     * @since 2.0
+     */
+    public MultiRowReorderCommand(ILayer layer,
+            int[] fromRowPositions,
+            int toRowPosition,
+            boolean reorderToTopEdge) {
+
+        this.fromRowPositionCoordinates = new ArrayList<>(fromRowPositions.length);
+        for (int fromRowPosition : fromRowPositions) {
             this.fromRowPositionCoordinates.add(new RowPositionCoordinate(layer, fromRowPosition));
         }
 
-        this.toRowPositionCoordinate = new RowPositionCoordinate(layer,
-                toRowPosition);
+        this.toRowPositionCoordinate = new RowPositionCoordinate(layer, toRowPosition);
 
         this.reorderToTopEdge = reorderToTopEdge;
     }
@@ -115,6 +159,17 @@
     }
 
     /**
+     * @return The positions of the rows that should be reordered
+     * @since 2.0
+     */
+    public int[] getFromRowPositionsArray() {
+        return this.fromRowPositionCoordinates.stream()
+                .mapToInt(RowPositionCoordinate::getRowPosition)
+                .sorted()
+                .toArray();
+    }
+
+    /**
      * @return The position of the row to which the dragged rows should be
      *         dropped
      */
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/command/MultiRowReorderCommandHandler.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/command/MultiRowReorderCommandHandler.java
index 1be8cc8..33bc800 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/command/MultiRowReorderCommandHandler.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/command/MultiRowReorderCommandHandler.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2013, 2019 Dirk Fauth and others.
+ * Copyright (c) 2013, 2020 Dirk Fauth and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -10,8 +10,6 @@
  *******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.reorder.command;
 
-import java.util.List;
-
 import org.eclipse.nebula.widgets.nattable.command.AbstractLayerCommandHandler;
 import org.eclipse.nebula.widgets.nattable.reorder.RowReorderLayer;
 
@@ -25,7 +23,7 @@
 
     @Override
     protected boolean doCommand(MultiRowReorderCommand command) {
-        List<Integer> fromRowPositions = command.getFromRowPositions();
+        int[] fromRowPositions = command.getFromRowPositionsArray();
         int toRowPosition = command.getToRowPosition();
         boolean reorderToTopEdge = command.isReorderToTopEdge();
 
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/event/ColumnReorderEvent.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/event/ColumnReorderEvent.java
index d11dc0d..e54f74d 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/event/ColumnReorderEvent.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/event/ColumnReorderEvent.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2019 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -11,10 +11,12 @@
 package org.eclipse.nebula.widgets.nattable.reorder.event;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
+import java.util.stream.Collectors;
 
+import org.eclipse.collections.api.list.primitive.MutableIntList;
+import org.eclipse.collections.impl.factory.primitive.IntLists;
 import org.eclipse.nebula.widgets.nattable.coordinate.PositionUtil;
 import org.eclipse.nebula.widgets.nattable.coordinate.Range;
 import org.eclipse.nebula.widgets.nattable.layer.ILayer;
@@ -30,7 +32,7 @@
     private ILayer beforeLayer;
 
     private Collection<Range> beforeFromColumnPositionRanges;
-    private Collection<Integer> beforeFromColumnIndexes;
+    private MutableIntList beforeFromColumnIndexes;
 
     private int beforeToColumnPosition;
     private int beforeToColumnIndex;
@@ -86,8 +88,8 @@
             int beforeToColumnIndex,
             boolean reorderToLeftEdge) {
         this(layer,
-                Arrays.asList(new Integer[] { Integer.valueOf(beforeFromColumnPosition) }),
-                Arrays.asList(new Integer[] { Integer.valueOf(beforeFromColumnIndex) }),
+                new int[] { beforeFromColumnPosition },
+                new int[] { beforeFromColumnIndex },
                 beforeToColumnPosition,
                 beforeToColumnIndex,
                 reorderToLeftEdge);
@@ -144,17 +146,53 @@
             int beforeToColumnPosition,
             int beforeToColumnIndex,
             boolean reorderToLeftEdge) {
+
+        this(layer,
+                beforeFromColumnPositions.stream().mapToInt(Integer::intValue).toArray(),
+                beforeFromColumnIndexes.stream().mapToInt(Integer::intValue).toArray(),
+                beforeToColumnPosition,
+                beforeToColumnIndex,
+                reorderToLeftEdge);
+    }
+
+    /**
+     *
+     * @param layer
+     *            The layer to which the column positions match.
+     * @param beforeFromColumnPositions
+     *            The column positions that were reordered, before the reorder
+     *            operation was performed.
+     * @param beforeFromColumnIndexes
+     *            The indexes of the reordered positions.
+     * @param beforeToColumnPosition
+     *            The position of the column to which the reorder operation was
+     *            performed, before the reorder operation was performed
+     * @param beforeToColumnIndex
+     *            The index of the column to which the reorder operation was
+     *            performed.
+     * @param reorderToLeftEdge
+     *            whether the reorder operation was performed to the left or the
+     *            right edge.
+     *
+     * @since 2.0
+     */
+    public ColumnReorderEvent(ILayer layer,
+            int[] beforeFromColumnPositions,
+            int[] beforeFromColumnIndexes,
+            int beforeToColumnPosition,
+            int beforeToColumnIndex,
+            boolean reorderToLeftEdge) {
         super(layer);
         this.beforeLayer = layer;
         this.beforeFromColumnPositionRanges = PositionUtil.getRanges(beforeFromColumnPositions);
-        this.beforeFromColumnIndexes = beforeFromColumnIndexes;
+        this.beforeFromColumnIndexes = IntLists.mutable.of(beforeFromColumnIndexes);
         this.beforeToColumnPosition = beforeToColumnPosition;
         this.beforeToColumnIndex = beforeToColumnIndex;
         this.reorderToLeftEdge = reorderToLeftEdge;
 
-        List<Integer> allColumnPositions = new ArrayList<Integer>(beforeFromColumnPositions);
-        allColumnPositions.add(Integer.valueOf(beforeToColumnPosition));
-        setColumnPositionRanges(PositionUtil.getRanges(allColumnPositions));
+        MutableIntList allColumnPositions = IntLists.mutable.of(beforeFromColumnPositions);
+        allColumnPositions.add(beforeToColumnPosition);
+        setColumnPositionRanges(PositionUtil.getRanges(allColumnPositions.toSortedArray()));
     }
 
     /**
@@ -167,7 +205,7 @@
         super(event);
         this.beforeLayer = event.beforeLayer;
         this.beforeFromColumnPositionRanges = new ArrayList<Range>(event.beforeFromColumnPositionRanges);
-        this.beforeFromColumnIndexes = new ArrayList<Integer>(event.beforeFromColumnIndexes);
+        this.beforeFromColumnIndexes = IntLists.mutable.ofAll(event.beforeFromColumnIndexes);
         this.beforeToColumnPosition = event.beforeToColumnPosition;
         this.beforeToColumnIndex = event.beforeToColumnIndex;
         this.reorderToLeftEdge = event.reorderToLeftEdge;
@@ -179,7 +217,16 @@
      * @since 1.6
      */
     public Collection<Integer> getBeforeFromColumnIndexes() {
-        return this.beforeFromColumnIndexes;
+        return this.beforeFromColumnIndexes.primitiveStream().boxed().collect(Collectors.toList());
+    }
+
+    /**
+     *
+     * @return The indexes of the reordered columns.
+     * @since 2.0
+     */
+    public int[] getBeforeFromColumnIndexesArray() {
+        return this.beforeFromColumnIndexes.toSortedArray();
     }
 
     /**
@@ -212,7 +259,7 @@
     /**
      * Setter for the beforeToColumnIndex that needs to be called used in case a
      * reorder operation was performed to a hidden column.
-     * 
+     *
      * @param beforeIndex
      *            The index of the column to which the reorder operation was
      *            performed.
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/event/RowReorderEvent.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/event/RowReorderEvent.java
index d8e1a04..c20ba85 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/event/RowReorderEvent.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/reorder/event/RowReorderEvent.java
@@ -1,20 +1,22 @@
 /*******************************************************************************
- * Copyright (c) 2013, 2019 Dirk Fauth and others.
+ * Copyright (c) 2013, 2020 Dirk Fauth and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
- *    Dirk Fauth <dirk.fauth@gmail.com> - initial API and implementation
+ *    Dirk Fauth <dirk.fauth@googlemail.com> - initial API and implementation
  *******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.reorder.event;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
+import java.util.stream.Collectors;
 
+import org.eclipse.collections.api.list.primitive.MutableIntList;
+import org.eclipse.collections.impl.factory.primitive.IntLists;
 import org.eclipse.nebula.widgets.nattable.coordinate.PositionUtil;
 import org.eclipse.nebula.widgets.nattable.coordinate.Range;
 import org.eclipse.nebula.widgets.nattable.layer.ILayer;
@@ -30,7 +32,7 @@
     private ILayer beforeLayer;
 
     private Collection<Range> beforeFromRowPositionRanges;
-    private Collection<Integer> beforeFromRowIndexes;
+    private MutableIntList beforeFromRowIndexes;
 
     private int beforeToRowPosition;
     private int beforeToRowIndex;
@@ -85,8 +87,8 @@
             int beforeToRowIndex,
             boolean reorderToTopEdge) {
         this(layer,
-                Arrays.asList(new Integer[] { Integer.valueOf(beforeFromRowPosition) }),
-                Arrays.asList(new Integer[] { Integer.valueOf(beforeFromRowIndex) }),
+                new int[] { beforeFromRowPosition },
+                new int[] { beforeFromRowIndex },
                 beforeToRowPosition,
                 beforeToRowIndex,
                 reorderToTopEdge);
@@ -142,17 +144,53 @@
             int beforeToRowPosition,
             int beforeToRowIndex,
             boolean reorderToTopEdge) {
+
+        this(layer,
+                beforeFromRowPositions.stream().mapToInt(Integer::intValue).toArray(),
+                beforeFromRowIndexes.stream().mapToInt(Integer::intValue).toArray(),
+                beforeToRowPosition,
+                beforeToRowIndex,
+                reorderToTopEdge);
+    }
+
+    /**
+     *
+     * @param layer
+     *            The layer to which the row positions match.
+     * @param beforeFromRowPositions
+     *            The row positions that were reordered, before the reorder
+     *            operation was performed.
+     * @param beforeFromRowIndexes
+     *            The indexes of the reordered positions.
+     * @param beforeToRowPosition
+     *            The position of the row to which the reorder operation was
+     *            performed, before the reorder operation was performed
+     * @param beforeToRowIndex
+     *            The index of the row to which the reorder operation was
+     *            performed.
+     * @param reorderToTopEdge
+     *            whether the reorder operation was performed to the top or the
+     *            bottom edge.
+     *
+     * @since 2.0
+     */
+    public RowReorderEvent(ILayer layer,
+            int[] beforeFromRowPositions,
+            int[] beforeFromRowIndexes,
+            int beforeToRowPosition,
+            int beforeToRowIndex,
+            boolean reorderToTopEdge) {
         super(layer);
         this.beforeLayer = layer;
         this.beforeFromRowPositionRanges = PositionUtil.getRanges(beforeFromRowPositions);
-        this.beforeFromRowIndexes = beforeFromRowIndexes;
+        this.beforeFromRowIndexes = IntLists.mutable.of(beforeFromRowIndexes);
         this.beforeToRowPosition = beforeToRowPosition;
         this.beforeToRowIndex = beforeToRowIndex;
         this.reorderToTopEdge = reorderToTopEdge;
 
-        List<Integer> allColumnPositions = new ArrayList<Integer>(beforeFromRowPositions);
-        allColumnPositions.add(Integer.valueOf(beforeToRowPosition));
-        setRowPositionRanges(PositionUtil.getRanges(allColumnPositions));
+        MutableIntList allColumnPositions = IntLists.mutable.of(beforeFromRowPositions);
+        allColumnPositions.add(beforeToRowPosition);
+        setRowPositionRanges(PositionUtil.getRanges(allColumnPositions.toSortedArray()));
     }
 
     /**
@@ -165,7 +203,7 @@
         super(event);
         this.beforeLayer = event.beforeLayer;
         this.beforeFromRowPositionRanges = event.beforeFromRowPositionRanges;
-        this.beforeFromRowIndexes = new ArrayList<Integer>(event.beforeFromRowIndexes);
+        this.beforeFromRowIndexes = IntLists.mutable.ofAll(event.beforeFromRowIndexes);
         this.beforeToRowPosition = event.beforeToRowPosition;
         this.beforeToRowIndex = event.beforeToRowIndex;
         this.reorderToTopEdge = event.reorderToTopEdge;
@@ -177,7 +215,16 @@
      * @since 1.6
      */
     public Collection<Integer> getBeforeFromRowIndexes() {
-        return this.beforeFromRowIndexes;
+        return this.beforeFromRowIndexes.primitiveStream().boxed().collect(Collectors.toList());
+    }
+
+    /**
+     *
+     * @return The indexes of the reordered rows.
+     * @since 2.0
+     */
+    public int[] getBeforeFromRowIndexesArray() {
+        return this.beforeFromRowIndexes.toSortedArray();
     }
 
     /**
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/resize/command/MultiColumnResizeCommandHandler.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/resize/command/MultiColumnResizeCommandHandler.java
index b806b2f..79bb903 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/resize/command/MultiColumnResizeCommandHandler.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/resize/command/MultiColumnResizeCommandHandler.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2018 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -10,7 +10,6 @@
  ******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.resize.command;
 
-import java.util.Collection;
 import java.util.List;
 
 import org.eclipse.nebula.widgets.nattable.command.AbstractLayerCommandHandler;
@@ -34,7 +33,7 @@
 
     @Override
     protected boolean doCommand(MultiColumnResizeCommand command) {
-        Collection<Integer> columnPositions = command.getColumnPositions();
+        int[] columnPositions = command.getColumnPositionsArray();
 
         for (int columnPosition : columnPositions) {
             int newColumnWidth = command.downScaleValue()
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/resize/command/MultiRowResizeCommandHandler.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/resize/command/MultiRowResizeCommandHandler.java
index af88fe5..c392e70 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/resize/command/MultiRowResizeCommandHandler.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/resize/command/MultiRowResizeCommandHandler.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2018 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -10,7 +10,6 @@
  ******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.resize.command;
 
-import java.util.Collection;
 import java.util.List;
 
 import org.eclipse.nebula.widgets.nattable.command.AbstractLayerCommandHandler;
@@ -34,7 +33,7 @@
 
     @Override
     protected boolean doCommand(MultiRowResizeCommand command) {
-        Collection<Integer> rowPositions = command.getRowPositions();
+        int[] rowPositions = command.getRowPositionsArray();
 
         for (int rowPosition : rowPositions) {
             int newRowHeight = command.downScaleValue()
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/selection/RowSelectionModel.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/selection/RowSelectionModel.java
index 0bf6232..ac2934c 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/selection/RowSelectionModel.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/selection/RowSelectionModel.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2018 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -22,6 +22,7 @@
 import java.util.Set;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.stream.IntStream;
 
 import org.eclipse.nebula.widgets.nattable.coordinate.Range;
 import org.eclipse.nebula.widgets.nattable.data.IRowDataProvider;
@@ -106,7 +107,7 @@
                 range.height = 1;
             }
 
-            Map<Serializable, R> rowsToSelect = new HashMap<Serializable, R>();
+            HashMap<Serializable, R> rowsToSelect = new HashMap<>();
 
             int maxY = Math.min(range.y + range.height, this.selectionLayer.getRowCount());
             for (int rowPosition = range.y; rowPosition < maxY; rowPosition++) {
@@ -193,7 +194,7 @@
 
     @Override
     public List<Rectangle> getSelections() {
-        List<Rectangle> selectionRectangles = new ArrayList<Rectangle>(this.selectedRows.size());
+        ArrayList<Rectangle> selectionRectangles = new ArrayList<>(this.selectedRows.size());
 
         this.selectionsLock.readLock().lock();
 
@@ -277,8 +278,7 @@
     }
 
     @Override
-    public boolean isColumnPositionFullySelected(
-            int columnPosition, int fullySelectedColumnRowCount) {
+    public boolean isColumnPositionFullySelected(int columnPosition, int fullySelectedColumnRowCount) {
         this.selectionsLock.readLock().lock();
 
         try {
@@ -298,7 +298,7 @@
 
     @Override
     public List<R> getSelectedRowObjects() {
-        final List<R> rowObjects = new ArrayList<R>(this.selectedRows.size());
+    	ArrayList<R> rowObjects = new ArrayList<>(this.selectedRows.size());
 
         this.selectionsLock.readLock().lock();
         try {
@@ -323,7 +323,7 @@
 
     @Override
     public Set<Range> getSelectedRowPositions() {
-        Set<Range> selectedRowRanges = new HashSet<Range>();
+        HashSet<Range> selectedRowRanges = new HashSet<>();
 
         this.selectionsLock.readLock().lock();
 
@@ -425,7 +425,7 @@
         if (event.isVerticalStructureChanged()) {
             // the change is already done and we don't know about indexes, so we
             // need to check if the selected objects still exist
-            Collection<Serializable> keysToRemove = new ArrayList<Serializable>();
+            ArrayList<Serializable> keysToRemove = new ArrayList<>();
             for (Map.Entry<Serializable, R> entry : this.selectedRows.entrySet()) {
                 int rowIndex = this.rowDataProvider.indexOfRowObject(entry.getValue());
                 if (rowIndex == -1) {
@@ -447,19 +447,19 @@
             // selection we use all rows in the event to indicate the selection
             // change for all deleted rows
             if (!keysToRemove.isEmpty()) {
-                Collection<Integer> rowPositions = new HashSet<Integer>();
                 Collection<StructuralDiff> diffs = event.getRowDiffs();
+
+                int[] rowPositions = null;
                 if (diffs != null) {
-                    for (StructuralDiff rowDiff : diffs) {
-                        if (rowDiff.getDiffType() != null
-                                && rowDiff.getDiffType().equals(DiffTypeEnum.DELETE)) {
-                            Range beforePositionRange = rowDiff.getBeforePositionRange();
-                            for (int i = beforePositionRange.start; i < beforePositionRange.end; i++) {
-                                rowPositions.add(i);
-                            }
-                        }
-                    }
+                    rowPositions = diffs.stream()
+                            .filter(diff -> diff.getDiffType() != null && diff.getDiffType().equals(DiffTypeEnum.DELETE))
+                            .map(rowDiff -> rowDiff.getBeforePositionRange())
+                            .flatMapToInt(range -> IntStream.range(range.start, range.end))
+                            .toArray();
+                } else {
+                    rowPositions = new int[0];
                 }
+
                 // if there is no diff in the event we assume everything has
                 // changed, in such a case we are not able to fire an
                 // appropriate event the layer stack upwards since it will be
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/selection/SelectRowCommandHandler.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/selection/SelectRowCommandHandler.java
index a8c2b75..d6701af 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/selection/SelectRowCommandHandler.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/selection/SelectRowCommandHandler.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2018 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -22,7 +22,7 @@
 
 import java.util.Collection;
 import java.util.HashSet;
-import java.util.Set;
+import java.util.stream.IntStream;
 
 import org.eclipse.nebula.widgets.nattable.command.ILayerCommandHandler;
 import org.eclipse.nebula.widgets.nattable.coordinate.Range;
@@ -47,7 +47,7 @@
         if (command.convertToTargetLayer(this.selectionLayer)) {
             selectRows(
                     command.getColumnPosition(),
-                    command.getRowPositions(),
+                    command.getRowPositionsArray(),
                     command.isWithShiftMask(),
                     command.isWithControlMask(),
                     command.getRowPositionToMoveIntoViewport());
@@ -71,13 +71,50 @@
      * @param rowPositionToMoveIntoViewport
      *            Information which row should be moved to the viewport,
      *            transported by the {@link SelectRowsCommand}.
+     *
+     * @deprecated Use {@link #selectRows(int, int[], boolean, boolean, int)}
+     *             with primitive values.
      */
+    @Deprecated
     protected void selectRows(
             int columnPosition, Collection<Integer> rowPositions,
             boolean withShiftMask, boolean withControlMask,
             int rowPositionToMoveIntoViewport) {
 
-        Set<Range> changedRowRanges = new HashSet<Range>();
+        selectRows(
+                columnPosition,
+                rowPositions.stream().mapToInt(Integer::intValue).toArray(),
+                withShiftMask,
+                withControlMask,
+                rowPositionToMoveIntoViewport);
+    }
+
+    /**
+     * Performs row selection based on the given informations and fires a
+     * {@link RowSelectionEvent} for the changed selection.
+     *
+     * @param columnPosition
+     *            The column position of the {@link SelectRowsCommand}.
+     * @param rowPositions
+     *            The row position of the {@link SelectRowsCommand}.
+     * @param withShiftMask
+     *            The shift mask information of the {@link SelectRowsCommand}.
+     * @param withControlMask
+     *            The control mask information of the {@link SelectRowsCommand}.
+     * @param rowPositionToMoveIntoViewport
+     *            Information which row should be moved to the viewport,
+     *            transported by the {@link SelectRowsCommand}.
+     *
+     * @since 2.0
+     */
+    protected void selectRows(
+            int columnPosition,
+            int[] rowPositions,
+            boolean withShiftMask,
+            boolean withControlMask,
+            int rowPositionToMoveIntoViewport) {
+
+        HashSet<Range> changedRowRanges = new HashSet<Range>();
 
         for (int rowPosition : rowPositions) {
             changedRowRanges.addAll(
@@ -86,12 +123,10 @@
                             withShiftMask, withControlMask));
         }
 
-        Set<Integer> changedRows = new HashSet<Integer>();
-        for (Range range : changedRowRanges) {
-            for (int i = range.start; i < range.end; i++) {
-                changedRows.add(Integer.valueOf(i));
-            }
-        }
+        int[] changedRows = changedRowRanges.stream()
+                .flatMapToInt(range -> IntStream.range(range.start, range.end))
+                .toArray();
+
         this.selectionLayer.fireLayerEvent(
                 new RowSelectionEvent(
                         this.selectionLayer, changedRows, rowPositionToMoveIntoViewport, withShiftMask, withControlMask));
@@ -111,11 +146,11 @@
      *            The control mask information of the {@link SelectRowsCommand}.
      * @return The changed selection.
      */
-    private Set<Range> internalSelectRow(
+    private HashSet<Range> internalSelectRow(
             int columnPosition, int rowPosition,
             boolean withShiftMask, boolean withControlMask) {
 
-        Set<Range> changedRowRanges = new HashSet<Range>();
+        HashSet<Range> changedRowRanges = new HashSet<Range>();
 
         if (noShiftOrControl(withShiftMask, withControlMask)) {
             changedRowRanges.addAll(this.selectionLayer.getSelectedRowPositions());
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/selection/SelectRowGroupCommandHandler.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/selection/SelectRowGroupCommandHandler.java
index 60a8343..fd68728 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/selection/SelectRowGroupCommandHandler.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/selection/SelectRowGroupCommandHandler.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2015 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -18,11 +18,9 @@
 import static org.eclipse.nebula.widgets.nattable.selection.SelectionUtils.isShiftOnly;
 import static org.eclipse.nebula.widgets.nattable.selection.SelectionUtils.noShiftOrControl;
 
-import java.util.ArrayList;
-import java.util.Collections;
+import java.util.Arrays;
 import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.stream.IntStream;
 
 import org.eclipse.nebula.widgets.nattable.command.AbstractLayerCommandHandler;
 import org.eclipse.nebula.widgets.nattable.coordinate.Range;
@@ -31,19 +29,19 @@
 import org.eclipse.nebula.widgets.nattable.group.model.IRowGroupModel;
 import org.eclipse.nebula.widgets.nattable.selection.command.SelectRowGroupsCommand;
 import org.eclipse.nebula.widgets.nattable.selection.event.RowSelectionEvent;
-import org.eclipse.nebula.widgets.nattable.util.ObjectUtils;
 import org.eclipse.swt.graphics.Rectangle;
 
-public class SelectRowGroupCommandHandler<T> extends
-        AbstractLayerCommandHandler<SelectRowGroupsCommand> {
+public class SelectRowGroupCommandHandler<T> extends AbstractLayerCommandHandler<SelectRowGroupsCommand> {
 
     private final IRowGroupModel<T> model;
     private final RowGroupHeaderLayer<T> rowGroupHeaderLayer;
     private final SelectionLayer selectionLayer;
 
-    public SelectRowGroupCommandHandler(IRowGroupModel<T> model,
+    public SelectRowGroupCommandHandler(
+            IRowGroupModel<T> model,
             SelectionLayer selectionLayer,
             RowGroupHeaderLayer<T> rowGroupHeaderLayer) {
+
         this.model = model;
         this.selectionLayer = selectionLayer;
         this.rowGroupHeaderLayer = rowGroupHeaderLayer;
@@ -56,80 +54,115 @@
 
     @Override
     protected boolean doCommand(SelectRowGroupsCommand command) {
-        final List<Integer> rowIndexes = RowGroupUtils.getRowIndexesInGroup(
-                this.model, this.rowGroupHeaderLayer.getRowIndexByPosition(command
-                        .getRowPosition()));
-        final List<Integer> rowPositions = RowGroupUtils
-                .getRowPositionsInGroup(this.selectionLayer, rowIndexes);
-        selectRows(command.getColumnPosition(), rowPositions,
-                command.isWithShiftMask(), command.isWithControlMask(),
+        int[] rowIndexes = RowGroupUtils.getRowIndexesInGroupAsArray(
+                this.model,
+                this.rowGroupHeaderLayer.getRowIndexByPosition(command.getRowPosition()));
+        int[] rowPositions = RowGroupUtils.getRowPositionsInGroup(
+                this.selectionLayer,
+                rowIndexes);
+        selectRows(
+                command.getColumnPosition(),
+                rowPositions,
+                command.isWithShiftMask(),
+                command.isWithControlMask(),
                 command.getRowPositionToMoveIntoViewport(),
                 command.isMoveAnchorToTopOfGroup());
         return true;
     }
 
-    protected void selectRows(int columnPosition, List<Integer> rowPositions,
-            boolean withShiftMask, boolean withControlMask,
-            int rowPositionToMoveIntoViewport, boolean moveAnchorToTopOfGroup) {
-        Set<Range> changedRowRanges = new HashSet<Range>();
+    /**
+     * Trigger the selection of rows.
+     *
+     * @param columnPosition
+     *            column position needed for the selection anchor.
+     * @param rowPositions
+     *            the row to select.
+     * @param withShiftMask
+     *            if shift mask selection behavior is enabled or not.
+     * @param withControlMask
+     *            if control mask selection behavior is enabled or not.
+     * @param rowPositionToMoveIntoViewport
+     *            <code>true</code> if the selected row should be moved into the
+     *            viewport to make it visible.
+     * @param moveAnchorToTopOfGroup
+     *            <code>true</code> if the selection anchor should be moved to
+     *            the first row in the group.
+     *
+     * @since 2.0
+     */
+    protected void selectRows(
+            int columnPosition,
+            int[] rowPositions,
+            boolean withShiftMask,
+            boolean withControlMask,
+            int rowPositionToMoveIntoViewport,
+            boolean moveAnchorToTopOfGroup) {
 
-        if (rowPositions.size() > 0) {
-            changedRowRanges.addAll(internalSelectRow(columnPosition,
-                    rowPositions.get(0), rowPositions.size(), withShiftMask,
-                    withControlMask, moveAnchorToTopOfGroup));
+        HashSet<Range> changedRowRanges = new HashSet<Range>();
+
+        if (rowPositions.length > 0) {
+            changedRowRanges.addAll(internalSelectRow(
+                    columnPosition,
+                    rowPositions[0],
+                    rowPositions.length,
+                    withShiftMask,
+                    withControlMask,
+                    moveAnchorToTopOfGroup));
         }
 
-        Set<Integer> changedRows = new HashSet<Integer>();
-        for (Range range : changedRowRanges) {
-            for (int i = range.start; i < range.end; i++) {
-                changedRows.add(Integer.valueOf(i));
-            }
-        }
-        this.selectionLayer.fireLayerEvent(new RowSelectionEvent(this.selectionLayer,
-                changedRows, rowPositionToMoveIntoViewport, withShiftMask, withControlMask));
+        int[] changedRows = changedRowRanges.stream()
+                .flatMapToInt(range -> IntStream.range(range.start, range.end))
+                .toArray();
+        this.selectionLayer.fireLayerEvent(
+                new RowSelectionEvent(
+                        this.selectionLayer,
+                        changedRows,
+                        rowPositionToMoveIntoViewport,
+                        withShiftMask,
+                        withControlMask));
     }
 
-    private Set<Range> internalSelectRow(int columnPosition, int rowPosition,
-            int rowCount, boolean withShiftMask, boolean withControlMask,
+    private HashSet<Range> internalSelectRow(
+            int columnPosition,
+            int rowPosition,
+            int rowCount,
+            boolean withShiftMask,
+            boolean withControlMask,
             boolean moveAnchorToTopOfGroup) {
-        Set<Range> changedRowRanges = new HashSet<Range>();
+
+        HashSet<Range> changedRowRanges = new HashSet<Range>();
 
         if (noShiftOrControl(withShiftMask, withControlMask)) {
             changedRowRanges.addAll(this.selectionLayer.getSelectedRowPositions());
             this.selectionLayer.clear(false);
-            this.selectionLayer.selectCell(0, rowPosition, withShiftMask,
-                    withControlMask);
-            this.selectionLayer.selectRegion(0, rowPosition,
-                    this.selectionLayer.getColumnCount(), rowCount);
-            changedRowRanges
-                    .add(new Range(rowPosition, rowPosition + rowCount));
+            this.selectionLayer.selectCell(0, rowPosition, withShiftMask, withControlMask);
+            this.selectionLayer.selectRegion(
+                    0,
+                    rowPosition,
+                    this.selectionLayer.getColumnCount(),
+                    rowCount);
+            changedRowRanges.add(new Range(rowPosition, rowPosition + rowCount));
         } else if (isControlOnly(withShiftMask, withControlMask)) {
-            changedRowRanges.add(selectRowWithCtrlKey(columnPosition,
-                    rowPosition, rowCount));
+            changedRowRanges.add(selectRowWithCtrlKey(columnPosition, rowPosition, rowCount));
         } else if (isShiftOnly(withShiftMask, withControlMask)) {
-            changedRowRanges.add(selectRowWithShiftKey(columnPosition,
-                    rowPosition, rowCount));
+            changedRowRanges.add(selectRowWithShiftKey(columnPosition, rowPosition, rowCount));
         }
         if (moveAnchorToTopOfGroup) {
             this.selectionLayer.moveSelectionAnchor(columnPosition, rowPosition);
         }
-        this.selectionLayer.getLastSelectedCellPosition().columnPosition = this.selectionLayer
-                .getColumnCount() - 1;
+        this.selectionLayer.getLastSelectedCellPosition().columnPosition = this.selectionLayer.getColumnCount() - 1;
         this.selectionLayer.getLastSelectedCellPosition().rowPosition = rowPosition;
 
         return changedRowRanges;
     }
 
-    private Range selectRowWithCtrlKey(int columnPosition, int rowPosition,
-            int rowCount) {
-        Rectangle selectedRowRectangle = new Rectangle(0, rowPosition,
-                this.selectionLayer.getColumnCount(), rowCount);
+    private Range selectRowWithCtrlKey(int columnPosition, int rowPosition, int rowCount) {
+        Rectangle selectedRowRectangle = new Rectangle(0, rowPosition, this.selectionLayer.getColumnCount(), rowCount);
 
         if (this.selectionLayer.isRowPositionFullySelected(rowPosition)) {
             this.selectionLayer.clearSelection(selectedRowRectangle);
             if (this.selectionLayer.getLastSelectedRegion() != null
-                    && this.selectionLayer.getLastSelectedRegion().equals(
-                            selectedRowRectangle)) {
+                    && this.selectionLayer.getLastSelectedRegion().equals(selectedRowRectangle)) {
                 this.selectionLayer.setLastSelectedRegion(null);
             }
         } else {
@@ -141,34 +174,35 @@
                         this.selectionLayer.getLastSelectedRegion().width,
                         this.selectionLayer.getLastSelectedRegion().height));
             }
-            this.selectionLayer.selectRegion(0, rowPosition,
-                    this.selectionLayer.getColumnCount(), rowCount);
+            this.selectionLayer.selectRegion(
+                    0,
+                    rowPosition,
+                    this.selectionLayer.getColumnCount(),
+                    rowCount);
         }
 
         return new Range(rowPosition, rowPosition + 1);
     }
 
-    private Range selectRowWithShiftKey(int columnPosition, int rowPosition,
-            int rowCount) {
+    private Range selectRowWithShiftKey(int columnPosition, int rowPosition, int rowCount) {
         if (this.selectionLayer.getLastSelectedRegion() != null) {
-            int start = Math.min(this.selectionLayer.getLastSelectedRegion().y,
-                    rowPosition);
-            int end = Math.max(this.selectionLayer.getLastSelectedRegion().y,
-                    rowPosition);
+            int start = Math.min(this.selectionLayer.getLastSelectedRegion().y, rowPosition);
+            int end = Math.max(this.selectionLayer.getLastSelectedRegion().y, rowPosition);
 
             for (int i = start; i <= end; i++) {
                 int index = this.selectionLayer.getRowIndexByPosition(i);
                 if (RowGroupUtils.isPartOfAGroup(this.model, index)
                         && !this.selectionLayer.isRowPositionFullySelected(i)) {
-                    List<Integer> rowPositions = new ArrayList<Integer>(
-                            RowGroupUtils.getRowPositionsInGroup(
-                                    this.selectionLayer, RowGroupUtils
-                                            .getRowIndexesInGroup(this.model, index)));
-                    Collections.sort(rowPositions);
-                    this.selectionLayer.selectRegion(0, rowPositions.get(0),
+                    int[] rowPositions = RowGroupUtils.getRowPositionsInGroup(
+                            this.selectionLayer,
+                            RowGroupUtils.getRowIndexesInGroupAsArray(this.model, index));
+                    Arrays.sort(rowPositions);
+                    this.selectionLayer.selectRegion(
+                            0,
+                            rowPositions[0],
                             this.selectionLayer.getColumnCount(),
-                            rowPositions.size());
-                    i = ObjectUtils.getLastElement(rowPositions);
+                            rowPositions.length);
+                    i = rowPositions[rowPositions.length - 1];
                 }
             }
 
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/selection/SelectionLayer.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/selection/SelectionLayer.java
index 260c33e..7012c19 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/selection/SelectionLayer.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/selection/SelectionLayer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2019 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -15,7 +15,6 @@
  ******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.selection;
 
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
@@ -564,7 +563,7 @@
             boolean withShiftMask, boolean withControlMask) {
         this.selectRowCommandHandler.selectRows(
                 columnPosition,
-                Arrays.asList(Integer.valueOf(rowPosition)),
+                new int[] { rowPosition },
                 withShiftMask,
                 withControlMask,
                 rowPosition);
@@ -731,7 +730,7 @@
      *         <code>false</code> otherwise
      */
     protected boolean handleMultiColumnHideCommand(MultiColumnHideCommand command) {
-        for (int columnPosition : command.getColumnPositions()) {
+        for (int columnPosition : command.getColumnPositionsArray()) {
             if (isColumnPositionFullySelected(columnPosition)) {
                 Rectangle selection = new Rectangle(
                         columnPosition,
@@ -783,7 +782,7 @@
      *         <code>false</code> otherwise
      */
     protected boolean handleMultiRowHideCommand(MultiRowHideCommand command) {
-        for (int rowPosition : command.getRowPositions()) {
+        for (int rowPosition : command.getRowPositionsArray()) {
             if (isRowPositionFullySelected(rowPosition)) {
                 Rectangle selection = new Rectangle(
                         0,
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/selection/SelectionModel.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/selection/SelectionModel.java
index 862492f..95427a3 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/selection/SelectionModel.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/selection/SelectionModel.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2018 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -567,7 +567,7 @@
         Collections.sort(selectionRectanglesInRow, new Comparator<Rectangle>() {
             @Override
             public int compare(Rectangle rectangle1, Rectangle rectangle2) {
-                return new Integer(rectangle1.x).compareTo(new Integer(rectangle2.x));
+                return Integer.valueOf(rectangle1.x).compareTo(Integer.valueOf(rectangle2.x));
             }
         });
     }
@@ -577,7 +577,7 @@
                 new Comparator<Rectangle>() {
                     @Override
                     public int compare(Rectangle rectangle1, Rectangle rectangle2) {
-                        return new Integer(rectangle1.y).compareTo(new Integer(rectangle2.y));
+                        return Integer.valueOf(rectangle1.y).compareTo(Integer.valueOf(rectangle2.y));
                     }
                 });
     }
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/selection/event/RowSelectionEvent.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/selection/event/RowSelectionEvent.java
index 14d92bc..d27763b 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/selection/event/RowSelectionEvent.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/selection/event/RowSelectionEvent.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2018 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -69,7 +69,11 @@
      * @param withControlMask
      *            Boolean to determinate if the control mask is used.
      * @since 1.4
+     * @deprecated Replaced by
+     *             {@link #RowSelectionEvent(SelectionLayer, int[], int, boolean, boolean)}
+     *             with primitive values.
      */
+    @Deprecated
     public RowSelectionEvent(SelectionLayer selectionLayer,
             Collection<Integer> rowPositions, int rowPositionToMoveIntoViewport,
             boolean withShiftMask, boolean withControlMask) {
@@ -85,6 +89,31 @@
      *
      * @param selectionLayer
      *            The selection layer.
+     * @param rowPositions
+     *            The positions of the rows.
+     * @param rowPositionToMoveIntoViewport
+     *            The row position to move into the viewport.
+     * @param withShiftMask
+     *            Boolean to determinate if the shift mask is used.
+     * @param withControlMask
+     *            Boolean to determinate if the control mask is used.
+     * @since 2.0
+     */
+    public RowSelectionEvent(SelectionLayer selectionLayer,
+            int[] rowPositions, int rowPositionToMoveIntoViewport,
+            boolean withShiftMask, boolean withControlMask) {
+        super(selectionLayer, PositionUtil.getRanges(rowPositions));
+        this.selectionLayer = selectionLayer;
+        this.rowPositionToMoveIntoViewport = rowPositionToMoveIntoViewport;
+        this.withShiftMask = withShiftMask;
+        this.withControlMask = withControlMask;
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param selectionLayer
+     *            The selection layer.
      * @param rowPositionRange
      *            The range of row positions that where selected.
      * @param rowPositionToMoveIntoViewport
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/summaryrow/SummaryRowLayer.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/summaryrow/SummaryRowLayer.java
index 67b9f85..f2a4a4e 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/summaryrow/SummaryRowLayer.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/summaryrow/SummaryRowLayer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2017 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -300,7 +300,7 @@
             }
         } else if (command instanceof MultiRowResizeCommand && command.convertToTargetLayer(this)) {
             MultiRowResizeCommand rowResizeCommand = (MultiRowResizeCommand) command;
-            for (int row : rowResizeCommand.getRowPositions()) {
+            for (int row : rowResizeCommand.getRowPositionsArray()) {
                 if (isSummaryRowPosition(row)) {
                     if (rowResizeCommand.downScaleValue() && this.dpiConverter != null) {
                         this.summaryRowHeight = this.dpiConverter.convertDpiToPixel(rowResizeCommand.getRowHeight(row));
diff --git a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/tree/TreeLayer.java b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/tree/TreeLayer.java
index 0d77e83..618b3be 100644
--- a/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/tree/TreeLayer.java
+++ b/org.eclipse.nebula.widgets.nattable.core/src/org/eclipse/nebula/widgets/nattable/tree/TreeLayer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2019 Original authors and others.
+ * Copyright (c) 2012, 2020 Original authors and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -12,13 +12,15 @@
 package org.eclipse.nebula.widgets.nattable.tree;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
-import java.util.Set;
-import java.util.TreeSet;
+import java.util.stream.Collectors;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.eclipse.collections.api.set.primitive.MutableIntSet;
+import org.eclipse.collections.impl.factory.primitive.IntSets;
 import org.eclipse.nebula.widgets.nattable.command.ILayerCommand;
 import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
 import org.eclipse.nebula.widgets.nattable.hideshow.AbstractRowHideShowLayer;
@@ -71,7 +73,7 @@
      * where the hide/show approach is not used (e.g. GlazedListTreeRowModel)
      * </p>
      */
-    private final Set<Integer> hiddenRowIndexes = new TreeSet<Integer>();
+    private MutableIntSet hiddenRowIndexes = IntSets.mutable.empty();
 
     /**
      * The IndentedTreeImagePainter that paints indentation to the left of the
@@ -319,13 +321,22 @@
 
     @Override
     public boolean isRowIndexHidden(int rowIndex) {
-        return this.hiddenRowIndexes.contains(Integer.valueOf(rowIndex))
-                || isHiddenInUnderlyingLayer(rowIndex);
+        return this.hiddenRowIndexes.contains(rowIndex) || isHiddenInUnderlyingLayer(rowIndex);
     }
 
     @Override
     public Collection<Integer> getHiddenRowIndexes() {
-        return this.hiddenRowIndexes;
+        return this.hiddenRowIndexes.toSortedList().primitiveStream().boxed().collect(Collectors.toList());
+    }
+
+    @Override
+    public int[] getHiddenRowIndexesArray() {
+        return this.hiddenRowIndexes.toSortedArray();
+    }
+
+    @Override
+    public boolean hasHiddenRows() {
+        return !this.hiddenRowIndexes.isEmpty();
     }
 
     /**
@@ -352,38 +363,56 @@
      *            collapsed
      */
     public void collapseTreeRow(int parentIndex) {
-        List<Integer> rowIndexes = this.treeRowModel.collapse(parentIndex);
-        List<Integer> rowPositions = new ArrayList<Integer>(rowIndexes.size());
-        for (Integer rowIndex : rowIndexes) {
-            int rowPos = getRowPositionByIndex(rowIndex);
-            // if the rowPos is negative, it is not visible because of hidden
-            // state in an underlying layer
-            if (rowPos >= 0) {
-                rowPositions.add(rowPos);
-            }
-        }
+        int[] rowIndexes = this.treeRowModel.collapse(parentIndex).stream()
+                .mapToInt(Integer::intValue)
+                .sorted()
+                .toArray();
+
+        // if the rowPos is negative, it is not visible because of hidden
+        // state in an underlying layer
+        int[] rowPositions = Arrays.stream(rowIndexes)
+                .map(this::getRowPositionByIndex)
+                .filter(rowPos -> rowPos >= 0)
+                .sorted()
+                .toArray();
+
+        // collect the indexes that where really hidden in this layer
+        int[] hiddenIndexes = Arrays.stream(rowPositions)
+                .map(this::getRowIndexByPosition)
+                .sorted()
+                .toArray();
+
         this.hiddenRowIndexes.addAll(rowIndexes);
         invalidateCache();
-        fireLayerEvent(new HideRowPositionsEvent(this, rowPositions));
+        fireLayerEvent(new HideRowPositionsEvent(this, rowPositions, hiddenIndexes));
     }
 
     /**
      * Collapses all tree nodes in the tree.
      */
     public void collapseAll() {
-        List<Integer> rowIndexes = this.treeRowModel.collapseAll();
-        List<Integer> rowPositions = new ArrayList<Integer>(rowIndexes.size());
-        for (Integer rowIndex : rowIndexes) {
-            int rowPos = getRowPositionByIndex(rowIndex);
-            // if the rowPos is negative, it is not visible because of hidden
-            // state in an underlying layer
-            if (rowPos >= 0) {
-                rowPositions.add(rowPos);
-            }
-        }
+        int[] rowIndexes = this.treeRowModel.collapseAll().stream()
+                .mapToInt(Integer::intValue)
+                .sorted()
+                .toArray();
+
+        // if the rowPos is negative, it is not visible because of hidden
+        // state in an underlying layer
+        int[] rowPositions = Arrays.stream(rowIndexes)
+                .map(this::getRowPositionByIndex)
+                .filter(rowPos -> rowPos >= 0)
+                .sorted()
+                .toArray();
+
+        // collect the indexes that where really hidden in this layer
+        int[] hiddenIndexes = Arrays.stream(rowPositions)
+                .map(this::getRowIndexByPosition)
+                .sorted()
+                .toArray();
+
         this.hiddenRowIndexes.addAll(rowIndexes);
         invalidateCache();
-        fireLayerEvent(new HideRowPositionsEvent(this, rowPositions));
+        fireLayerEvent(new HideRowPositionsEvent(this, rowPositions, hiddenIndexes));
     }
 
     /**
@@ -394,14 +423,7 @@
      *            expanded
      */
     public void expandTreeRow(int parentIndex) {
-        List<Integer> rowIndexes = this.treeRowModel.expand(parentIndex);
-        // Bug 432865: iterating and removing every single item is faster than
-        // removeAll()
-        for (final Integer expandedChildRowIndex : rowIndexes) {
-            this.hiddenRowIndexes.remove(expandedChildRowIndex);
-        }
-        invalidateCache();
-        fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes));
+        internalExpand(this.treeRowModel.expand(parentIndex));
     }
 
     /**
@@ -415,14 +437,7 @@
      *            The level to which the tree node should be expanded.
      */
     public void expandTreeRowToLevel(int parentIndex, int level) {
-        List<Integer> rowIndexes = this.treeRowModel.expandToLevel(parentIndex, level);
-        // Bug 432865: iterating and removing every single item is faster than
-        // removeAll()
-        for (final Integer expandedChildRowIndex : rowIndexes) {
-            this.hiddenRowIndexes.remove(expandedChildRowIndex);
-        }
-        invalidateCache();
-        fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes));
+        internalExpand(this.treeRowModel.expandToLevel(parentIndex, level));
     }
 
     /**
@@ -430,9 +445,16 @@
      */
     public void expandAll() {
         List<Integer> rowIndexes = this.treeRowModel.expandAll();
-        this.hiddenRowIndexes.clear();
+        // optimization to avoid huge empty collections in memory after clear
+        this.hiddenRowIndexes = IntSets.mutable.empty();
         invalidateCache();
-        fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes));
+        fireLayerEvent(new ShowRowPositionsEvent(
+                this,
+                rowIndexes.stream()
+                        .mapToInt(this::getRowPositionByIndex)
+                        .filter(r -> r >= 0)
+                        .sorted()
+                        .toArray()));
     }
 
     /**
@@ -442,14 +464,26 @@
      *            The level to which the tree node should be expanded.
      */
     public void expandAllToLevel(int level) {
-        List<Integer> rowIndexes = this.treeRowModel.expandToLevel(level);
-        // Bug 432865: iterating and removing every single item is faster than
-        // removeAll()
-        for (final Integer expandedChildRowIndex : rowIndexes) {
-            this.hiddenRowIndexes.remove(expandedChildRowIndex);
-        }
+        internalExpand(this.treeRowModel.expandToLevel(level));
+    }
+
+    /**
+     * Remove the given row indexes from the local collection of hidden row
+     * indexes, invalidate the cache and fire a {@link ShowRowPositionsEvent}.
+     *
+     * @param rowIndexes
+     *            The row indexes to show again.
+     */
+    private void internalExpand(List<Integer> rowIndexes) {
+        this.hiddenRowIndexes.removeAll(rowIndexes.stream().mapToInt(Integer::intValue).toArray());
         invalidateCache();
-        fireLayerEvent(new ShowRowPositionsEvent(this, rowIndexes));
+        fireLayerEvent(new ShowRowPositionsEvent(
+                this,
+                rowIndexes.stream()
+                        .mapToInt(this::getRowPositionByIndex)
+                        .filter(r -> r >= 0)
+                        .sorted()
+                        .toArray()));
     }
 
     /**
@@ -519,7 +553,7 @@
         // transform position to index
         if (command.convertToTargetLayer(this)) {
             List<Integer> rowPositionsToHide = new ArrayList<Integer>();
-            for (Integer rowPos : command.getRowPositions()) {
+            for (int rowPos : command.getRowPositionsArray()) {
                 rowPositionsToHide.add(rowPos);
                 int rowIndex = getRowIndexByPosition(rowPos);
                 if (this.treeRowModel.hasChildren(rowIndex)
diff --git a/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_600_GlazedLists/_604_Tree/_6045_HierarchicalTreeLayerExample.java b/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_600_GlazedLists/_604_Tree/_6045_HierarchicalTreeLayerExample.java
index 085b1d9..c4d7cd8 100644
--- a/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_600_GlazedLists/_604_Tree/_6045_HierarchicalTreeLayerExample.java
+++ b/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_600_GlazedLists/_604_Tree/_6045_HierarchicalTreeLayerExample.java
@@ -11,7 +11,6 @@
 package org.eclipse.nebula.widgets.nattable.examples._600_GlazedLists._604_Tree;
 
 import java.io.Serializable;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -349,8 +348,7 @@
         multiReorderButton.addSelectionListener(new SelectionAdapter() {
             @Override
             public void widgetSelected(SelectionEvent e) {
-                List<Integer> fromColumnPositions = Arrays.asList(4, 5);
-                natTable.doCommand(new MultiColumnReorderCommand(natTable, fromColumnPositions, 8));
+                natTable.doCommand(new MultiColumnReorderCommand(natTable, new int[] { 4, 5 }, 8));
             }
         });
 
diff --git a/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_800_Integration/_814_EditableSortableGroupByWithFilterExample.java b/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_800_Integration/_814_EditableSortableGroupByWithFilterExample.java
index e4424fb..f67cca5 100644
--- a/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_800_Integration/_814_EditableSortableGroupByWithFilterExample.java
+++ b/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_800_Integration/_814_EditableSortableGroupByWithFilterExample.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2013, 2015 Dirk Fauth and others.
+ * Copyright (c) 2013, 2020 Dirk Fauth and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -72,7 +72,7 @@
 import org.eclipse.nebula.widgets.nattable.examples.runner.StandaloneNatExampleRunner;
 import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsEventLayer;
 import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsSortModel;
-import org.eclipse.nebula.widgets.nattable.extension.glazedlists.filterrow.DefaultGlazedListsFilterStrategy;
+import org.eclipse.nebula.widgets.nattable.extension.glazedlists.filterrow.DefaultGlazedListsStaticFilterStrategy;
 import org.eclipse.nebula.widgets.nattable.extension.glazedlists.groupBy.DarkGroupByThemeExtension;
 import org.eclipse.nebula.widgets.nattable.extension.glazedlists.groupBy.DefaultGroupByThemeExtension;
 import org.eclipse.nebula.widgets.nattable.extension.glazedlists.groupBy.GroupByConfigAttributes;
@@ -84,6 +84,7 @@
 import org.eclipse.nebula.widgets.nattable.extension.glazedlists.groupBy.ModernGroupByThemeExtension;
 import org.eclipse.nebula.widgets.nattable.extension.glazedlists.groupBy.summary.IGroupBySummaryProvider;
 import org.eclipse.nebula.widgets.nattable.extension.glazedlists.groupBy.summary.SummationGroupBySummaryProvider;
+import org.eclipse.nebula.widgets.nattable.extension.glazedlists.hideshow.GlazedListsRowHideShowLayer;
 import org.eclipse.nebula.widgets.nattable.filterrow.FilterRowHeaderComposite;
 import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
 import org.eclipse.nebula.widgets.nattable.grid.data.DefaultColumnHeaderDataProvider;
@@ -247,12 +248,16 @@
                 true);
 
         // add the filter row functionality
+        DefaultGlazedListsStaticFilterStrategy<ExtendedPersonWithAddress> filterStrategy =
+                new DefaultGlazedListsStaticFilterStrategy<>(
+                        bodyLayerStack.getFilterList(),
+                        columnPropertyAccessor,
+                        configRegistry);
+        filterStrategy.addStaticFilter(bodyLayerStack.getRowHideShowLayer().getHideRowMatcherEditor());
+
         final FilterRowHeaderComposite<ExtendedPersonWithAddress> filterRowHeaderLayer =
                 new FilterRowHeaderComposite<>(
-                        new DefaultGlazedListsFilterStrategy<>(
-                                bodyLayerStack.getFilterList(),
-                                columnPropertyAccessor,
-                                configRegistry),
+                        filterStrategy,
                         sortHeaderLayer,
                         columnHeaderDataLayer.getDataProvider(),
                         configRegistry);
@@ -463,9 +468,17 @@
             }
 
             @Override
+            protected PopupMenuBuilder createRowHeaderMenu(NatTable natTable) {
+                return new PopupMenuBuilder(natTable)
+                        .withHideRowMenuItem()
+                        .withShowAllRowsMenuItem();
+            }
+
+            @Override
             protected PopupMenuBuilder createCornerMenu(NatTable natTable) {
                 return super.createCornerMenu(natTable)
                         .withShowAllColumnsMenuItem()
+                        .withShowAllRowsMenuItem()
                         .withStateManagerMenuItemProvider()
                         .withMenuItemProvider(new IMenuItemProvider() {
 
@@ -743,6 +756,8 @@
 
         private final GroupByDataLayer<T> bodyDataLayer;
 
+        private final GlazedListsRowHideShowLayer<T> rowHideShowLayer;
+
         private final SelectionLayer selectionLayer;
 
         private final TreeLayer treeLayer;
@@ -819,8 +834,12 @@
                     new ColumnReorderLayer(changeLayer);
             ColumnHideShowLayer columnHideShowLayer =
                     new ColumnHideShowLayer(columnReorderLayer);
+
+            this.rowHideShowLayer =
+                    new GlazedListsRowHideShowLayer<>(columnHideShowLayer, this.bodyDataProvider, rowIdAccessor, this.filterList);
+
             this.selectionLayer =
-                    new SelectionLayer(columnHideShowLayer);
+                    new SelectionLayer(this.rowHideShowLayer);
 
             // add a tree layer to visualize the grouping
             this.treeLayer = new TreeLayer(this.selectionLayer, this.bodyDataLayer.getTreeRowModel());
@@ -865,6 +884,10 @@
         public GroupByModel getGroupByModel() {
             return this.groupByModel;
         }
+
+        public GlazedListsRowHideShowLayer<T> getRowHideShowLayer() {
+            return this.rowHideShowLayer;
+        }
     }
 
     /**
diff --git a/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_800_Integration/_817_TreeLayerWithRowGroupingExample.java b/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_800_Integration/_817_TreeLayerWithRowGroupingExample.java
new file mode 100644
index 0000000..1c0221a
--- /dev/null
+++ b/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_800_Integration/_817_TreeLayerWithRowGroupingExample.java
@@ -0,0 +1,397 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Dirk Fauth and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Dirk Fauth <dirk.fauth@googlemail.com> - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.nebula.widgets.nattable.examples._800_Integration;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.nebula.widgets.nattable.NatTable;
+import org.eclipse.nebula.widgets.nattable.config.AbstractRegistryConfiguration;
+import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes;
+import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration;
+import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
+import org.eclipse.nebula.widgets.nattable.data.IColumnPropertyAccessor;
+import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
+import org.eclipse.nebula.widgets.nattable.data.ListDataProvider;
+import org.eclipse.nebula.widgets.nattable.data.ReflectiveColumnPropertyAccessor;
+import org.eclipse.nebula.widgets.nattable.data.convert.DefaultDateDisplayConverter;
+import org.eclipse.nebula.widgets.nattable.dataset.person.Person;
+import org.eclipse.nebula.widgets.nattable.dataset.person.PersonService;
+import org.eclipse.nebula.widgets.nattable.examples.AbstractNatExample;
+import org.eclipse.nebula.widgets.nattable.examples.runner.StandaloneNatExampleRunner;
+import org.eclipse.nebula.widgets.nattable.grid.data.DefaultColumnHeaderDataProvider;
+import org.eclipse.nebula.widgets.nattable.grid.data.DefaultCornerDataProvider;
+import org.eclipse.nebula.widgets.nattable.grid.data.DefaultRowHeaderDataProvider;
+import org.eclipse.nebula.widgets.nattable.grid.layer.ColumnHeaderLayer;
+import org.eclipse.nebula.widgets.nattable.grid.layer.CornerLayer;
+import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultColumnHeaderDataLayer;
+import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultRowHeaderDataLayer;
+import org.eclipse.nebula.widgets.nattable.grid.layer.GridLayer;
+import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer;
+import org.eclipse.nebula.widgets.nattable.group.performance.RowGroupExpandCollapseLayer;
+import org.eclipse.nebula.widgets.nattable.group.performance.RowGroupHeaderLayer;
+import org.eclipse.nebula.widgets.nattable.hideshow.RowHideShowLayer;
+import org.eclipse.nebula.widgets.nattable.layer.AbstractLayerTransform;
+import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
+import org.eclipse.nebula.widgets.nattable.layer.ILayer;
+import org.eclipse.nebula.widgets.nattable.layer.cell.ColumnLabelAccumulator;
+import org.eclipse.nebula.widgets.nattable.painter.cell.CheckBoxPainter;
+import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
+import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
+import org.eclipse.nebula.widgets.nattable.tree.ITreeData;
+import org.eclipse.nebula.widgets.nattable.tree.ITreeRowModel;
+import org.eclipse.nebula.widgets.nattable.tree.TreeLayer;
+import org.eclipse.nebula.widgets.nattable.tree.TreeRowModel;
+import org.eclipse.nebula.widgets.nattable.tree.command.TreeCollapseAllCommand;
+import org.eclipse.nebula.widgets.nattable.tree.command.TreeExpandAllCommand;
+import org.eclipse.nebula.widgets.nattable.tree.config.TreeLayerExpandCollapseKeyBindings;
+import org.eclipse.nebula.widgets.nattable.ui.menu.HeaderMenuConfiguration;
+import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuBuilder;
+import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/**
+ * Simple example showing how to create a tree within a grid.
+ */
+public class _817_TreeLayerWithRowGroupingExample extends AbstractNatExample {
+
+    public static void main(String[] args) throws Exception {
+        StandaloneNatExampleRunner.run(new _817_TreeLayerWithRowGroupingExample());
+    }
+
+    @Override
+    public String getDescription() {
+        return "This example shows how to create a tree within a grid."
+                + " It will use a child as the parent node for tree structuring.";
+    }
+
+    @Override
+    public Control createExampleControl(Composite parent) {
+        Composite container = new Composite(parent, SWT.NONE);
+        container.setLayout(new GridLayout());
+
+        // property names of the Person class
+        String[] propertyNames = { "lastName", "firstName", "gender", "married", "birthday" };
+
+        // mapping from property to label, needed for column header labels
+        Map<String, String> propertyToLabelMap = new HashMap<>();
+        propertyToLabelMap.put("lastName", "Lastname");
+        propertyToLabelMap.put("firstName", "Firstname");
+        propertyToLabelMap.put("gender", "Gender");
+        propertyToLabelMap.put("married", "Married");
+        propertyToLabelMap.put("birthday", "Birthday");
+
+        IColumnPropertyAccessor<Person> columnPropertyAccessor =
+                new ReflectiveColumnPropertyAccessor<>(propertyNames);
+
+        List<Person> personsWithAddress = PersonService.getFixedPersons();
+        final BodyLayerStack<Person> bodyLayerStack =
+                new BodyLayerStack<>(
+                        personsWithAddress,
+                        columnPropertyAccessor,
+                        new PersonTreeData(personsWithAddress));
+
+        // build the column header layer
+        IDataProvider columnHeaderDataProvider =
+                new DefaultColumnHeaderDataProvider(propertyNames, propertyToLabelMap);
+        DataLayer columnHeaderDataLayer =
+                new DefaultColumnHeaderDataLayer(columnHeaderDataProvider);
+        ILayer columnHeaderLayer =
+                new ColumnHeaderLayer(columnHeaderDataLayer, bodyLayerStack, bodyLayerStack.getSelectionLayer());
+
+        // build the row header layer
+        IDataProvider rowHeaderDataProvider =
+                new DefaultRowHeaderDataProvider(bodyLayerStack.getBodyDataProvider());
+        DataLayer rowHeaderDataLayer =
+                new DefaultRowHeaderDataLayer(rowHeaderDataProvider);
+        ILayer rowHeaderLayer =
+                new RowHeaderLayer(rowHeaderDataLayer, bodyLayerStack, bodyLayerStack.getSelectionLayer());
+        RowGroupHeaderLayer rowGroupHeaderLayer =
+                new RowGroupHeaderLayer(
+                        rowHeaderLayer,
+                        bodyLayerStack.getTreeLayer(),
+                        bodyLayerStack.getSelectionLayer());
+
+        rowGroupHeaderLayer.addGroup("Flanders", 0, 8);
+        rowGroupHeaderLayer.addGroup("Simpsons", 8, 10);
+
+        // build the corner layer
+        IDataProvider cornerDataProvider =
+                new DefaultCornerDataProvider(columnHeaderDataProvider, rowHeaderDataProvider);
+        DataLayer cornerDataLayer =
+                new DataLayer(cornerDataProvider);
+        ILayer cornerLayer =
+                new CornerLayer(cornerDataLayer, rowGroupHeaderLayer, columnHeaderLayer);
+
+        // build the grid layer
+        GridLayer gridLayer =
+                new GridLayer(bodyLayerStack, columnHeaderLayer, rowGroupHeaderLayer, cornerLayer);
+
+        // turn the auto configuration off as we want to add our header menu
+        // configuration
+        NatTable natTable = new NatTable(container, gridLayer, false);
+
+        // as the autoconfiguration of the NatTable is turned off, we have to
+        // add the DefaultNatTableStyleConfiguration manually
+        natTable.addConfiguration(new DefaultNatTableStyleConfiguration());
+
+        natTable.addConfiguration(new AbstractRegistryConfiguration() {
+
+            @Override
+            public void configureRegistry(IConfigRegistry configRegistry) {
+                // register a CheckBoxPainter as CellPainter for the married
+                // information
+                configRegistry.registerConfigAttribute(
+                        CellConfigAttributes.CELL_PAINTER,
+                        new CheckBoxPainter(),
+                        DisplayMode.NORMAL,
+                        ColumnLabelAccumulator.COLUMN_LABEL_PREFIX + 3);
+
+                configRegistry.registerConfigAttribute(
+                        CellConfigAttributes.DISPLAY_CONVERTER,
+                        new DefaultDateDisplayConverter("MM/dd/yyyy"),
+                        DisplayMode.NORMAL,
+                        ColumnLabelAccumulator.COLUMN_LABEL_PREFIX + 4);
+
+            }
+        });
+
+        natTable.addConfiguration(new HeaderMenuConfiguration(natTable) {
+            @Override
+            protected PopupMenuBuilder createRowHeaderMenu(NatTable natTable) {
+                return super.createRowHeaderMenu(natTable)
+                        .withHideRowMenuItem()
+                        .withShowAllRowsMenuItem();
+            }
+        });
+
+        // adds the key bindings that allows pressing space bar to
+        // expand/collapse tree nodes
+        natTable.addConfiguration(
+                new TreeLayerExpandCollapseKeyBindings(
+                        bodyLayerStack.getTreeLayer(),
+                        bodyLayerStack.getSelectionLayer()));
+
+        natTable.configure();
+
+        GridDataFactory.fillDefaults().grab(true, true).applyTo(natTable);
+
+        Composite buttonPanel = new Composite(container, SWT.NONE);
+        buttonPanel.setLayout(new RowLayout());
+        GridDataFactory.fillDefaults().grab(true, false).applyTo(buttonPanel);
+
+        Button collapseAllButton = new Button(buttonPanel, SWT.PUSH);
+        collapseAllButton.setText("Collapse All");
+        collapseAllButton.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                natTable.doCommand(new TreeCollapseAllCommand());
+            }
+        });
+
+        Button expandAllButton = new Button(buttonPanel, SWT.PUSH);
+        expandAllButton.setText("Expand All");
+        expandAllButton.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                natTable.doCommand(new TreeExpandAllCommand());
+            }
+        });
+
+        return container;
+    }
+
+    /**
+     * Always encapsulate the body layer stack in an AbstractLayerTransform to
+     * ensure that the index transformations are performed in later commands.
+     *
+     * @param <T>
+     */
+    class BodyLayerStack<T> extends AbstractLayerTransform {
+
+        private final IDataProvider bodyDataProvider;
+
+        private final SelectionLayer selectionLayer;
+
+        private final TreeLayer treeLayer;
+
+        public BodyLayerStack(List<T> values,
+                IColumnPropertyAccessor<T> columnPropertyAccessor,
+                ITreeData<T> treeData) {
+
+            this.bodyDataProvider = new ListDataProvider<>(values, columnPropertyAccessor);
+            DataLayer bodyDataLayer = new DataLayer(this.bodyDataProvider);
+
+            // simply apply labels for every column by index
+            bodyDataLayer.setConfigLabelAccumulator(new ColumnLabelAccumulator());
+
+            RowHideShowLayer hideShowLayer = new RowHideShowLayer(bodyDataLayer);
+            RowGroupExpandCollapseLayer expandCollapseLayer = new RowGroupExpandCollapseLayer(hideShowLayer);
+            this.selectionLayer = new SelectionLayer(expandCollapseLayer);
+
+            ITreeRowModel<T> treeRowModel = new TreeRowModel<>(treeData);
+            this.treeLayer = new TreeLayer(this.selectionLayer, treeRowModel);
+
+            ViewportLayer viewportLayer = new ViewportLayer(this.treeLayer);
+
+            setUnderlyingLayer(viewportLayer);
+        }
+
+        public SelectionLayer getSelectionLayer() {
+            return this.selectionLayer;
+        }
+
+        public TreeLayer getTreeLayer() {
+            return this.treeLayer;
+        }
+
+        public IDataProvider getBodyDataProvider() {
+            return this.bodyDataProvider;
+        }
+    }
+
+    /**
+     * Simple ITreeData implementation that uses the lastname of the Person
+     * object as tree item.
+     * <p>
+     * Using a String directly as the tree item has the possible disadvantage of
+     * haven non-unique items in the tree within subtrees.
+     */
+    private static class PersonTreeData implements ITreeData<Person> {
+
+        private List<Person> values;
+
+        private Map<String, List<Person>> parentMapping;
+
+        private Map<String, Person> firstElementMapping = new HashMap<>();
+
+        public PersonTreeData(List<Person> values) {
+            this.values = values;
+
+            // first we need to sort by lastname to ensure all elements with the
+            // same lastname are grouped together
+            this.values.sort(Comparator.comparing(Person::getLastName));
+
+            // then we build up the mapping from lastname to all child elements
+            this.parentMapping = values.stream().collect(Collectors.groupingBy(Person::getLastName));
+
+            // identify the parent node element
+            String current = null;
+            for (Person p : this.values) {
+                if (p.getLastName() != current) {
+                    this.firstElementMapping.put(p.getLastName(), p);
+                    current = p.getLastName();
+                }
+            }
+
+            // remove the parent node element from the children list
+            this.firstElementMapping.forEach((lastname, parent) -> {
+                this.parentMapping.get(lastname).remove(parent);
+            });
+        }
+
+        @Override
+        public String formatDataForDepth(int depth, Person object) {
+            if (object != null) {
+                return object.toString();
+            } else {
+                return ""; //$NON-NLS-1$
+            }
+        }
+
+        @Override
+        public String formatDataForDepth(int depth, int index) {
+            return formatDataForDepth(depth, getDataAtIndex(index));
+        }
+
+        @Override
+        public int getDepthOfData(Person object) {
+            Person firstElement = this.firstElementMapping.get(object.getLastName());
+            return firstElement.equals(object) ? 0 : 1;
+        }
+
+        @Override
+        public int getDepthOfData(int index) {
+            return getDepthOfData(getDataAtIndex(index));
+        }
+
+        @Override
+        public Person getDataAtIndex(int index) {
+            if (!isValidIndex(index)) {
+                return null;
+            }
+            return this.values.get(index);
+        }
+
+        @Override
+        public int indexOf(Person child) {
+            return this.values.indexOf(child);
+        }
+
+        @Override
+        public boolean hasChildren(Person object) {
+            if (object != null && getDepthOfData(object) == 0) {
+                List<Person> children = this.parentMapping.get(object.getLastName());
+                return children != null && !children.isEmpty();
+            }
+            return false;
+        }
+
+        @Override
+        public boolean hasChildren(int index) {
+            return hasChildren(getDataAtIndex(index));
+        }
+
+        @Override
+        public List<Person> getChildren(Person object) {
+            if (object != null && getDepthOfData(object) == 0) {
+                return this.parentMapping.get(object.getLastName());
+            }
+            return new ArrayList<>(0);
+        }
+
+        @Override
+        public List<Person> getChildren(Person object, boolean fullDepth) {
+            // since we only support one level here it is the same as
+            // getChildren(Person)
+            return getChildren(object);
+        }
+
+        @Override
+        public List<Person> getChildren(int index) {
+            return getChildren(getDataAtIndex(index));
+        }
+
+        @Override
+        public int getElementCount() {
+            return this.values.size();
+        }
+
+        @Override
+        public boolean isValidIndex(int index) {
+            return (!(index < 0) && index < this.values.size());
+        }
+
+    }
+
+}
diff --git a/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/data/command/GlazedListsRowDeleteCommandHandler.java b/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/data/command/GlazedListsRowDeleteCommandHandler.java
index 81cb23b..4e42fb5 100644
--- a/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/data/command/GlazedListsRowDeleteCommandHandler.java
+++ b/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/data/command/GlazedListsRowDeleteCommandHandler.java
@@ -10,7 +10,6 @@
  ******************************************************************************/
 package org.eclipse.nebula.widgets.nattable.extension.glazedlists.data.command;
 
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -56,17 +55,7 @@
     public boolean doCommand(ILayer targetLayer, RowDeleteCommand command) {
         // convert the transported position to the target layer
         if (command.convertToTargetLayer(targetLayer)) {
-            // TODO convert to Java 8
-            // int[] positions =
-            // command.getRowPositions().stream().mapToInt(i -> i).toArray();
-            int[] positions = new int[command.getRowPositions().size()];
-            int idx = 0;
-            for (Integer pos : command.getRowPositions()) {
-                positions[idx] = pos;
-                idx++;
-            }
-
-            Arrays.sort(positions);
+            int[] positions = command.getRowPositionsArray();
             Map<Integer, T> deleted = new HashMap<Integer, T>();
 
             this.bodyData.getReadWriteLock().writeLock().lock();
diff --git a/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/hideshow/GlazedListsRowHideShowLayer.java b/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/hideshow/GlazedListsRowHideShowLayer.java
index 9523169..01386cb 100644
--- a/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/hideshow/GlazedListsRowHideShowLayer.java
+++ b/org.eclipse.nebula.widgets.nattable.extension.glazedlists/src/org/eclipse/nebula/widgets/nattable/extension/glazedlists/hideshow/GlazedListsRowHideShowLayer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2013, 2019 Dirk Fauth and others.
+ * Copyright (c) 2013, 2020 Dirk Fauth and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -20,7 +20,6 @@
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.Properties;
-import java.util.Set;
 import java.util.stream.Collectors;
 
 import org.apache.commons.codec.binary.Base64;
@@ -58,7 +57,7 @@
  */
 public class GlazedListsRowHideShowLayer<T> extends AbstractLayerTransform implements IRowHideShowLayer, IUniqueIndexLayer {
 
-    private static final Log log = LogFactory.getLog(GlazedListsRowHideShowLayer.class);
+    private static final Log LOG = LogFactory.getLog(GlazedListsRowHideShowLayer.class);
 
     /**
      * Key for persisting the number of hidden row id's. This is necessary
@@ -71,7 +70,7 @@
     /**
      * The collection of row id's that are hidden.
      */
-    private final Set<Serializable> rowIdsToHide = new HashSet<Serializable>();
+    private final HashSet<Serializable> rowIdsToHide = new HashSet<Serializable>();
 
     /**
      * The {@link IRowIdAccessor} that is used to extract the id out of the row
@@ -219,9 +218,23 @@
         registerCommandHandler(new RowPositionHideCommandHandler(this));
     }
 
+    /**
+     *
+     * @return The {@link MatcherEditor} that is used to filter rows via row
+     *         id's.
+     *
+     * @since 2.0
+     */
+    public MatcherEditor<T> getHideRowMatcherEditor() {
+        return this.hideRowByIdMatcherEditor;
+    }
+
     @Override
     public void hideRowPositions(int... rowPositions) {
-        hideRowPositions(Arrays.stream(rowPositions).boxed().collect(Collectors.toList()));
+        hideRows(Arrays.stream(rowPositions)
+                .map(this::getRowIndexByPosition)
+                .mapToObj(rowIndex -> this.rowIdAccessor.getRowId(this.rowDataProvider.getRowObject(rowIndex)))
+                .collect(Collectors.toSet()));
     }
 
     /**
@@ -234,17 +247,14 @@
      */
     @Override
     public void hideRowPositions(Collection<Integer> rowPositions) {
-        Collection<Serializable> rowIds = new HashSet<Serializable>();
-        for (Integer rowPos : rowPositions) {
-            int rowIndex = getRowIndexByPosition(rowPos);
-            rowIds.add(this.rowIdAccessor.getRowId(this.rowDataProvider.getRowObject(rowIndex)));
-        }
-        hideRows(rowIds);
+        hideRowPositions(rowPositions.stream().mapToInt(Integer::intValue).toArray());
     }
 
     @Override
     public void hideRowIndexes(int... rowIndexes) {
-        hideRowIndexes(Arrays.stream(rowIndexes).boxed().collect(Collectors.toList()));
+        hideRows(Arrays.stream(rowIndexes)
+                .mapToObj(rowIndex -> this.rowIdAccessor.getRowId(this.rowDataProvider.getRowObject(rowIndex)))
+                .collect(Collectors.toSet()));
     }
 
     /**
@@ -257,16 +267,14 @@
      */
     @Override
     public void hideRowIndexes(Collection<Integer> rowIndexes) {
-        Collection<Serializable> rowIds = new HashSet<Serializable>();
-        for (Integer rowIndex : rowIndexes) {
-            rowIds.add(this.rowIdAccessor.getRowId(this.rowDataProvider.getRowObject(rowIndex)));
-        }
-        hideRows(rowIds);
+        hideRowPositions(rowIndexes.stream().mapToInt(Integer::intValue).toArray());
     }
 
     @Override
     public void showRowIndexes(int... rowIndexes) {
-        showRowIndexes(Arrays.stream(rowIndexes).boxed().collect(Collectors.toList()));
+        showRows(Arrays.stream(rowIndexes)
+                .mapToObj(rowIndex -> this.rowIdAccessor.getRowId(this.rowDataProvider.getRowObject(rowIndex)))
+                .collect(Collectors.toSet()));
     }
 
     /**
@@ -279,11 +287,7 @@
      */
     @Override
     public void showRowIndexes(Collection<Integer> rowIndexes) {
-        Collection<Serializable> rowIds = new HashSet<Serializable>();
-        for (Integer rowIndex : rowIndexes) {
-            rowIds.add(this.rowIdAccessor.getRowId(this.rowDataProvider.getRowObject(rowIndex)));
-        }
-        showRows(rowIds);
+        showRowIndexes(rowIndexes.stream().mapToInt(Integer::intValue).toArray());
     }
 
     /**
@@ -368,13 +372,13 @@
                 properties.setProperty(prefix + PERSISTENCE_KEY_HIDDEN_ROW_IDS,
                         new String(Base64.encodeBase64(bos.toByteArray())));
             } catch (Exception e) {
-                log.error("Error while persisting GlazedListsRowHideShowLayer state", e); //$NON-NLS-1$
+                LOG.error("Error while persisting GlazedListsRowHideShowLayer state", e); //$NON-NLS-1$
             } finally {
                 if (out != null) {
                     try {
                         out.close();
                     } catch (IOException e) {
-                        log.error("Error on closing the output stream", e); //$NON-NLS-1$
+                        LOG.error("Error on closing the output stream", e); //$NON-NLS-1$
                     }
                 }
             }
@@ -400,13 +404,13 @@
                     this.rowIdsToHide.add(ser);
                 }
             } catch (Exception e) {
-                log.error("Error while restoring GlazedListsRowHideShowLayer state", e); //$NON-NLS-1$
+                LOG.error("Error while restoring GlazedListsRowHideShowLayer state", e); //$NON-NLS-1$
             } finally {
                 if (in != null) {
                     try {
                         in.close();
                     } catch (IOException e) {
-                        log.error("Error on closing the input stream", e); //$NON-NLS-1$
+                        LOG.error("Error on closing the input stream", e); //$NON-NLS-1$
                     }
                 }
             }
diff --git a/org.eclipse.nebula.widgets.nattable.test.performance/META-INF/MANIFEST.MF b/org.eclipse.nebula.widgets.nattable.test.performance/META-INF/MANIFEST.MF
index 28d8a2f..7f74bc5 100644
--- a/org.eclipse.nebula.widgets.nattable.test.performance/META-INF/MANIFEST.MF
+++ b/org.eclipse.nebula.widgets.nattable.test.performance/META-INF/MANIFEST.MF
@@ -5,9 +5,29 @@
 Bundle-Version: 1.1.1.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Import-Package: ca.odell.glazedlists,
+ org.eclipse.collections.api;version="10.1.0",
+ org.eclipse.collections.api.collection.primitive;version="10.1.0",
+ org.eclipse.collections.api.factory;version="10.1.0",
+ org.eclipse.collections.api.factory.list;version="10.1.0",
+ org.eclipse.collections.api.factory.list.primitive;version="10.1.0",
+ org.eclipse.collections.api.factory.map;version="10.1.0",
+ org.eclipse.collections.api.factory.map.primitive;version="10.1.0",
+ org.eclipse.collections.api.factory.set.primitive;version="10.1.0",
+ org.eclipse.collections.api.iterator;version="10.1.0",
+ org.eclipse.collections.api.list;version="10.1.0",
+ org.eclipse.collections.api.list.primitive;version="10.1.0",
+ org.eclipse.collections.api.map;version="10.1.0",
+ org.eclipse.collections.api.map.primitive;version="10.1.0",
+ org.eclipse.collections.api.ordered.primitive;version="10.1.0",
+ org.eclipse.collections.api.set.primitive;version="10.1.0",
+ org.eclipse.collections.api.tuple.primitive;version="10.1.0",
+ org.eclipse.collections.impl.factory.primitive;version="10.1.0",
+ org.eclipse.collections.impl.map.mutable.primitive;version="10.1.0",
  org.eclipse.nebula.widgets.nattable,
+ org.eclipse.nebula.widgets.nattable.coordinate;version="2.0.0",
  org.eclipse.nebula.widgets.nattable.data,
  org.eclipse.nebula.widgets.nattable.dataset.fixture.data,
+ org.eclipse.nebula.widgets.nattable.dataset.person;version="1.4.0",
  org.eclipse.nebula.widgets.nattable.extension.glazedlists,
  org.eclipse.nebula.widgets.nattable.grid,
  org.eclipse.nebula.widgets.nattable.grid.data,
@@ -18,6 +38,9 @@
  org.eclipse.nebula.widgets.nattable.painter.layer,
  org.eclipse.nebula.widgets.nattable.reorder,
  org.eclipse.nebula.widgets.nattable.selection,
+ org.eclipse.nebula.widgets.nattable.test.fixture.layer,
+ org.eclipse.nebula.widgets.nattable.tree;version="2.0.0",
+ org.eclipse.nebula.widgets.nattable.util;version="2.0.0",
  org.eclipse.nebula.widgets.nattable.viewport,
  org.eclipse.swt,
  org.eclipse.swt.events,
diff --git a/org.eclipse.nebula.widgets.nattable.test.performance/src/org/eclipse/nebula/widgets/nattable/test/performance/CollectionsBenchmark.java b/org.eclipse.nebula.widgets.nattable.test.performance/src/org/eclipse/nebula/widgets/nattable/test/performance/CollectionsBenchmark.java
new file mode 100644
index 0000000..00f21f9
--- /dev/null
+++ b/org.eclipse.nebula.widgets.nattable.test.performance/src/org/eclipse/nebula/widgets/nattable/test/performance/CollectionsBenchmark.java
@@ -0,0 +1,490 @@
+/*****************************************************************************
+ * Copyright (c) 2020 Dirk Fauth.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *      Dirk Fauth <dirk.fauth@googlemail.com> - Initial API and implementation
+ *
+ *****************************************************************************/
+package org.eclipse.nebula.widgets.nattable.test.performance;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.IntStream;
+
+import org.eclipse.collections.api.factory.Maps;
+import org.eclipse.collections.api.list.primitive.MutableIntList;
+import org.eclipse.collections.api.map.MutableMap;
+import org.eclipse.collections.api.map.primitive.MutableIntIntMap;
+import org.eclipse.collections.api.set.primitive.MutableIntSet;
+import org.eclipse.collections.api.tuple.primitive.IntIntPair;
+import org.eclipse.collections.impl.factory.primitive.IntIntMaps;
+import org.eclipse.collections.impl.factory.primitive.IntLists;
+import org.eclipse.collections.impl.factory.primitive.IntSets;
+import org.eclipse.nebula.widgets.nattable.util.ArrayUtil;
+
+public class CollectionsBenchmark {
+
+    public static final int ITERATIONS = 100;
+
+    MutableMap<String, MutableIntSet> hidden = Maps.mutable.empty();
+    MutableIntIntMap map = IntIntMaps.mutable.empty();
+
+    public static void main(String[] args) {
+
+        CollectionsBenchmark benchmark = new CollectionsBenchmark();
+        benchmark.startBenchmark();
+    }
+
+    CollectionsBenchmark() {
+        this.hidden.put("Test1", IntSets.mutable.ofAll(IntStream.range(0, 100_000))); //$NON-NLS-1$
+        this.hidden.put("Test2", IntSets.mutable.ofAll(IntStream.range(100_001, 200_000))); //$NON-NLS-1$
+        this.hidden.put("Test3", IntSets.mutable.ofAll(IntStream.range(200_000, 300_000))); //$NON-NLS-1$
+        this.hidden.put("Test4", IntSets.mutable.ofAll(IntStream.range(300_000, 400_000))); //$NON-NLS-1$
+        this.hidden.put("Test5", IntSets.mutable.ofAll(IntStream.range(400_000, 500_000))); //$NON-NLS-1$
+
+        for (int i = 0; i < 1_000_000; i++) {
+            this.map.put(i, i * 2);
+        }
+    }
+
+    public void startBenchmark() {
+        // rampUp to remove class loading from performance measure
+        findByContains(this.hidden, 1, true);
+        findByCollections(this.hidden, 1, true);
+        flattenByIterationSet(this.hidden, true);
+        flattenByIterationList(this.hidden, true);
+        sumAndMaxByIteration(this.map, true);
+
+        boolean findByContains = findByContains(this.hidden, 100_000, false);
+        boolean findByCollections = findByCollections(this.hidden, 100_000, false);
+
+        if (findByContains || findByCollections) {
+            System.err.println("non-existing value was found");
+        }
+
+        findByContains = findByContains(this.hidden, 450_000, false);
+        findByCollections = findByCollections(this.hidden, 450_000, false);
+
+        if (!findByContains || !findByCollections) {
+            System.err.println("value was not found");
+        }
+
+        ArrayList<Integer> valuesCollection = new ArrayList<>();
+        for (int i = 0; i < 1_000_000; i++) {
+            if (i == 0 || i % 100_000 != 0) {
+                valuesCollection.add(i);
+            }
+        }
+        boolean c1 = containsInCollection(valuesCollection, 450_000, false);
+
+        int[] valuesArray = new int[999_991];
+        int index = 0;
+        for (int i = 0; i < 1_000_000; i++) {
+            if (i == 0 || i % 100_000 != 0) {
+                valuesArray[index] = i;
+                index++;
+            }
+        }
+        boolean c2 = containsInPrimitive(valuesArray, 450_000, false);
+
+        MutableIntList valuesIntList = IntLists.mutable.of(Arrays.copyOf(valuesArray, valuesArray.length));
+        boolean c3 = containsInMutableIntList(valuesIntList, 450_000, false);
+
+        MutableIntSet valuesIntSet = IntSets.mutable.of(Arrays.copyOf(valuesArray, valuesArray.length));
+        boolean c4 = containsInMutableIntSet(valuesIntSet, 450_000, false);
+
+        if (!c1 || !c2 || !c3 || !c4) {
+            System.err.println("value not contained");
+        }
+
+        System.out.println();
+        flattenByIterationSet(this.hidden, false);
+        flattenByIterationList(this.hidden, false);
+
+        System.out.println();
+        System.out.println("Remove Performance Tests"); //$NON-NLS-1$
+
+        int[] toRemove = new int[100_000];
+        for (int i = 0; i < 100_000; i++) {
+            toRemove[i] = i + 200_000;
+        }
+
+        // This one is really slow, so we disable it here
+        // List<Integer> dataList = IntStream.range(0,
+        // 1_000_000).boxed().collect(Collectors.toList());
+        // removeAllByIterationArrayList(dataList, toRemove, false);
+        // removeAllArrayList(dataList, toRemove, false);
+
+        MutableIntList dataIntList = IntLists.mutable.ofAll(IntStream.range(0, 1_000_000));
+        // This one is really slow, so we disable it here
+        // removeAllByIterationMutableIntList(dataIntList, toRemove, false);
+        removeAllMutableIntList(dataIntList, toRemove, false);
+
+        MutableIntSet dataIntSet = IntSets.mutable.ofAll(IntStream.range(0, 1_000_000));
+        removeAllByIterationMutableIntSet(dataIntSet, toRemove, false);
+        removeAllMutableIntSet(dataIntSet, toRemove, false);
+
+        System.out.println();
+        System.out.println("Sum and max Performance Tests"); //$NON-NLS-1$
+        int[] result1 = sumAndMaxByIteration(this.map, false);
+        int[] result2 = sumAndMax(this.map, false);
+
+        if (!Arrays.equals(result1, result2)) {
+            System.out.println("result is not equal");
+        }
+    }
+
+    public static boolean findByContains(MutableMap<String, MutableIntSet> hidden, int columnIndex, boolean rampUp) {
+        int sum = 0;
+        boolean result = false;
+        for (int j = 0; j < (rampUp ? 1 : ITERATIONS); j++) {
+            long start = System.currentTimeMillis();
+
+            for (MutableIntSet indexes : hidden.values()) {
+                if (indexes.contains(columnIndex)) {
+                    result = true;
+                    break;
+                }
+            }
+            long end = System.currentTimeMillis();
+
+            sum += (end - start);
+        }
+
+        if (!rampUp) {
+            System.out.println("find via value iteration\t" + (sum / ITERATIONS) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        return result;
+    }
+
+    public static boolean findByCollections(MutableMap<String, MutableIntSet> hidden, int columnIndex, boolean rampUp) {
+        int sum = 0;
+        boolean result = false;
+        for (int j = 0; j < (rampUp ? 1 : ITERATIONS); j++) {
+            long start = System.currentTimeMillis();
+
+            MutableIntSet detect = hidden.detect(indexes -> indexes.contains(columnIndex));
+            result = detect != null;
+            long end = System.currentTimeMillis();
+
+            sum += (end - start);
+        }
+
+        if (!rampUp) {
+            System.out.println("find via detect()\t\t" + (sum / ITERATIONS) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        return result;
+    }
+
+    public static boolean containsInCollection(List<Integer> values, int columnIndex, boolean rampUp) {
+        int sum = 0;
+        boolean result = false;
+        for (int j = 0; j < (rampUp ? 1 : ITERATIONS); j++) {
+            long start = System.currentTimeMillis();
+
+            result = values.contains(Integer.valueOf(columnIndex));
+
+            long end = System.currentTimeMillis();
+
+            sum += (end - start);
+        }
+
+        if (!rampUp) {
+            System.out.println("contains in collection\t\t" + (sum / ITERATIONS) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        return result;
+    }
+
+    public static boolean containsInPrimitive(int[] values, int columnIndex, boolean rampUp) {
+        int sum = 0;
+        boolean result = false;
+        for (int j = 0; j < (rampUp ? 1 : ITERATIONS); j++) {
+            long start = System.currentTimeMillis();
+
+            result = Arrays.stream(values).anyMatch(x -> x == columnIndex);
+
+            long end = System.currentTimeMillis();
+
+            sum += (end - start);
+        }
+
+        if (!rampUp) {
+            System.out.println("contains in int[]\t\t" + (sum / ITERATIONS) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        return result;
+    }
+
+    private boolean containsInMutableIntList(MutableIntList values, int columnIndex, boolean rampUp) {
+        int sum = 0;
+        boolean result = false;
+        for (int j = 0; j < (rampUp ? 1 : ITERATIONS); j++) {
+            long start = System.currentTimeMillis();
+
+            result = values.contains(columnIndex);
+
+            long end = System.currentTimeMillis();
+
+            sum += (end - start);
+        }
+
+        if (!rampUp) {
+            System.out.println("contains in MutableIntList\t" + (sum / ITERATIONS) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        return result;
+    }
+
+    private boolean containsInMutableIntSet(MutableIntSet values, int columnIndex, boolean rampUp) {
+        int sum = 0;
+        boolean result = false;
+        for (int j = 0; j < (rampUp ? 1 : ITERATIONS); j++) {
+            long start = System.currentTimeMillis();
+
+            result = values.contains(columnIndex);
+
+            long end = System.currentTimeMillis();
+
+            sum += (end - start);
+        }
+
+        if (!rampUp) {
+            System.out.println("contains in MutableIntSet\t" + (sum / ITERATIONS) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        return result;
+    }
+
+    public static int[] flattenByIterationSet(MutableMap<String, MutableIntSet> hidden, boolean rampUp) {
+        int sum = 0;
+        int[] result = null;
+        for (int j = 0; j < (rampUp ? 1 : ITERATIONS); j++) {
+            long start = System.currentTimeMillis();
+
+            MutableIntSet hiddenColumnIndexes = IntSets.mutable.empty();
+            for (MutableIntSet indexes : hidden.values()) {
+                hiddenColumnIndexes.addAll(indexes);
+            }
+            result = hiddenColumnIndexes.toSortedArray();
+
+            long end = System.currentTimeMillis();
+
+            sum += (end - start);
+        }
+
+        if (!rampUp) {
+            System.out.println("flatten by iteration MutableIntSet\t" + (sum / ITERATIONS) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        return result;
+    }
+
+    public static int[] flattenByIterationList(MutableMap<String, MutableIntSet> hidden, boolean rampUp) {
+        int sum = 0;
+        int[] result = null;
+        for (int j = 0; j < (rampUp ? 1 : ITERATIONS); j++) {
+            long start = System.currentTimeMillis();
+
+            MutableIntList hiddenColumnIndexes = IntLists.mutable.empty();
+            for (MutableIntSet indexes : hidden.values()) {
+                hiddenColumnIndexes.addAll(indexes);
+            }
+            result = hiddenColumnIndexes.distinct().toSortedArray();
+
+            long end = System.currentTimeMillis();
+
+            sum += (end - start);
+        }
+
+        if (!rampUp) {
+            System.out.println("flatten by iteration MutableIntList\t" + (sum / ITERATIONS) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        return result;
+    }
+
+    public static void removeAllByIterationArrayList(List<Integer> values, int[] toRemove, boolean rampUp) {
+        int sum = 0;
+        for (int j = 0; j < (rampUp ? 1 : ITERATIONS); j++) {
+            // create a copy so we really remove everytime
+            ArrayList<Integer> v = new ArrayList<Integer>(values);
+
+            long start = System.currentTimeMillis();
+
+            for (int r : toRemove) {
+                v.remove(r);
+            }
+
+            long end = System.currentTimeMillis();
+
+            sum += (end - start);
+        }
+
+        if (!rampUp) {
+            System.out.println("remove all by iteration ArrayList\t" + (sum / ITERATIONS) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+    }
+
+    public static void removeAllArrayList(List<Integer> values, int[] toRemove, boolean rampUp) {
+        List<Integer> asIntegerList = ArrayUtil.asIntegerList(toRemove);
+        int sum = 0;
+        for (int j = 0; j < (rampUp ? 1 : ITERATIONS); j++) {
+            // create a copy so we really remove everytime
+            ArrayList<Integer> v = new ArrayList<Integer>(values);
+
+            long start = System.currentTimeMillis();
+
+            v.removeAll(asIntegerList);
+
+            long end = System.currentTimeMillis();
+
+            sum += (end - start);
+        }
+
+        if (!rampUp) {
+            System.out.println("remove all ArrayList\t\t\t" + (sum / ITERATIONS) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+    }
+
+    public static void removeAllByIterationMutableIntList(MutableIntList values, int[] toRemove, boolean rampUp) {
+        int sum = 0;
+        for (int j = 0; j < (rampUp ? 1 : ITERATIONS); j++) {
+            // create a copy so we really remove everytime
+            MutableIntList v = values.toList();
+
+            long start = System.currentTimeMillis();
+
+            for (int r : toRemove) {
+                v.remove(r);
+            }
+
+            long end = System.currentTimeMillis();
+
+            sum += (end - start);
+        }
+
+        if (!rampUp) {
+            System.out.println("remove all by iteration MutableIntList\t" + (sum / ITERATIONS) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+    }
+
+    public static void removeAllMutableIntList(MutableIntList values, int[] toRemove, boolean rampUp) {
+        int sum = 0;
+        for (int j = 0; j < (rampUp ? 1 : ITERATIONS); j++) {
+            // create a copy so we really remove everytime
+            MutableIntList v = values.toList();
+
+            long start = System.currentTimeMillis();
+
+            v.removeAll(toRemove);
+
+            long end = System.currentTimeMillis();
+
+            sum += (end - start);
+        }
+
+        if (!rampUp) {
+            System.out.println("remove all MutableIntList\t\t" + (sum / ITERATIONS) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+    }
+
+    public static void removeAllByIterationMutableIntSet(MutableIntSet values, int[] toRemove, boolean rampUp) {
+        int sum = 0;
+        for (int j = 0; j < (rampUp ? 1 : ITERATIONS); j++) {
+            // create a copy so we really remove everytime
+            MutableIntSet v = values.toSet();
+
+            long start = System.currentTimeMillis();
+
+            for (int r : toRemove) {
+                v.remove(r);
+            }
+
+            long end = System.currentTimeMillis();
+
+            sum += (end - start);
+        }
+
+        if (!rampUp) {
+            System.out.println("remove all by iteration MutableIntSet\t" + (sum / ITERATIONS) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+    }
+
+    public static void removeAllMutableIntSet(MutableIntSet values, int[] toRemove, boolean rampUp) {
+        int sum = 0;
+        for (int j = 0; j < (rampUp ? 1 : ITERATIONS); j++) {
+            // create a copy so we really remove everytime
+            MutableIntSet v = values.toSet();
+
+            long start = System.currentTimeMillis();
+
+            v.removeAll(toRemove);
+
+            long end = System.currentTimeMillis();
+
+            sum += (end - start);
+        }
+
+        if (!rampUp) {
+            System.out.println("remove all MutableIntSet\t\t" + (sum / ITERATIONS) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+    }
+
+    public static int[] sumAndMaxByIteration(MutableIntIntMap map, boolean rampUp) {
+        int sum = 0;
+        int[] result = new int[2];
+        for (int j = 0; j < (rampUp ? 1 : ITERATIONS); j++) {
+            long start = System.currentTimeMillis();
+
+            int valueSum = 0;
+            int lastPos = -1;
+            for (IntIntPair entry : map.keyValuesView().toSortedList()) {
+                valueSum += entry.getTwo();
+                lastPos = Math.max(lastPos, entry.getOne());
+            }
+
+            result[0] = valueSum;
+            result[1] = lastPos;
+
+            long end = System.currentTimeMillis();
+
+            sum += (end - start);
+        }
+
+        if (!rampUp) {
+            System.out.println("sum and max by iteration\t\t" + (sum / ITERATIONS) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        return result;
+    }
+
+    public static int[] sumAndMax(MutableIntIntMap map, boolean rampUp) {
+        int sum = 0;
+        int[] result = new int[2];
+        for (int j = 0; j < (rampUp ? 1 : ITERATIONS); j++) {
+            long start = System.currentTimeMillis();
+
+            result[0] = (int) map.values().sum();
+            result[1] = map.keySet().max();
+
+            long end = System.currentTimeMillis();
+
+            sum += (end - start);
+        }
+
+        if (!rampUp) {
+            System.out.println("sum and max\t\t\t\t" + (sum / ITERATIONS) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        return result;
+    }
+}
diff --git a/org.eclipse.nebula.widgets.nattable.test.performance/src/org/eclipse/nebula/widgets/nattable/test/performance/PositionUtilBenchmark.java b/org.eclipse.nebula.widgets.nattable.test.performance/src/org/eclipse/nebula/widgets/nattable/test/performance/PositionUtilBenchmark.java
new file mode 100644
index 0000000..feba817
--- /dev/null
+++ b/org.eclipse.nebula.widgets.nattable.test.performance/src/org/eclipse/nebula/widgets/nattable/test/performance/PositionUtilBenchmark.java
@@ -0,0 +1,471 @@
+/*****************************************************************************
+ * Copyright (c) 2020 Dirk Fauth.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *      Dirk Fauth <dirk.fauth@googlemail.com> - Initial API and implementation
+ *
+ *****************************************************************************/
+package org.eclipse.nebula.widgets.nattable.test.performance;
+
+import static org.eclipse.nebula.widgets.nattable.util.ObjectUtils.isNotEmpty;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.ObjIntConsumer;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import org.eclipse.collections.api.factory.Lists;
+import org.eclipse.collections.api.iterator.IntIterator;
+import org.eclipse.collections.api.list.MutableList;
+import org.eclipse.collections.api.list.primitive.IntList;
+import org.eclipse.collections.api.list.primitive.MutableIntList;
+import org.eclipse.collections.impl.factory.primitive.IntLists;
+import org.eclipse.nebula.widgets.nattable.coordinate.PositionUtil;
+import org.eclipse.nebula.widgets.nattable.coordinate.Range;
+
+public class PositionUtilBenchmark {
+
+    private static final int ITERATIONS = 100;
+
+    private int[] valuesPrimitive;
+
+    private ArrayList<Integer> valuesWrapper;
+
+    public static void main(String[] args) {
+        PositionUtilBenchmark benchmark = new PositionUtilBenchmark();
+        benchmark.startGrouping();
+
+        System.out.println();
+        System.out.println();
+        benchmark.getPositionsPerformance();
+    }
+
+    PositionUtilBenchmark() {
+        this.valuesPrimitive = createPrimitiveValuesArrayForEach();
+        int[] valuesFromIntStream = createPrimitiveValuesArrayIntStream();
+
+        if (!Arrays.equals(this.valuesPrimitive, valuesFromIntStream)) {
+            System.err.println("Primitive input not equal");
+        }
+
+        this.valuesWrapper = createWrapperValuesForEach();
+        ArrayList<Integer> wrapperFromStream = createWrapperValuesIntStream();
+
+        if (!this.valuesWrapper.equals(wrapperFromStream)) {
+            System.err.println("Wrapper input not equal");
+        }
+    }
+
+    @SuppressWarnings("unused")
+    void startGrouping() {
+
+        System.out.println();
+        System.out.println("start grouping");
+        System.out.println();
+
+        List<List<Integer>> groupedByContiguous = getGroupedByContiguousWrapperToWrapper(this.valuesWrapper);
+
+        int[][] g1 = getGroupedByContiguousWithIntStream(this.valuesPrimitive);
+
+        int[][] g2 = getGroupedByContiguousWithForLoop(this.valuesPrimitive);
+
+        if (!equals(g1, g2)) {
+            System.err.println("output not equal"); //$NON-NLS-1$
+        }
+
+        int[][] g21 = getGroupedByContiguousPositionUtil(this.valuesPrimitive);
+
+        if (!equals(g2, g21)) {
+            System.err.println("output not equal"); //$NON-NLS-1$
+        }
+
+        int[][] g3 = getGroupedByContiguousEclipseCollections(this.valuesPrimitive);
+
+        if (!equals(g1, g3)) {
+            System.err.println("output not equal between primitive and Eclipse Collections"); //$NON-NLS-1$
+        }
+
+        int[][] g4 = getGroupedByContiguousEclipseCollectionsWithCollector(this.valuesPrimitive);
+
+        if (!equals(g1, g4)) {
+            System.err.println("output not equal between Eclipse Collections"); //$NON-NLS-1$
+        }
+
+    }
+
+    private int[] createPrimitiveValuesArrayForEach() {
+        int sum = 0;
+        int[] values = null;
+        for (int j = 0; j < ITERATIONS; j++) {
+            long start = System.currentTimeMillis();
+            values = new int[999_991];
+            int index = 0;
+            for (int i = 0; i < 1_000_000; i++) {
+                if (i == 0 || i % 100_000 != 0) {
+                    values[index] = i;
+                    index++;
+                }
+            }
+            long end = System.currentTimeMillis();
+
+            sum += (end - start);
+        }
+
+        System.out.println("collecting int[] via for-loop\t\t" + (sum / ITERATIONS) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$
+
+        return values;
+    }
+
+    private int[] createPrimitiveValuesArrayIntStream() {
+        int sum = 0;
+        int[] values = null;
+        for (int j = 0; j < ITERATIONS; j++) {
+            long start = System.currentTimeMillis();
+            values = IntStream.range(0, 1_000_000)
+                    .filter(i -> i == 0 || i % 100_000 != 0)
+                    .toArray();
+            long end = System.currentTimeMillis();
+
+            sum += (end - start);
+        }
+
+        System.out.println("collecting int[] via IntStream\t\t" + (sum / ITERATIONS) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$
+
+        return values;
+    }
+
+    private ArrayList<Integer> createWrapperValuesForEach() {
+        int sum = 0;
+        ArrayList<Integer> values = null;
+        for (int j = 0; j < ITERATIONS; j++) {
+            long start = System.currentTimeMillis();
+            values = new ArrayList<Integer>();
+            for (int i = 0; i < 1_000_000; i++) {
+                if (i == 0 || i % 100_000 != 0) {
+                    values.add(i);
+                }
+            }
+            long end = System.currentTimeMillis();
+
+            sum += (end - start);
+        }
+
+        System.out.println("collecting List<Integer> via for-loop\t" + (sum / ITERATIONS) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$
+
+        return values;
+    }
+
+    private ArrayList<Integer> createWrapperValuesIntStream() {
+        int sum = 0;
+        ArrayList<Integer> values = null;
+        for (int j = 0; j < ITERATIONS; j++) {
+            long start = System.currentTimeMillis();
+
+            values = new ArrayList<Integer>();
+            values.addAll(IntStream.range(0, 1_000_000)
+                    .filter(i -> (i == 0 || i % 100_000 != 0))
+                    .boxed()
+                    .collect(Collectors.toList()));
+
+            long end = System.currentTimeMillis();
+
+            sum += (end - start);
+        }
+
+        System.out.println("collecting List<Integer> via IntStream\t" + (sum / ITERATIONS) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$
+
+        return values;
+    }
+
+    public static List<List<Integer>> getGroupedByContiguousWrapperToWrapper(Collection<Integer> numberCollection) {
+        int sum = 0;
+        ArrayList<List<Integer>> grouped = null;
+        for (int j = 0; j < ITERATIONS; j++) {
+            long start = System.currentTimeMillis();
+
+            ArrayList<Integer> numbers = new ArrayList<Integer>(numberCollection);
+            Collections.sort(numbers);
+
+            ArrayList<Integer> contiguous = new ArrayList<Integer>();
+            grouped = new ArrayList<List<Integer>>();
+
+            for (int i = 0; i < numbers.size() - 1; i++) {
+                if (numbers.get(i).intValue() + 1 != numbers.get(i + 1).intValue()) {
+                    contiguous.add(numbers.get(i));
+                    grouped.add(contiguous);
+                    contiguous = new ArrayList<Integer>();
+                } else {
+                    contiguous.add(numbers.get(i));
+                }
+            }
+            if (isNotEmpty(numbers)) {
+                contiguous.add(numbers.get(numbers.size() - 1));
+            }
+            grouped.add(contiguous);
+
+            long end = System.currentTimeMillis();
+
+            sum += (end - start);
+        }
+
+        System.out.println("grouping List<Integer> to List<List<Integer>>\t\t\t\t\t" + (sum / ITERATIONS) + " ms");
+
+        return grouped;
+    }
+
+    public static int[][] getGroupedByContiguousWithIntStream(int... numbers) {
+        int sum = 0;
+        int[][] result = null;
+        for (int j = 0; j < ITERATIONS; j++) {
+            long start = System.currentTimeMillis();
+
+            ArrayList<Range> ranges = Arrays.stream(numbers)
+                    .sorted()
+                    .collect(
+                            ArrayList<Range>::new,
+                            new RangeAccumulator(),
+                            (g1, g2) -> {
+                                g1.addAll(g2);
+                            });
+
+            result = ranges.stream()
+                    .map(r -> IntStream.range(r.start, r.end).toArray())
+                    .toArray(size -> new int[size][]);
+
+            long end = System.currentTimeMillis();
+
+            sum += (end - start);
+        }
+
+        System.out.println("grouping int[] to int[][] via primitive streams, map via IntStream\t\t" + (sum / ITERATIONS) + " ms");
+
+        return result;
+    }
+
+    public static int[][] getGroupedByContiguousWithForLoop(int... numbers) {
+        int sum = 0;
+        int[][] result = null;
+        for (int j = 0; j < ITERATIONS; j++) {
+            long start = System.currentTimeMillis();
+
+            ArrayList<Range> ranges = Arrays.stream(numbers)
+                    .sorted()
+                    .collect(
+                            ArrayList<Range>::new,
+                            new RangeAccumulator(),
+                            (g1, g2) -> {
+                                g1.addAll(g2);
+                            });
+
+            result = ranges.stream()
+                    .map(r -> {
+                        int[] res = new int[r.end - r.start];
+                        int i = 0;
+                        for (int pos = r.start; pos < r.end; pos++) {
+                            res[i] = pos;
+                            i++;
+                        }
+
+                        return res;
+                    })
+                    .toArray(size -> new int[size][]);
+
+            long end = System.currentTimeMillis();
+
+            sum += (end - start);
+        }
+
+        System.out.println("grouping int[] to int[][] via primitive streams, map via for-loop\t\t" + (sum / ITERATIONS) + " ms");
+
+        return result;
+    }
+
+    public static int[][] getGroupedByContiguousPositionUtil(int... values) {
+        int sum = 0;
+        int[][] result = null;
+        for (int j = 0; j < ITERATIONS; j++) {
+            long start = System.currentTimeMillis();
+
+            result = PositionUtil.getGroupedByContiguous(values);
+
+            long end = System.currentTimeMillis();
+
+            sum += (end - start);
+        }
+
+        System.out.println("grouping int[] to int[][] via PositionUtil\t\t\t\t\t" + (sum / ITERATIONS) + " ms");
+
+        return result;
+    }
+
+    public static int[][] getGroupedByContiguousEclipseCollections(int... numbers) {
+        int sum = 0;
+        int[][] result = null;
+        for (int j = 0; j < ITERATIONS; j++) {
+            long start = System.currentTimeMillis();
+
+            MutableList<MutableIntList> out = Lists.mutable.empty();
+            MutableIntList curList = IntLists.mutable.empty();
+
+            // sort the numbers
+            IntList iNumbers = IntLists.immutable.of(numbers).toSortedList();
+
+            final IntIterator it = iNumbers.intIterator();
+            int last = it.next();
+            out.add(curList);
+            curList.add(last);
+
+            while (it.hasNext()) {
+                int next = it.next();
+
+                if (next == last + 1) {
+                    curList.add(next);
+                } else {
+                    curList = IntLists.mutable.empty();
+                    curList.add(next);
+                    out.add(curList);
+                }
+
+                last = next;
+            }
+
+            result = out.collect(groupList -> groupList.toArray()).toArray(new int[0][0]);
+
+            long end = System.currentTimeMillis();
+
+            sum += (end - start);
+        }
+
+        System.out.println("grouping int[] to int[][] via primitive Eclipse Collections\t\t\t" + (sum / ITERATIONS) + " ms");
+
+        return result;
+    }
+
+    public static int[][] getGroupedByContiguousEclipseCollectionsWithCollector(int... numbers) {
+        class ContiguousCollector {
+            MutableList<MutableIntList> grouped = Lists.mutable.empty();
+        }
+
+        ObjIntConsumer<ContiguousCollector> accumulator = (collector, i) -> {
+            MutableIntList lastGroup = collector.grouped.getLast();
+            if (lastGroup == null) {
+                lastGroup = IntLists.mutable.empty();
+                collector.grouped.add(lastGroup);
+            }
+            if (!lastGroup.isEmpty()) {
+                int last = lastGroup.getLast();
+                if (i > (last + 1)) {
+                    lastGroup = IntLists.mutable.empty();
+                    collector.grouped.add(lastGroup);
+                }
+            }
+            lastGroup.add(i);
+        };
+
+        int sum = 0;
+        int[][] result = null;
+        for (int j = 0; j < ITERATIONS; j++) {
+            long start = System.currentTimeMillis();
+
+            MutableList<MutableIntList> grouped = IntLists.immutable.of(numbers)
+                    .primitiveStream()
+                    .sorted()
+                    .collect(
+                            ContiguousCollector::new,
+                            accumulator,
+                            (g1, g2) -> {
+                                g1.grouped.addAll(g2.grouped);
+                            }).grouped;
+
+            result = grouped.collect(groupList -> groupList.toArray()).toArray(new int[0][0]);
+
+            long end = System.currentTimeMillis();
+
+            sum += (end - start);
+        }
+
+        System.out.println("grouping int[] to int[][] via primitive Eclipse Collections with Collector\t" + (sum / ITERATIONS) + " ms");
+
+        return result;
+    }
+
+    public void getPositionsPerformance() {
+        List<Range> values = Arrays.asList(
+                new Range(0, 100_000),
+                new Range(100_001, 200_000),
+                new Range(200_001, 300_000),
+                new Range(300_001, 400_000),
+                new Range(400_001, 500_000),
+                new Range(500_001, 600_000),
+                new Range(600_001, 700_000),
+                new Range(700_001, 800_000),
+                new Range(800_001, 900_000),
+                new Range(900_001, 1_000_000));
+
+        int sum = 0;
+        int[] groupedByContiguous = null;
+        for (int j = 0; j < ITERATIONS; j++) {
+            long start = System.currentTimeMillis();
+
+            groupedByContiguous = PositionUtil.getPositions(values);
+
+            long end = System.currentTimeMillis();
+
+            sum += (end - start);
+        }
+
+        System.out.println("PositionUtil.getPositions(Collection<Range>) " + (sum / ITERATIONS) + " ms , array length = " + groupedByContiguous.length);
+    }
+
+    private static class RangeAccumulator implements ObjIntConsumer<ArrayList<Range>> {
+
+        @Override
+        public void accept(ArrayList<Range> ranges, int i) {
+            Range lastGroup;
+            if (ranges.size() > 0) {
+                lastGroup = ranges.get(ranges.size() - 1);
+            } else {
+                lastGroup = new Range(i, i + 1);
+                ranges.add(lastGroup);
+            }
+
+            int last = lastGroup.end;
+            if (i > last) {
+                lastGroup = new Range(i, i + 1);
+                ranges.add(lastGroup);
+            } else {
+                lastGroup.end = i + 1;
+            }
+        }
+
+    }
+
+    public static boolean equals(int[][] a, int[][] a2) {
+        if (a == a2)
+            return true;
+        if (a == null || a2 == null)
+            return false;
+
+        int length = a.length;
+        if (a2.length != length)
+            return false;
+
+        for (int i = 0; i < length; i++) {
+            int[] o1 = a[i];
+            int[] o2 = a2[i];
+            if (!(o1 == null ? o2 == null : Arrays.equals(o1, o2)))
+                return false;
+        }
+
+        return true;
+    }
+}
diff --git a/org.eclipse.nebula.widgets.nattable.test.performance/src/org/eclipse/nebula/widgets/nattable/test/performance/TreeLayerPerformanceTest.java b/org.eclipse.nebula.widgets.nattable.test.performance/src/org/eclipse/nebula/widgets/nattable/test/performance/TreeLayerPerformanceTest.java
new file mode 100644
index 0000000..2350000
--- /dev/null
+++ b/org.eclipse.nebula.widgets.nattable.test.performance/src/org/eclipse/nebula/widgets/nattable/test/performance/TreeLayerPerformanceTest.java
@@ -0,0 +1,256 @@
+/*****************************************************************************
+ * Copyright (c) 2020 Dirk Fauth.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *      Dirk Fauth <dirk.fauth@googlemail.com> - Initial API and implementation
+ *
+ *****************************************************************************/
+package org.eclipse.nebula.widgets.nattable.test.performance;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.eclipse.nebula.widgets.nattable.data.IColumnPropertyAccessor;
+import org.eclipse.nebula.widgets.nattable.data.ListDataProvider;
+import org.eclipse.nebula.widgets.nattable.data.ReflectiveColumnPropertyAccessor;
+import org.eclipse.nebula.widgets.nattable.dataset.person.Person;
+import org.eclipse.nebula.widgets.nattable.dataset.person.Person.Gender;
+import org.eclipse.nebula.widgets.nattable.dataset.person.PersonService;
+import org.eclipse.nebula.widgets.nattable.hideshow.RowHideShowLayer;
+import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
+import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
+import org.eclipse.nebula.widgets.nattable.tree.ITreeData;
+import org.eclipse.nebula.widgets.nattable.tree.ITreeRowModel;
+import org.eclipse.nebula.widgets.nattable.tree.TreeLayer;
+import org.eclipse.nebula.widgets.nattable.tree.TreeRowModel;
+import org.junit.Test;
+
+public class TreeLayerPerformanceTest {
+
+    @Test
+    public void performanceCollapseExpandAll() {
+        List<Person> values = PersonService.getFixedPersons();
+        for (int i = 42; i < 100_042; i++) {
+            values.add(new Person(i, "Ralph", "Wiggum", Gender.MALE, false, new Date()));
+        }
+
+        String[] propertyNames = { "lastName", "firstName", "gender", "married", "birthday" };
+        IColumnPropertyAccessor<Person> columnPropertyAccessor = new ReflectiveColumnPropertyAccessor<>(propertyNames);
+        ListDataProvider<Person> bodyDataProvider = new ListDataProvider<>(values, columnPropertyAccessor);
+        DataLayer bodyDataLayer = new DataLayer(bodyDataProvider);
+
+        ITreeRowModel<Person> treeRowModel = new TreeRowModel<>(new PersonTreeData(values));
+
+        RowHideShowLayer hideShowLayer = new RowHideShowLayer(bodyDataLayer);
+
+        SelectionLayer selectionLayer = new SelectionLayer(hideShowLayer);
+
+        TreeLayer treeLayer = new TreeLayer(selectionLayer, treeRowModel);
+
+        long start = System.currentTimeMillis();
+        treeLayer.collapseAll();
+        long end = System.currentTimeMillis();
+
+        assertEquals(3, treeLayer.getRowCount());
+        System.out.println("collapseAll() " + (end - start) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$
+
+        start = System.currentTimeMillis();
+        treeLayer.expandAll();
+        end = System.currentTimeMillis();
+
+        assertEquals(100_018, treeLayer.getRowCount());
+        System.out.println("expandAll() " + (end - start) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$
+    }
+
+    @Test
+    public void performanceCollapseAllExpandOneBigParent() {
+        List<Person> values = PersonService.getFixedPersons();
+        for (int i = 42; i < 100_042; i++) {
+            values.add(new Person(i, "Ralph", "Wiggum", Gender.MALE, false, new Date()));
+        }
+
+        String[] propertyNames = { "lastName", "firstName", "gender", "married", "birthday" };
+        IColumnPropertyAccessor<Person> columnPropertyAccessor = new ReflectiveColumnPropertyAccessor<>(propertyNames);
+        ListDataProvider<Person> bodyDataProvider = new ListDataProvider<>(values, columnPropertyAccessor);
+        DataLayer bodyDataLayer = new DataLayer(bodyDataProvider);
+
+        ITreeRowModel<Person> treeRowModel = new TreeRowModel<>(new PersonTreeData(values));
+
+        RowHideShowLayer hideShowLayer = new RowHideShowLayer(bodyDataLayer);
+
+        SelectionLayer selectionLayer = new SelectionLayer(hideShowLayer);
+
+        TreeLayer treeLayer = new TreeLayer(selectionLayer, treeRowModel);
+
+        long start = System.currentTimeMillis();
+        treeLayer.collapseAll();
+        long end = System.currentTimeMillis();
+
+        assertEquals(3, treeLayer.getRowCount());
+        System.out.println("collapseAll() " + (end - start) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$
+
+        start = System.currentTimeMillis();
+        treeLayer.expandTreeRow(18);
+        end = System.currentTimeMillis();
+
+        assertEquals(100_002, treeLayer.getRowCount());
+        System.out.println("expandRow() " + (end - start) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$
+    }
+
+    @Test
+    public void performanceBuildCache() {
+        List<Person> values = PersonService.getFixedPersons();
+        for (int i = 42; i < 1_000_042; i++) {
+            values.add(new Person(i, "Ralph", "Wiggum", Gender.MALE, false, new Date()));
+        }
+
+        String[] propertyNames = { "lastName", "firstName", "gender", "married", "birthday" };
+        IColumnPropertyAccessor<Person> columnPropertyAccessor = new ReflectiveColumnPropertyAccessor<>(propertyNames);
+        ListDataProvider<Person> bodyDataProvider = new ListDataProvider<>(values, columnPropertyAccessor);
+        DataLayer bodyDataLayer = new DataLayer(bodyDataProvider);
+
+        ITreeRowModel<Person> treeRowModel = new TreeRowModel<>(new PersonTreeData(values));
+
+        SelectionLayer selectionLayer = new SelectionLayer(bodyDataLayer);
+
+        TreeLayer treeLayer = new TreeLayer(selectionLayer, treeRowModel);
+
+        treeLayer.collapseTreeRow(0);
+
+        long start = System.currentTimeMillis();
+        assertEquals(1_000_011, treeLayer.getRowCount());
+        long end = System.currentTimeMillis();
+
+        System.out.println("buildCache() " + (end - start) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$
+    }
+
+    private static class PersonTreeData implements ITreeData<Person> {
+
+        private List<Person> values;
+
+        private Map<String, List<Person>> parentMapping;
+
+        private Map<String, Person> firstElementMapping = new HashMap<>();
+
+        public PersonTreeData(List<Person> values) {
+            this.values = values;
+
+            // first we need to sort by lastname to ensure all elements with the
+            // same lastname are grouped together
+            this.values.sort(Comparator.comparing(Person::getLastName));
+
+            // then we build up the mapping from lastname to all child elements
+            this.parentMapping = values.stream().collect(Collectors.groupingBy(Person::getLastName));
+
+            // identify the parent node element
+            String current = null;
+            for (Person p : this.values) {
+                if (p.getLastName() != current) {
+                    this.firstElementMapping.put(p.getLastName(), p);
+                    current = p.getLastName();
+                }
+            }
+
+            // remove the parent node element from the children list
+            this.firstElementMapping.forEach((lastname, parent) -> {
+                this.parentMapping.get(lastname).remove(parent);
+            });
+        }
+
+        @Override
+        public String formatDataForDepth(int depth, Person object) {
+            if (object != null) {
+                return object.toString();
+            } else {
+                return ""; //$NON-NLS-1$
+            }
+        }
+
+        @Override
+        public String formatDataForDepth(int depth, int index) {
+            return formatDataForDepth(depth, getDataAtIndex(index));
+        }
+
+        @Override
+        public int getDepthOfData(Person object) {
+            Person firstElement = this.firstElementMapping.get(object.getLastName());
+            return firstElement.equals(object) ? 0 : 1;
+        }
+
+        @Override
+        public int getDepthOfData(int index) {
+            return getDepthOfData(getDataAtIndex(index));
+        }
+
+        @Override
+        public Person getDataAtIndex(int index) {
+            if (!isValidIndex(index)) {
+                return null;
+            }
+            return this.values.get(index);
+        }
+
+        @Override
+        public int indexOf(Person child) {
+            return this.values.indexOf(child);
+        }
+
+        @Override
+        public boolean hasChildren(Person object) {
+            if (object != null && getDepthOfData(object) == 0) {
+                List<Person> children = this.parentMapping.get(object.getLastName());
+                return children != null && !children.isEmpty();
+            }
+            return false;
+        }
+
+        @Override
+        public boolean hasChildren(int index) {
+            return hasChildren(getDataAtIndex(index));
+        }
+
+        @Override
+        public List<Person> getChildren(Person object) {
+            if (object != null && getDepthOfData(object) == 0) {
+                return this.parentMapping.get(object.getLastName());
+            }
+            return new ArrayList<>(0);
+        }
+
+        @Override
+        public List<Person> getChildren(Person object, boolean fullDepth) {
+            // since we only support one level here it is the same as
+            // getChildren(PersonWithAddress)
+            return getChildren(object);
+        }
+
+        @Override
+        public List<Person> getChildren(int index) {
+            return getChildren(getDataAtIndex(index));
+        }
+
+        @Override
+        public int getElementCount() {
+            return this.values.size();
+        }
+
+        @Override
+        public boolean isValidIndex(int index) {
+            return (!(index < 0) && index < this.values.size());
+        }
+
+    }
+
+}