diff --git a/common/source/java/ch/systemsx/cisd/common/collections/GroupByMap.java b/common/source/java/ch/systemsx/cisd/common/collections/GroupByMap.java
new file mode 100644
index 0000000000000000000000000000000000000000..c62cf97db7f323ab9724f60e16549c365e88e2c8
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/collections/GroupByMap.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2011 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.common.collections;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A map which groups all elements added at the same key.
+ * 
+ * @author Tomasz Pylak
+ */
+public class GroupByMap<K, E>
+{
+    private final Map<K, List<E>> map;
+
+    private final IKeyExtractor<K, E> extractor;
+
+    /** @param extractor computes a key for the row */
+    public GroupByMap(final IKeyExtractor<K, E> extractor)
+    {
+        this.extractor = extractor;
+        this.map = new HashMap<K, List<E>>();
+    }
+
+    /** Creates a map for the specified rows with a given key extractor. */
+    public static <K, E> GroupByMap<K, E> create(final Iterable<E> rows,
+            final IKeyExtractor<K, E> extractor)
+    {
+        GroupByMap<K, E> table = new GroupByMap<K, E>(extractor);
+        for (E row : rows)
+        {
+            table.add(row);
+        }
+        return table;
+    }
+
+    /** Adds a new row. */
+    public void add(E row)
+    {
+        K key = extractor.getKey(row);
+        List<E> elements = map.get(key);
+        if (elements == null)
+        {
+            elements = new ArrayList<E>();
+        }
+        elements.add(row);
+        map.put(key, elements);
+    }
+
+    /** Returns all rows added at the specified key. */
+    public List<E> tryGet(K key)
+    {
+        return map.get(key);
+    }
+
+    /** @return all available keys */
+    public Set<K> getKeys()
+    {
+        return map.keySet();
+    }
+}
diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/collections/GroupByMapTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/collections/GroupByMapTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..61ee70f5243425deada2911641947ce0219079b9
--- /dev/null
+++ b/common/sourceTest/java/ch/systemsx/cisd/common/collections/GroupByMapTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2011 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.common.collections;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.testng.AssertJUnit;
+import org.testng.annotations.Test;
+
+/**
+ * Tests of {@link GroupByMap}.
+ * 
+ * @author Tomasz Pylak
+ */
+public class GroupByMapTest extends AssertJUnit
+{
+    private IKeyExtractor<Integer, Integer> createKeyExtractor()
+    {
+        return new IKeyExtractor<Integer, Integer>()
+            {
+                public Integer getKey(Integer e)
+                {
+                    return e;
+                }
+            };
+    }
+
+    @Test
+    public void test()
+    {
+        List<Integer> list = Arrays.asList(new Integer[]
+            { 1, 100, 1, 5, 100, 100 });
+        GroupByMap<Integer, Integer> map = GroupByMap.create(list, createKeyExtractor());
+        assertNull(map.tryGet(9));
+
+        List<Integer> list100 = map.tryGet(100);
+        assertNotNull(list100);
+        assertEquals(3, list100.size());
+
+        List<Integer> list5 = map.tryGet(5);
+        assertNotNull(list5);
+        assertEquals(1, list5.size());
+    }
+}