Bug 573025 - Introduce and apply builder for NamespaceList

To simplify and speed up the build of NamespaceLists a Builder is added,
which acts like a modifiable Collection and can efficiently be build
into a NamsepaceList or modified.

The new builder is applied where suitable.

Change-Id: I5269cb34512d0470df9c47849727929720d74cd3
Signed-off-by: Hannes Wellmann <wellmann.hannes1@gmx.net>
Reviewed-on: https://git.eclipse.org/r/c/equinox/rt.equinox.framework/+/180177
Tested-by: Equinox Bot <equinox-bot@eclipse.org>
Reviewed-by: Thomas Watson <tjwatson@us.ibm.com>
diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/AllTests.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/AllTests.java
index ab08f8e..e3ef17d 100644
--- a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/AllTests.java
+++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/AllTests.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2013 IBM Corporation and others.
+ * Copyright (c) 2013, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -24,6 +24,7 @@
 		suite.addTest(new JUnit4TestAdapter(ResolutionReportTest.class));
 		suite.addTest(new JUnit4TestAdapter(ModuleContainerUsageTest.class));
 		suite.addTest(new JUnit4TestAdapter(NamespaceListTest.class));
+		suite.addTest(new JUnit4TestAdapter(NamespaceListBuilderTest.class));
 		return suite;
 	}
 }
diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/NamespaceListBuilderTest.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/NamespaceListBuilderTest.java
new file mode 100644
index 0000000..9e45530
--- /dev/null
+++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/NamespaceListBuilderTest.java
@@ -0,0 +1,819 @@
+/*******************************************************************************
+ * Copyright (c) 2021 Hannes Wellmann and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Hannes Wellmann - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.tests.container;
+
+import static org.eclipse.osgi.tests.container.NamespaceListTest.build;
+import static org.eclipse.osgi.tests.container.NamespaceListTest.builderAddAfterLastMatch;
+import static org.eclipse.osgi.tests.container.NamespaceListTest.builderAddAll;
+import static org.eclipse.osgi.tests.container.NamespaceListTest.builderCreate;
+import static org.eclipse.osgi.tests.container.NamespaceListTest.builderRemoveElementsOfNamespaceIf;
+import static org.eclipse.osgi.tests.container.NamespaceListTest.builderRemoveNamespaceIf;
+import static org.eclipse.osgi.tests.container.NamespaceListTest.createEmptyNamespaceList;
+import static org.eclipse.osgi.tests.container.NamespaceListTest.getList;
+import static org.eclipse.osgi.tests.container.NamespaceListTest.newNamespace;
+import static org.eclipse.osgi.tests.container.NamespaceListTest.populate;
+import static org.eclipse.osgi.tests.container.NamespaceListTest.randomListSort;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NoSuchElementException;
+import java.util.function.Function;
+import org.eclipse.osgi.tests.container.NamespaceListTest.NamespaceElement;
+import org.junit.Test;
+
+public class NamespaceListBuilderTest {
+
+	@Test
+	public void testCreate() throws Exception {
+
+		Collection<NamespaceElement> builder = builderCreate();
+
+		assertTrue("Builder is not initially empty", builder.isEmpty());
+	}
+
+	@Test
+	public void testIteratorsElementSequence_multipleNamespace() throws Exception {
+		List<NamespaceElement> elements = populate(4, 3);
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		Iterator<NamespaceElement> iterator = builder.iterator();
+
+		for (int i = 0; i < 12; i++) {
+			assertTrue(iterator.hasNext());
+			assertSame(elements.get(i), iterator.next());
+		}
+		assertFalse(iterator.hasNext());
+	}
+
+	@Test
+	public void testIteratorsElementSequence_oneNamespace() throws Exception {
+		List<NamespaceElement> elements = populate(3, "ns-1");
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		Iterator<NamespaceElement> iterator = builder.iterator();
+
+		for (int i = 0; i < 3; i++) {
+			assertTrue(iterator.hasNext());
+			assertSame(elements.get(i), iterator.next());
+		}
+		assertFalse(iterator.hasNext());
+	}
+
+	@Test
+	public void testIteratorsElementSequence_empty() throws Exception {
+
+		Collection<NamespaceElement> builder = builderCreate();
+
+		Iterator<NamespaceElement> iterator = builder.iterator();
+
+		assertFalse(iterator.hasNext());
+	}
+
+	@Test
+	public void testIteratorsElementSequence_iterationBeyoundEnd_NoSuchElementException() throws Exception {
+		List<NamespaceElement> elements = populate(2, 3);
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		Iterator<NamespaceElement> iterator = builder.iterator();
+
+		for (int i = 0; i < 6; i++) {
+			assertTrue(iterator.hasNext());
+			assertSame(elements.get(i), iterator.next());
+		}
+		assertFalse(iterator.hasNext());
+		assertThrows(NoSuchElementException.class, iterator::next);
+	}
+
+	@Test
+	public void testIteratorsElementSequence_iterationBeyoundEndOfEmptyBuilder_NoSuchElementException()
+			throws Exception {
+
+		Collection<NamespaceElement> builder = builderCreate();
+
+		Iterator<NamespaceElement> iterator = builder.iterator();
+
+		assertFalse(iterator.hasNext());
+		assertThrows(NoSuchElementException.class, iterator::next);
+	}
+
+	@Test
+	public void testIteratorRemove_removeOneElement() throws Exception {
+
+		List<NamespaceElement> elements = populate(4, 3);
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		Iterator<NamespaceElement> iterator = builder.iterator();
+
+		for (int i = 0; i < 6; i++) {
+			iterator.next();
+		}
+		iterator.remove();
+
+		elements.remove(5);
+
+		assertStricEqualContent(builder, elements);
+	}
+
+	@Test
+	public void testIteratorRemove_RemoveAllElements() throws Exception {
+
+		List<NamespaceElement> elements = populate(4, 3);
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		Iterator<NamespaceElement> iterator = builder.iterator();
+
+		for (int i = 0; i < 12; i++) {
+			iterator.next();
+			iterator.remove();
+		}
+
+		assertStricEqualContent(builder, Collections.emptyList());
+	}
+
+	@Test
+	public void testIteratorRemove_RemoveTwice_IllegalStateException() throws Exception {
+
+		List<NamespaceElement> elements = populate(4, 3);
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		Iterator<NamespaceElement> iterator = builder.iterator();
+
+		iterator.next();
+		iterator.remove();
+		assertThrows(IllegalStateException.class, iterator::remove);
+
+		elements.remove(0);
+
+		assertStricEqualContent(builder, elements);
+	}
+
+	@Test
+	public void testIteratorRemove_NextNotCalled_IllegalStateException() throws Exception {
+
+		List<NamespaceElement> elements = populate(1, 1);
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		Iterator<NamespaceElement> iterator = builder.iterator();
+
+		assertThrows(IllegalStateException.class, iterator::remove);
+
+		assertStricEqualContent(builder, elements);
+	}
+
+	@Test
+	public void testClear() throws Exception {
+		List<NamespaceElement> elements = populate(3, 2);
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		Object namespaceList = build(builder);
+
+		builder.clear();
+
+		assertEquals(0, builder.size());
+		assertIterationOrderEquals(Collections.emptyList(), builder);
+
+		// assert that a previously build list is not affected
+
+		assertEquals(getList(namespaceList, null), elements);
+		assertEquals(getList(namespaceList, "ns-0"), elements.subList(0, 3));
+		assertEquals(getList(namespaceList, "ns-1"), elements.subList(3, 6));
+	}
+
+	// --- test addition ---
+
+	@Test
+	public void testAdd_singleElement() throws Exception {
+		NamespaceElement e = new NamespaceElement(1, "ns-1");
+
+		Collection<NamespaceElement> builder = builderCreate();
+
+		builder.add(e);
+
+		assertEquals(Collections.singletonList(e), getList(build(builder), "ns-1"));
+
+		assertStricEqualContent(builder, Collections.singletonList(e));
+	}
+
+	@Test
+	public void testAdd_multipleElements() throws Exception {
+		List<NamespaceElement> elements = populate(4, 3);
+
+		Collection<NamespaceElement> builder = builderCreate();
+
+		for (NamespaceElement element : elements) {
+			builder.add(element);
+		}
+
+		assertStricEqualContent(builder, elements);
+	}
+
+	@Test
+	public void testAddAll_namespaceSortedList() throws Exception {
+		List<NamespaceElement> elements = populate(4, 7);
+
+		Collection<NamespaceElement> builder = builderCreate();
+
+		builder.addAll(elements);
+
+		assertStricEqualContent(builder, elements);
+	}
+
+	@Test
+	public void testAddAll_randomlySortedList() throws Exception {
+		List<NamespaceElement> elements = populate(4, 7);
+		randomListSort(elements);
+
+		Collection<NamespaceElement> builder = builderCreate();
+
+		builder.addAll(elements);
+
+		assertEqualContent(builder, elements);
+	}
+
+	@Test
+	public void testAddAll_emptyList() throws Exception {
+
+		Collection<NamespaceElement> builder = builderCreate();
+
+		builder.addAll(Collections.emptyList());
+
+		assertEqualContent(builder, Collections.emptyList());
+	}
+
+	@Test
+	public void testAddAll_namespaceList() throws Exception {
+		List<NamespaceElement> elements = populate(5, 5);
+
+		Object namespaceList = newNamespace(elements);
+
+		Collection<NamespaceElement> builder = builderCreate();
+
+		builderAddAll(builder, namespaceList);
+
+		assertStricEqualContent(builder, elements);
+	}
+
+	@Test
+	public void testAddAll_emptyNamespaceList() throws Exception {
+
+		Object namespaceList = createEmptyNamespaceList(NamespaceElement::getNamespace);
+
+		Collection<NamespaceElement> builder = builderCreate();
+
+		builderAddAll(builder, namespaceList);
+
+		assertStricEqualContent(builder, Collections.emptyList());
+	}
+
+	@Test
+	public void testAddAfterLastMatch_matchUpUntilTheMiddle() throws Exception {
+		List<NamespaceElement> elements = populate(4, "ns-0");
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		NamespaceElement element = new NamespaceElement(5, "ns-0");
+		builderAddAfterLastMatch(builder, element, e -> e.id < 2);
+
+		elements.add(2, element);
+		assertStricEqualContent(builder, elements);
+	}
+
+	@Test
+	public void testAddAfterLastMatch_allElementsMatches() throws Exception {
+		List<NamespaceElement> elements = populate(4, "ns-0");
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		NamespaceElement element = new NamespaceElement(5, "ns-0");
+		builderAddAfterLastMatch(builder, element, e -> true);
+
+		elements.add(4, element);
+		assertStricEqualContent(builder, elements);
+	}
+
+	@Test
+	public void testAddAfterLastMatch_noMatch() throws Exception {
+		List<NamespaceElement> elements = populate(4, "ns-0");
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		NamespaceElement element = new NamespaceElement(5, "ns-0");
+		builderAddAfterLastMatch(builder, element, e -> e.id > 100);
+
+		elements.add(0, element);
+		assertStricEqualContent(builder, elements);
+	}
+
+	@Test
+	public void testAddAfterLastMatch_emptyNamespaceList() throws Exception {
+		Collection<NamespaceElement> builder = builderCreate();
+
+		NamespaceElement element = new NamespaceElement(5, "ns-0");
+		builderAddAfterLastMatch(builder, element, e -> e.id < 2);
+
+		assertStricEqualContent(builder, Collections.singletonList(element));
+	}
+
+	// --- test removal ---
+
+	@Test
+	public void testRemove_elementIsOneOfMultipleOfNamespace() throws Exception {
+		List<NamespaceElement> elements = populate(4, 4);
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		assertTrue(builder.remove(new NamespaceElement(2, "ns-0")));
+
+		elements.remove(2);
+		assertStricEqualContent(builder, elements);
+	}
+
+	@Test
+	public void testRemove_onlyElementOfNamspace() throws Exception {
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.add(new NamespaceElement(3, "ns-0"));
+
+		assertTrue(builder.remove(new NamespaceElement(3, "ns-0")));
+
+		assertStricEqualContent(builder, Collections.emptyList());
+	}
+
+	@Test
+	public void testRemove_elementNotContainedInNamespaceList() throws Exception {
+		List<NamespaceElement> elements = populate(4, 3);
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		assertFalse(builder.remove(new NamespaceElement(100, "ns-0")));
+
+		assertStricEqualContent(builder, elements);
+	}
+
+	@Test
+	public void testRemove_elementWithNotPresentNamespace() throws Exception {
+		List<NamespaceElement> elements = populate(4, 3);
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		assertFalse(builder.remove(new NamespaceElement(1, "ns-100")));
+
+		assertStricEqualContent(builder, elements);
+	}
+
+	@Test
+	public void testRemove_emptyBuilder() throws Exception {
+		Collection<NamespaceElement> builder = builderCreate();
+
+		assertFalse(builder.remove(new NamespaceElement(3, "ns-0")));
+
+		assertStricEqualContent(builder, Collections.emptyList());
+	}
+
+	@Test
+	@SuppressWarnings("unlikely-arg-type")
+	public void testRemove_argumentOfOtherClass() throws Exception {
+		List<NamespaceElement> elements = populate(2, 3);
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		assertFalse(builder.remove("someString"));
+
+		assertStricEqualContent(builder, elements);
+	}
+
+	@Test
+	public void testRemoveAll_multipleElementsInMultipleNamsespaces() throws Exception {
+		List<NamespaceElement> elements = populate(4, 5);
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		List<NamespaceElement> toRemove = new ArrayList<>();
+		toRemove.add(new NamespaceElement(1, "ns-0")); // has total index 1
+		toRemove.add(new NamespaceElement(2, "ns-1")); // has total index 6
+		toRemove.add(new NamespaceElement(0, "ns-1")); // has total index 4
+		toRemove.add(new NamespaceElement(3, "ns-3")); // has total index 15
+		toRemove.add(new NamespaceElement(2, "ns-3")); // has total index 14
+
+		assertTrue(builder.removeAll(toRemove));
+
+		elements.remove(15);
+		elements.remove(14);
+		elements.remove(6);
+		elements.remove(4);
+		elements.remove(1);
+
+		assertStricEqualContent(builder, elements);
+	}
+
+	@Test
+	public void testRemoveAll_multipleElementsInMultipleNamsespacesAndSomeNotPresent() throws Exception {
+		List<NamespaceElement> elements = populate(4, 5);
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		List<NamespaceElement> toRemove = new ArrayList<>();
+		toRemove.add(new NamespaceElement(1, "ns-0")); // has total index 1
+		toRemove.add(new NamespaceElement(2, "ns-1")); // has total index 6
+		toRemove.add(new NamespaceElement(100, "ns-2")); // not present
+		toRemove.add(new NamespaceElement(0, "ns-1")); // has total index 4
+		toRemove.add(new NamespaceElement(3, "ns-3")); // has total index 15
+		toRemove.add(new NamespaceElement(100, "ns-3")); // not present
+		toRemove.add(new NamespaceElement(2, "ns-3")); // has total index 14
+		toRemove.add(new NamespaceElement(100, "ns-3")); // not present
+		toRemove.add(new NamespaceElement(1, "ns-100")); // not present
+
+		assertTrue(builder.removeAll(toRemove));
+
+		elements.remove(15);
+		elements.remove(14);
+		elements.remove(6);
+		elements.remove(4);
+		elements.remove(1);
+
+		assertStricEqualContent(builder, elements);
+	}
+
+	@Test
+	public void testRemoveAll_listOfAllElementsInBuilder() throws Exception {
+		List<NamespaceElement> elements = populate(4, 5);
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		assertTrue(builder.removeAll(elements));
+
+		assertStricEqualContent(builder, Collections.emptyList());
+	}
+
+	@Test
+	public void testRemoveAll_emptyList() throws Exception {
+		List<NamespaceElement> elements = populate(4, 5);
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		assertFalse(builder.removeAll(Collections.emptyList()));
+
+		assertStricEqualContent(builder, elements);
+	}
+
+	@Test
+	public void testRemoveAll_emptyBuilder() throws Exception {
+		Collection<NamespaceElement> builder = builderCreate();
+
+		assertFalse(builder.removeAll(populate(4, 3)));
+
+		assertStricEqualContent(builder, Collections.emptyList());
+	}
+
+	@Test
+	@SuppressWarnings("unlikely-arg-type")
+	public void testRemoveAll_argumentListOfOtherClass() throws Exception {
+		List<NamespaceElement> elements = populate(2, 3);
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		List<String> listOfOtherElements = Arrays.asList("someString", "other");
+		assertFalse(builder.removeAll(listOfOtherElements));
+
+		assertStricEqualContent(builder, elements);
+	}
+
+	@Test
+	public void testRemoveNamespaceIf_NamespaceMatches() throws Exception {
+		List<NamespaceElement> elements = populate(4, 5);
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		Collection<String> namespacesToRemove = Arrays.asList("ns-0", "ns-2");
+
+		builderRemoveNamespaceIf(builder, namespacesToRemove::contains);
+
+		elements.subList(8, 12).clear();
+		elements.subList(0, 4).clear();
+
+		assertStricEqualContent(builder, elements);
+	}
+
+	@Test
+	public void testRemoveNamespaceIf_NamespaceMatchesExpunging() throws Exception {
+		List<NamespaceElement> elements = populate(3, 2);
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		builderRemoveNamespaceIf(builder, n -> true);
+
+		assertStricEqualContent(builder, Collections.emptyList());
+	}
+
+	@Test
+	public void testRemoveNamespaceIf_NoNamespaceMatches() throws Exception {
+		List<NamespaceElement> elements = populate(3, 3);
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		builderRemoveNamespaceIf(builder, "ns-100"::equals);
+
+		assertStricEqualContent(builder, elements);
+	}
+
+	@Test
+	public void testRemoveIf_multipleMatches() throws Exception {
+		List<NamespaceElement> elements = populate(5, 5);
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		builder.removeIf(e -> e.id % 3 == 0 || "ns-1".equals(e.namespace));
+
+		elements.remove(23); // first and third of 20-25
+		elements.remove(20);
+
+		elements.remove(18); // first and third of 15-20
+		elements.remove(15);
+
+		elements.remove(13); // first and third of 10-15
+		elements.remove(10);
+
+		elements.subList(5, 10).clear(); // all of 5-10
+
+		elements.remove(3); // first and third of 0-5
+		elements.remove(0);
+
+		assertStricEqualContent(builder, elements);
+	}
+
+	@Test
+	public void testRemoveIf_allMatches() throws Exception {
+		List<NamespaceElement> elements = populate(4, 3);
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		builder.removeIf(e -> true);
+
+		assertStricEqualContent(builder, Collections.emptyList());
+	}
+
+	@Test
+	public void testRemoveIf_noMatches() throws Exception {
+		List<NamespaceElement> elements = populate(4, 3);
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		builder.removeIf(e -> false);
+
+		assertStricEqualContent(builder, elements);
+	}
+
+	@Test
+	public void testRemoveElementsOfNamespaceIf_multipleMatches() throws Exception {
+		List<NamespaceElement> elements = populate(4, 2);
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		builderRemoveElementsOfNamespaceIf(builder, "ns-0", e -> e.id == 1 || e.id == 2);
+
+		elements.remove(2);
+		elements.remove(1);
+
+		assertStricEqualContent(builder, elements);
+	}
+
+	@Test
+	public void testRemoveElementsOfNamespaceIf_allElementsOfNamespaceMatch() throws Exception {
+		List<NamespaceElement> elements = populate(4, 2);
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		builderRemoveElementsOfNamespaceIf(builder, "ns-0", e -> e.id == 1 || e.id == 2);
+
+		elements.remove(2);
+		elements.remove(1);
+
+		assertStricEqualContent(builder, elements);
+	}
+
+	@Test
+	public void testRemoveElementsOfNamespaceIf_allElementsMatch() throws Exception {
+		List<NamespaceElement> elements = populate(4, 1);
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		builderRemoveElementsOfNamespaceIf(builder, "ns-0", e -> true);
+
+		assertStricEqualContent(builder, Collections.emptyList());
+	}
+
+	@Test
+	public void testRemoveElementsOfNamespaceIf_noMatch() throws Exception {
+		List<NamespaceElement> elements = populate(4, 1);
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		builderRemoveElementsOfNamespaceIf(builder, "ns-0", e -> false);
+
+		assertStricEqualContent(builder, elements);
+	}
+
+	@Test
+	public void testRemoveElementsOfNamespaceIf_namespaceNotPresent() throws Exception {
+		List<NamespaceElement> elements = populate(4, 1);
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		builderRemoveElementsOfNamespaceIf(builder, "ns-100", e -> true);
+
+		assertStricEqualContent(builder, elements);
+	}
+
+	// --- test build ---
+
+	@Test
+	public void testBuild_notEmptyBuilder() throws Exception {
+		List<NamespaceElement> elements = populate(4, 2);
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		Object namespaceList1 = build(builder);
+
+		assertEquals(elements, getList(namespaceList1, null));
+		assertEquals(elements.subList(0, 4), getList(namespaceList1, "ns-0"));
+		assertEquals(elements.subList(4, 8), getList(namespaceList1, "ns-1"));
+
+		Object namespaceList2 = build(builder);
+
+		assertEquals(elements, getList(namespaceList1, null));
+		assertEquals(elements.subList(0, 4), getList(namespaceList1, "ns-0"));
+		assertEquals(elements.subList(4, 8), getList(namespaceList1, "ns-1"));
+
+		assertEquals(elements, getList(namespaceList2, null));
+		assertEquals(elements.subList(0, 4), getList(namespaceList2, "ns-0"));
+		assertEquals(elements.subList(4, 8), getList(namespaceList2, "ns-1"));
+	}
+
+	@Test
+	public void testBuild_emptyBuilder() throws Exception {
+		Collection<NamespaceElement> builder = builderCreate();
+
+		Object namespaceList = build(builder);
+
+		assertEquals(Collections.emptyList(), getList(namespaceList, null));
+	}
+
+	@Test
+	public void testBuild_subsequentModificationOfBuilder() throws Exception {
+		List<NamespaceElement> elements = populate(4, 2);
+
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+
+		Object namespaceList1 = build(builder);
+
+		assertEquals(elements, getList(namespaceList1, null));
+		assertEquals(elements.subList(0, 4), getList(namespaceList1, "ns-0"));
+		assertEquals(elements.subList(4, 8), getList(namespaceList1, "ns-1"));
+
+		List<NamespaceElement> additionalElements = populate(1, 3);
+		builder.addAll(additionalElements);
+
+		List<NamespaceElement> newElements = new ArrayList<>(elements);
+
+		newElements.add(8, additionalElements.get(2));
+		newElements.add(8, additionalElements.get(1));
+		newElements.add(4, additionalElements.get(0));
+
+		// assert the first list build is not modified
+		assertEquals(elements, getList(namespaceList1, null));
+		assertEquals(elements.subList(0, 4), getList(namespaceList1, "ns-0"));
+		assertEquals(elements.subList(4, 8), getList(namespaceList1, "ns-1"));
+
+		// asser the new content of the builder is as expected
+		assertStricEqualContent(builder, newElements);
+	}
+
+	// --- utility methods ---
+
+	private static void assertStricEqualContent(Collection<NamespaceElement> builder,
+			List<NamespaceElement> expectedElements) throws Exception {
+		assertStricEqualContent(builder, expectedElements, NamespaceElement::getNamespace);
+	}
+
+	private static <E> void assertStricEqualContent(Collection<E> builder, List<E> expectedElements,
+			Function<E, String> getNamespace) throws Exception {
+		// test all properties of the builder and its build list in order to ensure they
+		// are updated correctly
+
+		assertIterationOrderEquals(expectedElements, builder);
+		assertEquals(expectedElements.size(), builder.size());
+
+		Map<String, List<E>> namespaceElements = getNamespaceElements(expectedElements, getNamespace);
+		Object namespaceList = build(builder);
+
+		assertEquals(expectedElements.size(), getList(namespaceList, null).size());
+
+		for (Entry<String, List<E>> entry : namespaceElements.entrySet()) {
+			String namespace = entry.getKey();
+			List<E> elements = entry.getValue();
+			assertEquals(elements, getList(namespaceList, namespace));
+		}
+
+		assertEquals(expectedElements, getList(namespaceList, null));
+	}
+
+	private static void assertEqualContent(Collection<NamespaceElement> builder,
+			List<NamespaceElement> expectedElements) throws Exception {
+		// test all properties of the builder and its build list in order to ensure they
+		// are updated correctly
+
+		assertContentEquals(expectedElements, builder);
+		assertEquals(expectedElements.size(), builder.size());
+
+		Map<String, List<NamespaceElement>> namespaceElements = getNamespaceElements(expectedElements,
+				NamespaceElement::getNamespace);
+		Object namespaceList = build(builder);
+
+		assertEquals(expectedElements.size(), getList(namespaceList, null).size());
+
+		for (Entry<String, List<NamespaceElement>> entry : namespaceElements.entrySet()) {
+			String namespace = entry.getKey();
+			List<NamespaceElement> elements = entry.getValue();
+			assertContentEquals(elements, getList(namespaceList, namespace));
+		}
+
+		assertContentEquals(expectedElements, getList(namespaceList, null));
+	}
+
+	private static <E> void assertIterationOrderEquals(Collection<E> expected, Collection<E> actual) {
+		// instead of comparing the iterators, simply compare List-copies. They reflect
+		// the iteration order.
+		assertEquals(new ArrayList<>(expected), new ArrayList<>(actual));
+	}
+
+	private static <E> void assertContentEquals(List<E> expected, Collection<E> actual) {
+		assertEquals(new HashSet<>(expected), new HashSet<>(actual));
+	}
+
+	private static <E> Map<String, List<E>> getNamespaceElements(List<E> elements, Function<E, String> getNamespace) {
+		Map<String, List<E>> namespaceElements = new HashMap<>();
+		for (E element : elements) {
+			namespaceElements.computeIfAbsent(getNamespace.apply(element), n -> new ArrayList<>()).add(element);
+		}
+		return namespaceElements;
+	}
+}
diff --git a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/NamespaceListTest.java b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/NamespaceListTest.java
index dea96f9..65a8af7 100644
--- a/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/NamespaceListTest.java
+++ b/bundles/org.eclipse.osgi.tests/src/org/eclipse/osgi/tests/container/NamespaceListTest.java
@@ -10,23 +10,24 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Hannes Wellmann - Bug 573025: introduce and apply NamespaceList.Builder
  *******************************************************************************/
 package org.eclipse.osgi.tests.container;
 
 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.assertThrows;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
-import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
-import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Random;
 import java.util.function.Function;
+import java.util.function.Predicate;
 import org.junit.Test;
 import org.osgi.framework.Bundle;
 
@@ -34,24 +35,96 @@
  * Using reflection because to avoid exporting internals.
  */
 public class NamespaceListTest extends AbstractTest {
-	static final Method getList;
-	static final Method isEmpty;
-	static final Method getNamespaceIndex;
-	static final Method copyList;
-	static final Constructor<?> newNamespaceList;
+	static final Method NAMESPACELIST_GET_LIST;
+	static final Method NAMESPACELIST_IS_EMPTY;
+	static final Method NAMESPACELIST_EMPTY;
+	static final Method NAMESPACELIST_CREATE_BUILDER;
+
+	// only access fields reflectively that are not part of the Collection-API
+	static final Method BUILDER_CREATE;
+	static final Method BUILDER_ADD_ALL;
+	static final Method BUILDER_ADD_AFTER_LAST_MATCH;
+	static final Method BUILDER_REMOVE_NAMESPACE_IF;
+	static final Method BUILDER_REMOVE_ELEMENTS_OF_NAMESPACE_IF;
+	static final Method BUILDER_BUILD;
+
 	static {
 		try {
-			Class<?> namespaceList = Bundle.class.getClassLoader()
-					.loadClass("org.eclipse.osgi.internal.container.NamespaceList");
-			getList = namespaceList.getMethod("getList", String.class);
-			isEmpty = namespaceList.getMethod("isEmpty");
-			getNamespaceIndex = namespaceList.getMethod("getNamespaceIndex", String.class);
-			copyList = namespaceList.getMethod("copyList");
-			newNamespaceList = namespaceList.getConstructor(List.class, Function.class);
+			ClassLoader classLoader = Bundle.class.getClassLoader();
+			Class<?> namespaceList = classLoader.loadClass("org.eclipse.osgi.internal.container.NamespaceList");
+			Class<?> namespaceListBuilder = classLoader
+					.loadClass("org.eclipse.osgi.internal.container.NamespaceList$Builder");
+
+			NAMESPACELIST_GET_LIST = namespaceList.getMethod("getList", String.class);
+			NAMESPACELIST_IS_EMPTY = namespaceList.getMethod("isEmpty");
+			NAMESPACELIST_EMPTY = namespaceList.getMethod("empty", Function.class);
+			NAMESPACELIST_CREATE_BUILDER = namespaceList.getMethod("createBuilder");
+
+			BUILDER_CREATE = namespaceListBuilder.getMethod("create", Function.class);
+			BUILDER_ADD_ALL = namespaceListBuilder.getMethod("addAll", namespaceList);
+			BUILDER_ADD_AFTER_LAST_MATCH = namespaceListBuilder.getMethod("addAfterLastMatch", Object.class,
+					Predicate.class);
+			BUILDER_REMOVE_NAMESPACE_IF = namespaceListBuilder.getMethod("removeNamespaceIf", Predicate.class);
+			BUILDER_REMOVE_ELEMENTS_OF_NAMESPACE_IF = namespaceListBuilder.getMethod("removeElementsOfNamespaceIf",
+					String.class, Predicate.class);
+			BUILDER_BUILD = namespaceListBuilder.getMethod("build");
 		} catch (Throwable t) {
 			throw new RuntimeException(t);
 		}
 	}
+
+	static Object newNamespace(List<NamespaceElement> elements) throws Exception {
+		Collection<NamespaceElement> builder = builderCreate();
+		builder.addAll(elements);
+		return build(builder);
+	}
+
+	// --- reflectively invoked methods of NamespaceList ---
+
+	static <E> Object createEmptyNamespaceList(Function<E, String> getNamespace) throws Exception {
+		return NAMESPACELIST_EMPTY.invoke(null, getNamespace);
+	}
+
+	static boolean isEmpty(Object namespaceList) throws Exception {
+		return (boolean) NAMESPACELIST_IS_EMPTY.invoke(namespaceList);
+	}
+
+	static List<NamespaceElement> getList(Object namespaceList, String namespace) throws Exception {
+		return (List<NamespaceElement>) NAMESPACELIST_GET_LIST.invoke(namespaceList, namespace);
+	}
+
+	static Collection<NamespaceElement> createBuilder(Object namespaceList) throws Exception {
+		return (Collection<NamespaceElement>) NAMESPACELIST_CREATE_BUILDER.invoke(namespaceList);
+	}
+
+	// --- reflectively invoked non-Collection methods of NamespaceList$Builder ---
+
+	static Collection<NamespaceElement> builderCreate() throws Exception {
+		Function<NamespaceElement, String> getNamespace = NamespaceElement::getNamespace;
+		return (Collection<NamespaceElement>) BUILDER_CREATE.invoke(null, getNamespace);
+	}
+
+	static <E> void builderAddAll(Collection<E> builder, Object namespaceList) throws Exception {
+		BUILDER_ADD_ALL.invoke(builder, namespaceList);
+	}
+
+	static <E> void builderAddAfterLastMatch(Collection<E> builder, E e, Predicate<E> matcher) throws Exception {
+		BUILDER_ADD_AFTER_LAST_MATCH.invoke(builder, e, matcher);
+	}
+
+	static <E> void builderRemoveNamespaceIf(Collection<E> builder, Predicate<String> filter) throws Exception {
+		BUILDER_REMOVE_NAMESPACE_IF.invoke(builder, filter);
+	}
+
+	static <E> void builderRemoveElementsOfNamespaceIf(Collection<E> builder, String namespace, Predicate<E> filter)
+			throws Exception {
+		BUILDER_REMOVE_ELEMENTS_OF_NAMESPACE_IF.invoke(builder, namespace, filter);
+	}
+
+	static <E> Object build(Collection<E> builder) throws Exception {
+		return BUILDER_BUILD.invoke(builder);
+	}
+
 	static class NamespaceElement {
 		final int id;
 		final String namespace;
@@ -81,7 +154,7 @@
 
 		@Override
 		public int hashCode() {
-			return namespace.hashCode() ^ id;
+			return Objects.hash(namespace, id);
 		}
 
 		@Override
@@ -90,10 +163,14 @@
 		}
 	}
 
-	static final Function<NamespaceElement, String> getNamespaceFunc = (Function<NamespaceElement, String>) NamespaceElement::getNamespace;
-	Object newNamespace(List<NamespaceElement> elements) throws Exception {
-		return newNamespaceList.newInstance(elements, getNamespaceFunc);
+	// --- tests ---
+
+	@Test
+	public void testCreateEmptyList() throws Exception {
+		Object namespaceList = createEmptyNamespaceList(NamespaceElement::getNamespace);
+		assertTrue("List is not empty.", isEmpty(namespaceList));
 	}
+
 	@Test
 	public void testIsEmpty() throws Exception {
 		Object namespaceList = newNamespace(Collections.emptyList());
@@ -109,14 +186,6 @@
 		assertFalse("List is empty.", isEmpty(namespaceList));
 	}
 
-	private boolean isEmpty(Object namespaceList) throws Exception {
-		return (boolean) isEmpty.invoke(namespaceList);
-	}
-
-	private List<NamespaceElement> getList(Object namespaceList, String namespace) throws Exception {
-		return (List<NamespaceElement>) getList.invoke(namespaceList, namespace);
-	}
-
 	@Test
 	public void testGetList() throws Exception {
 		Object namespaceList = newNamespace(Collections.emptyList());
@@ -145,31 +214,9 @@
 	}
 
 	@Test
-	public void testGetNamespaceIndex() throws Exception {
-		Object namespaceList = newNamespace(Collections.emptyList());
-		assertNull("Unexpected index.", getNamespaceIndex(namespaceList, "ns-0"));
-
-		List<NamespaceElement> elements = populate(21, 13);
-
-		namespaceList = newNamespace(elements);
-		Entry<Integer, Integer> nsIndex = getNamespaceIndex(namespaceList, "ns-0");
-		assertNotNull("Expected an index", nsIndex);
-		checkIndex(nsIndex, 0, 21);
-
-		nsIndex = getNamespaceIndex(namespaceList, "ns-12");
-		assertNotNull("Expected an index", nsIndex);
-		checkIndex(nsIndex, 21 * 12, 21 * 13);
-
-		nsIndex = getNamespaceIndex(namespaceList, "ns-4");
-		assertNotNull("Expected an index", nsIndex);
-		checkIndex(nsIndex, 21 * 4, 21 * 5);
-	}
-
-	@Test
 	public void testOutOfOrderNamespace() throws Exception {
 		List<NamespaceElement> elements = populate(4, 4);
-		// random sort by hashcode
-		elements.sort((n1, n2) -> n1.hashCode() - n2.hashCode());
+		randomListSort(elements);
 		Object namespaceList = newNamespace(elements);
 		for (int i = 0; i < 4; i++) {
 			List<NamespaceElement> list = getList(namespaceList, "ns-" + i);
@@ -183,54 +230,29 @@
 	}
 
 	@Test
-	public void testCopyList() throws Exception {
-		Object namespaceList = newNamespace(Collections.emptyList());
-		List<NamespaceElement> copy = copyList(namespaceList);
-		assertEquals("Wrong list.", Collections.emptyList(), copy);
-		successAdd(copy);
-		copy = copyList(namespaceList);
-		assertEquals("Wrong list.", Collections.emptyList(), copy);
+	public void testCreateBuilder() throws Exception {
 
-		List<NamespaceElement> elements = populate(100, 13);
-		namespaceList = newNamespace(elements);
-		copy = copyList(namespaceList);
-		assertEquals("Wrong list.", elements, copy);
-		successAdd(copy);
-		copy = copyList(namespaceList);
-		assertEquals("Wrong list.", elements, copy);
+		List<NamespaceElement> elements = populate(5, 10);
+		Object namespaceList = newNamespace(elements);
+		Collection<NamespaceElement> builder = createBuilder(namespaceList);
+
+		Object buildNamespaceList = build(builder);
+
+		// The order of all elements should be maintained
+
+		assertEquals("Builder not populated correctly", getList(buildNamespaceList, null),
+				getList(namespaceList, null));
 	}
 
-	private List<NamespaceElement> copyList(Object namespaceList) throws Exception {
-		return (List<NamespaceElement>) copyList.invoke(namespaceList);
-	}
-
-	private void checkIndex(Entry<Integer, Integer> nsIndex, int start, int end) {
-		assertEquals("Unexpected Start", start, (int) nsIndex.getKey());
-	}
-
-	private Entry<Integer, Integer> getNamespaceIndex(Object namespaceList, String namespace) throws Exception {
-		return (Entry<Integer, Integer>) getNamespaceIndex.invoke(namespaceList, namespace);
-	}
+	// --- uility methods ---
 
 	private void failAdd(List<NamespaceElement> list) {
-		try {
-			list.add(new NamespaceElement(0, "ns"));
-			fail("Should fail to modify list");
-		} catch (UnsupportedOperationException e) {
-			// expected
-		}
+		NamespaceElement e = new NamespaceElement(0, "ns");
+		assertThrows(UnsupportedOperationException.class, () -> list.add(e));
 	}
 
-	private void successAdd(List<NamespaceElement> list) {
-		try {
-			list.add(new NamespaceElement(0, "ns"));
-		} catch (UnsupportedOperationException e) {
-			fail("Should not fail to modify list");
-		}
-	}
-
-	private List<NamespaceElement> populate(int numElementsPerNS, int numNS) {
-		ArrayList<NamespaceElement> elements = new ArrayList<>(numElementsPerNS * numNS);
+	static List<NamespaceElement> populate(int numElementsPerNS, int numNS) {
+		List<NamespaceElement> elements = new ArrayList<>(numElementsPerNS * numNS);
 		for (int namespace = 0; namespace < numNS; namespace++) {
 			for (int element = 0; element < numElementsPerNS; element++) {
 				elements.add(new NamespaceElement(element, "ns-" + namespace));
@@ -239,11 +261,16 @@
 		return elements;
 	}
 
-	private List<NamespaceElement> populate(int numElements, String namespace) {
-		ArrayList<NamespaceElement> elements = new ArrayList<>(numElements);
+	static List<NamespaceElement> populate(int numElements, String namespace) {
+		List<NamespaceElement> elements = new ArrayList<>(numElements);
 		for (int element = 0; element < numElements; element++) {
 			elements.add(new NamespaceElement(element, namespace));
 		}
 		return elements;
 	}
+
+	static void randomListSort(List<NamespaceElement> elements) {
+		// random sort in reproducible order
+		Collections.shuffle(elements, new Random(43L));
+	}
 }
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleContainer.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleContainer.java
index 52367f3..c8f1919 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleContainer.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleContainer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2020 IBM Corporation and others.
+ * Copyright (c) 2012, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -13,8 +13,6 @@
  *******************************************************************************/
 package org.eclipse.osgi.container;
 
-import static org.eclipse.osgi.internal.container.NamespaceList.WIRE;
-
 import java.io.Closeable;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
@@ -1049,9 +1047,9 @@
 					return null; // need to try again
 				// remove any wires from unresolved wirings that got removed
 				for (Map.Entry<ModuleWiring, Collection<ModuleWire>> entry : toRemoveWireLists.entrySet()) {
-					List<ModuleWire> provided = entry.getKey().getProvidedWires().copyList();
+					NamespaceList.Builder<ModuleWire> provided = entry.getKey().getProvidedWires().createBuilder();
 					provided.removeAll(entry.getValue());
-					entry.getKey().setProvidedWires(new NamespaceList<>(provided, WIRE));
+					entry.getKey().setProvidedWires(provided.build());
 					for (ModuleWire removedWire : entry.getValue()) {
 						// invalidate the wire
 						removedWire.invalidate();
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleDatabase.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleDatabase.java
index 4e8689a..108714d 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleDatabase.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleDatabase.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2020 IBM Corporation and others.
+ * Copyright (c) 2012, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -13,8 +13,6 @@
  *******************************************************************************/
 package org.eclipse.osgi.container;
 
-import static org.eclipse.osgi.internal.container.NamespaceList.WIRE;
-
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
@@ -44,6 +42,7 @@
 import org.eclipse.osgi.internal.container.Capabilities;
 import org.eclipse.osgi.internal.container.ComputeNodeOrder;
 import org.eclipse.osgi.internal.container.NamespaceList;
+import org.eclipse.osgi.internal.container.NamespaceList.Builder;
 import org.eclipse.osgi.internal.framework.EquinoxConfiguration;
 import org.osgi.framework.BundleException;
 import org.osgi.framework.Constants;
@@ -412,9 +411,9 @@
 				}
 				// remove any wires from unresolved wirings that got removed
 				for (Map.Entry<ModuleWiring, Collection<ModuleWire>> entry : toRemoveWireLists.entrySet()) {
-					List<ModuleWire> provided = entry.getKey().getProvidedWires().copyList();
+					NamespaceList.Builder<ModuleWire> provided = entry.getKey().getProvidedWires().createBuilder();
 					provided.removeAll(entry.getValue());
-					entry.getKey().setProvidedWires(new NamespaceList<>(provided, WIRE));
+					entry.getKey().setProvidedWires(provided.build());
 					for (ModuleWire removedWire : entry.getValue()) {
 						// invalidate the wire
 						removedWire.invalidate();
@@ -503,13 +502,9 @@
 	final Map<ModuleRevision, ModuleWiring> getWiringsClone() {
 		readLock();
 		try {
-			Map<ModuleRevision, ModuleWiring> clonedWirings = new HashMap<>();
-			for (Map.Entry<ModuleRevision, ModuleWiring> entry : wirings.entrySet()) {
-				ModuleWiring wiring = new ModuleWiring(entry.getKey(), entry.getValue().getCapabilities(),
-						entry.getValue().getRequirements(), entry.getValue().getProvidedWires(),
-						entry.getValue().getRequiredWires(), entry.getValue().getSubstitutedNames());
-				clonedWirings.put(entry.getKey(), wiring);
-			}
+			Map<ModuleRevision, ModuleWiring> clonedWirings = new HashMap<>(wirings);
+			clonedWirings.replaceAll((r, w) -> new ModuleWiring(r, w.getCapabilities(), w.getRequirements(),
+					w.getProvidedWires(), w.getRequiredWires(), w.getSubstitutedNames()));
 			return clonedWirings;
 		} finally {
 			readUnlock();
@@ -1393,25 +1388,25 @@
 				throw new NullPointerException("Could not find revision for wiring."); //$NON-NLS-1$
 
 			int numCapabilities = in.readInt();
-			List<ModuleCapability> capabilities = new ArrayList<>(numCapabilities);
+			NamespaceList.Builder<ModuleCapability> capabilities = Builder.create(NamespaceList.CAPABILITY);
 			for (int i = 0; i < numCapabilities; i++) {
 				capabilities.add((ModuleCapability) objectTable.get(in.readInt()));
 			}
 
 			int numRequirements = in.readInt();
-			List<ModuleRequirement> requirements = new ArrayList<>(numRequirements);
+			NamespaceList.Builder<ModuleRequirement> requirements = Builder.create(NamespaceList.REQUIREMENT);
 			for (int i = 0; i < numRequirements; i++) {
 				requirements.add((ModuleRequirement) objectTable.get(in.readInt()));
 			}
 
 			int numProvidedWires = in.readInt();
-			List<ModuleWire> providedWires = new ArrayList<>(numProvidedWires);
+			NamespaceList.Builder<ModuleWire> providedWires = Builder.create(NamespaceList.WIRE);
 			for (int i = 0; i < numProvidedWires; i++) {
 				providedWires.add((ModuleWire) objectTable.get(in.readInt()));
 			}
 
 			int numRequiredWires = in.readInt();
-			List<ModuleWire> requiredWires = new ArrayList<>(numRequiredWires);
+			NamespaceList.Builder<ModuleWire> requiredWires = Builder.create(NamespaceList.WIRE);
 			for (int i = 0; i < numRequiredWires; i++) {
 				requiredWires.add((ModuleWire) objectTable.get(in.readInt()));
 			}
@@ -1422,7 +1417,8 @@
 				substituted.add(readString(in, objectTable));
 			}
 
-			return new ModuleWiring(revision, capabilities, requirements, providedWires, requiredWires, substituted);
+			return new ModuleWiring(revision, capabilities.build(), requirements.build(), providedWires.build(),
+					requiredWires.build(), substituted);
 		}
 
 		private static void writeGenericInfo(String namespace, Map<String, ?> attributes, Map<String, String> directives, DataOutputStream out, Map<Object, Integer> objectTable) throws IOException {
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleResolver.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleResolver.java
index 3a40b5b..91eebcf 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleResolver.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleResolver.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2020 IBM Corporation and others.
+ * Copyright (c) 2012, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -29,7 +29,6 @@
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.ListIterator;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CancellationException;
@@ -40,7 +39,6 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.Function;
 import org.apache.felix.resolver.Logger;
 import org.apache.felix.resolver.ResolutionError;
 import org.apache.felix.resolver.ResolverImpl;
@@ -48,6 +46,7 @@
 import org.eclipse.osgi.container.namespaces.EquinoxFragmentNamespace;
 import org.eclipse.osgi.internal.container.InternalUtils;
 import org.eclipse.osgi.internal.container.NamespaceList;
+import org.eclipse.osgi.internal.container.NamespaceList.Builder;
 import org.eclipse.osgi.internal.debug.Debug;
 import org.eclipse.osgi.internal.framework.EquinoxConfiguration;
 import org.eclipse.osgi.internal.framework.EquinoxContainer;
@@ -183,17 +182,15 @@
 
 	Map<ModuleRevision, ModuleWiring> generateDelta(Map<Resource, List<Wire>> result, Map<ModuleRevision, ModuleWiring> wiringCopy) {
 		Map<ModuleRevision, Map<ModuleCapability, List<ModuleWire>>> provided = new HashMap<>();
-		Map<ModuleRevision, List<ModuleWire>> required = new HashMap<>();
+		Map<ModuleRevision, NamespaceList<ModuleWire>> required = new HashMap<>(result.size() * 4 / 3 + 1);
 		// First populate the list of provided and required wires for revision
 		// This is done this way to share the wire object between both the provider and requirer
 		for (Map.Entry<Resource, List<Wire>> resultEntry : result.entrySet()) {
 			ModuleRevision revision = (ModuleRevision) resultEntry.getKey();
-			List<ModuleWire> requiredWires = new ArrayList<>(resultEntry.getValue().size());
-			List<String> seen = new ArrayList<>(5);
+			NamespaceList.Builder<ModuleWire> requiredWires = NamespaceList.Builder.create(WIRE);
 			for (Wire wire : resultEntry.getValue()) {
 				ModuleWire moduleWire = new ModuleWire((ModuleCapability) wire.getCapability(), (ModuleRevision) wire.getProvider(), (ModuleRequirement) wire.getRequirement(), (ModuleRevision) wire.getRequirer());
-				requiredWires.add(getInsertIndex(moduleWire.getCapability().getNamespace(), requiredWires, seen,
-						NamespaceList.WIRE), moduleWire);
+				requiredWires.add(moduleWire);
 				Map<ModuleCapability, List<ModuleWire>> providedWiresMap = provided.get(moduleWire.getProvider());
 				if (providedWiresMap == null) {
 					providedWiresMap = new HashMap<>();
@@ -206,7 +203,7 @@
 				}
 				providedWires.add(moduleWire);
 			}
-			required.put(revision, requiredWires);
+			required.put(revision, requiredWires.build());
 		}
 
 		Map<ModuleRevision, ModuleWiring> delta = new HashMap<>();
@@ -232,148 +229,96 @@
 		return delta;
 	}
 
-	private ModuleWiring createNewWiring(ModuleRevision revision, Map<ModuleRevision, Map<ModuleCapability, List<ModuleWire>>> provided, Map<ModuleRevision, List<ModuleWire>> required) {
-		Map<ModuleCapability, List<ModuleWire>> providedWireMap = provided.get(revision);
-		if (providedWireMap == null)
-			providedWireMap = Collections.emptyMap();
-		List<ModuleWire> requiredWires = required.get(revision);
-		if (requiredWires == null)
-			requiredWires = Collections.emptyList();
+	private ModuleWiring createNewWiring(ModuleRevision revision, Map<ModuleRevision, Map<ModuleCapability, List<ModuleWire>>> provided, Map<ModuleRevision, NamespaceList<ModuleWire>> required) {
 
-		List<ModuleCapability> capabilities = sortByNamespaceCopy(revision.getModuleCapabilities(null), CAPABILITY);
-		ListIterator<ModuleCapability> iCapabilities = capabilities.listIterator(capabilities.size());
-		List<ModuleRequirement> requirements = sortByNamespaceCopy(revision.getModuleRequirements(null), REQUIREMENT);
-		ListIterator<ModuleRequirement> iRequirements = requirements.listIterator(requirements.size());
+		Map<ModuleCapability, List<ModuleWire>> providedWireMap = provided.getOrDefault(revision, Collections.emptyMap());
+		NamespaceList<ModuleWire> requiredWires = required.getOrDefault(revision, NamespaceList.empty(WIRE));
+
+		NamespaceList.Builder<ModuleCapability> capabilities = Builder.create(CAPABILITY);
+		capabilities.addAll(revision.getModuleCapabilities(null));
+		NamespaceList.Builder<ModuleRequirement> requirements = Builder.create(REQUIREMENT);
+		requirements.addAll(revision.getModuleRequirements(null));
 
 		// if revision is a fragment remove payload requirements and capabilities
 		if ((BundleRevision.TYPE_FRAGMENT & revision.getTypes()) != 0) {
-			removePayloadContent(iCapabilities, iRequirements);
+			removePayloadContent(capabilities, requirements);
 		} else {
 			// add fragment capabilities and requirements
 			List<ModuleCapability> hostCapabilities = revision.getModuleCapabilities(HostNamespace.HOST_NAMESPACE);
 			ModuleCapability hostCapability = hostCapabilities.isEmpty() ? null : hostCapabilities.get(0);
 			if (hostCapability != null) {
-				addPayloadContent(providedWireMap.get(hostCapability), iCapabilities, iRequirements);
+				addPayloadContent(providedWireMap.get(hostCapability), capabilities, requirements);
 			}
 		}
 
-		removeNonEffectiveCapabilities(iCapabilities);
-		removeNonEffectiveRequirements(iRequirements, requiredWires);
-		Collection<String> substituted = removeSubstitutedCapabilities(iCapabilities, requiredWires);
+		removeNonEffectiveCapabilities(capabilities);
+		removeNonEffectiveRequirements(requirements, requiredWires);
+		Collection<String> substituted = removeSubstitutedCapabilities(capabilities, requiredWires);
 
-		List<ModuleWire> providedWires = new ArrayList<>();
+		NamespaceList.Builder<ModuleWire> providedWires = NamespaceList.Builder.create(WIRE);
 		addProvidedWires(providedWireMap, providedWires, capabilities);
 
 		InternalUtils.filterCapabilityPermissions(capabilities);
-		return new ModuleWiring(revision, new NamespaceList<>(capabilities, CAPABILITY),
-				new NamespaceList<>(requirements, REQUIREMENT), new NamespaceList<>(providedWires, WIRE),
-				new NamespaceList<>(requiredWires, WIRE), substituted);
+		return new ModuleWiring(revision, capabilities.build(), requirements.build(), providedWires.build(),
+				requiredWires, substituted);
+
 	}
 
-	private <E> List<E> sortByNamespaceCopy(List<E> elements, Function<E, String> getNamespace) {
-		List<E> result = new ArrayList<>(elements.size());
-		List<String> seen = new ArrayList<>();
-		for (E e : elements) {
-			String namespace = getNamespace.apply(e);
-			result.add(getInsertIndex(namespace, result, seen, getNamespace), e);
-		}
-		return result;
+	private static void removePayloadContent(NamespaceList.Builder<ModuleCapability> capabilities,
+			NamespaceList.Builder<ModuleRequirement> requirements) {
+
+		capabilities.removeNamespaceIf(namespace -> !NON_PAYLOAD_CAPABILITIES.contains(namespace));
+		requirements.removeNamespaceIf(namespace -> !NON_PAYLOAD_REQUIREMENTS.contains(namespace));
 	}
 
-	private <E> int getInsertIndex(String namespace, List<E> elements, List<String> seen,
-			Function<E, String> getNamespace) {
-		if (seen.contains(namespace)) {
-			for (int i = elements.size() - 1; i > -1; i--) {
-				if (namespace.equals(getNamespace.apply(elements.get(i)))) {
-					return i + 1;
-				}
-			}
-			throw new IllegalStateException();
-		}
-		seen.add(namespace);
-		return elements.size();
-	}
-
-	private static void removePayloadContent(ListIterator<ModuleCapability> iCapabilities, ListIterator<ModuleRequirement> iRequirements) {
-		rewind(iCapabilities);
-		while (iCapabilities.hasNext()) {
-			if (!NON_PAYLOAD_CAPABILITIES.contains(iCapabilities.next().getNamespace())) {
-				iCapabilities.remove();
-			}
-		}
-
-		rewind(iRequirements);
-		while (iRequirements.hasNext()) {
-			if (!NON_PAYLOAD_REQUIREMENTS.contains(iRequirements.next().getNamespace())) {
-				iRequirements.remove();
-			}
-		}
-	}
-
-	private static Collection<String> removeSubstitutedCapabilities(ListIterator<ModuleCapability> iCapabilities, List<ModuleWire> requiredWires) {
-		Collection<String> substituted = null;
-		for (ModuleWire moduleWire : requiredWires) {
-			if (!PackageNamespace.PACKAGE_NAMESPACE.equals(moduleWire.getCapability().getNamespace()))
-				continue;
+	private static Collection<String> removeSubstitutedCapabilities(NamespaceList.Builder<ModuleCapability> capabilities, NamespaceList<ModuleWire> requiredWires) {
+		Collection<String> substituted = new ArrayList<>();
+		for (ModuleWire moduleWire : requiredWires.getList(PackageNamespace.PACKAGE_NAMESPACE)) {
 			String packageName = (String) moduleWire.getCapability().getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE);
-			rewind(iCapabilities);
-			while (iCapabilities.hasNext()) {
-				ModuleCapability capability = iCapabilities.next();
-				if (PackageNamespace.PACKAGE_NAMESPACE.equals(capability.getNamespace())) {
-					if (packageName.equals(capability.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE))) {
-						// found a package capability with the same name as a package that got imported
-						// this indicates a substitution
-						iCapabilities.remove();
-						if (substituted == null) {
-							substituted = new ArrayList<>();
-						}
-						if (!substituted.contains(packageName)) {
-							substituted.add(packageName);
-						}
+			capabilities.removeElementsOfNamespaceIf(PackageNamespace.PACKAGE_NAMESPACE, capability -> {
+				if (packageName.equals(capability.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE))) {
+					// found a package capability with the same name as a package that got imported
+					// this indicates a substitution
+					if (!substituted.contains(packageName)) {
+						substituted.add(packageName);
 					}
+					return true;
 				}
-			}
+				return false;
+			});
 		}
-		return substituted == null ? Collections.<String> emptyList() : substituted;
+		return substituted.isEmpty() ? Collections.emptyList() : substituted;
 	}
 
-	private static void removeNonEffectiveRequirements(ListIterator<ModuleRequirement> iRequirements, List<ModuleWire> requiredWires) {
+	private static void removeNonEffectiveRequirements(NamespaceList.Builder<ModuleRequirement> requirements, NamespaceList<ModuleWire> requiredWires) {
 
 		Set<ModuleRequirement> wireRequirements = new HashSet<>();
-		for (ModuleWire mw : requiredWires) {
+		for (ModuleWire mw : requiredWires.getList(null)) {
 			wireRequirements.add(mw.getRequirement());
 		}
-
-		rewind(iRequirements);
-		while (iRequirements.hasNext()) {
-			ModuleRequirement requirement = iRequirements.next();
+		requirements.removeIf(requirement -> {
 			// check the effective directive;
 			Object effective = requirement.getDirectives().get(Namespace.REQUIREMENT_EFFECTIVE_DIRECTIVE);
 			if (effective != null && !Namespace.EFFECTIVE_RESOLVE.equals(effective)) {
-				iRequirements.remove();
-			} else {
-
-				if (!wireRequirements.contains(requirement)) {
-					if (!PackageNamespace.PACKAGE_NAMESPACE.equals(requirement.getNamespace())) {
-						iRequirements.remove();
-					} else {
-						Object resolution = requirement.getDirectives().get(Namespace.REQUIREMENT_RESOLUTION_DIRECTIVE);
-						if (!PackageNamespace.RESOLUTION_DYNAMIC.equals(resolution)) {
-							iRequirements.remove();
-						}
-					}
+				return true;
+			}
+			if (!wireRequirements.contains(requirement)) {
+				if (!PackageNamespace.PACKAGE_NAMESPACE.equals(requirement.getNamespace())) {
+					return true;
+				}
+				Object resolution = requirement.getDirectives().get(Namespace.REQUIREMENT_RESOLUTION_DIRECTIVE);
+				if (!PackageNamespace.RESOLUTION_DYNAMIC.equals(resolution)) {
+					return true;
 				}
 			}
-		}
+			return false;
+		});
 	}
 
-	void removeNonEffectiveCapabilities(ListIterator<ModuleCapability> iCapabilities) {
-		rewind(iCapabilities);
-		while (iCapabilities.hasNext()) {
-			Capability capability = iCapabilities.next();
+	void removeNonEffectiveCapabilities(Collection<ModuleCapability> capabilities) {
+		capabilities.removeIf(capability -> {
 			Object effective = capability.getDirectives().get(Namespace.CAPABILITY_EFFECTIVE_DIRECTIVE);
 			if (effective != null && !Namespace.EFFECTIVE_RESOLVE.equals(effective)) {
-				iCapabilities.remove();
 				if (DEBUG_PROVIDERS) {
 					Debug.println(new StringBuilder("RESOLVER: Capability filtered because it was not effective") //$NON-NLS-1$
 							.append(SEPARATOR).append(TAB) //
@@ -384,16 +329,18 @@
 							.append(capability.getResource()) //
 							.toString());
 				}
+				return true;
 			}
-		}
+			return false;
+		});
 	}
 
-	private static void addPayloadContent(List<ModuleWire> hostWires, ListIterator<ModuleCapability> iCapabilities, ListIterator<ModuleRequirement> iRequirements) {
+	private static void addPayloadContent(List<ModuleWire> hostWires, NamespaceList.Builder<ModuleCapability> capabilities, NamespaceList.Builder<ModuleRequirement> requirements) {
 		if (hostWires == null)
 			return;
 		for (ModuleWire hostWire : hostWires) {
+
 			// add fragment capabilities
-			String currentNamespace = null;
 			List<ModuleCapability> fragmentCapabilities = hostWire.getRequirer().getModuleCapabilities(null);
 			for (ModuleCapability fragmentCapability : fragmentCapabilities) {
 				if (NON_PAYLOAD_CAPABILITIES.contains(fragmentCapability.getNamespace())) {
@@ -403,23 +350,10 @@
 				if (effective != null && !Namespace.EFFECTIVE_RESOLVE.equals(effective)) {
 					continue; // don't include, not effective
 				}
-				if (!fragmentCapability.getNamespace().equals(currentNamespace)) {
-					currentNamespace = fragmentCapability.getNamespace();
-					fastForward(iCapabilities);
-					while (iCapabilities.hasPrevious()) {
-						if (iCapabilities.previous().getNamespace().equals(currentNamespace)) {
-							iCapabilities.next(); // put position after the last one
-							break;
-						}
-					}
-				}
-				if (!iCapabilities.hasPrevious()) {
-					fastForward(iCapabilities);
-				}
-				iCapabilities.add(fragmentCapability);
+				capabilities.add(fragmentCapability);
 			}
+
 			// add fragment requirements
-			currentNamespace = null;
 			List<ModuleRequirement> fragmentRequriements = hostWire.getRequirer().getModuleRequirements(null);
 			for (ModuleRequirement fragmentRequirement : fragmentRequriements) {
 				if (NON_PAYLOAD_REQUIREMENTS.contains(fragmentRequirement.getNamespace())) {
@@ -429,24 +363,12 @@
 				if (effective != null && !Namespace.EFFECTIVE_RESOLVE.equals(effective)) {
 					continue; // don't include, not effective
 				}
-				if (!fragmentRequirement.getNamespace().equals(currentNamespace)) {
-					currentNamespace = fragmentRequirement.getNamespace();
-					boolean isDynamic = isDynamic(fragmentRequirement);
-					fastForward(iRequirements);
-					while (iRequirements.hasPrevious()) {
-						ModuleRequirement previous = iRequirements.previous();
-						if (previous.getNamespace().equals(currentNamespace)) {
-							if (isDynamic || !isDynamic(previous)) {
-								iRequirements.next(); // put position after the last one
-								break;
-							}
-						}
-					}
+				if (!PackageNamespace.PACKAGE_NAMESPACE.equals(fragmentRequirement.getNamespace())
+						|| isDynamic(fragmentRequirement)) {
+					requirements.add(fragmentRequirement);
+				} else {
+					requirements.addAfterLastMatch(fragmentRequirement, r -> !isDynamic(r));
 				}
-				if (!iRequirements.hasPrevious()) {
-					fastForward(iRequirements);
-				}
-				iRequirements.add(fragmentRequirement);
 			}
 		}
 	}
@@ -455,61 +377,24 @@
 		return PackageNamespace.PACKAGE_NAMESPACE.equals(requirement.getNamespace()) && PackageNamespace.RESOLUTION_DYNAMIC.equals(requirement.getDirectives().get(Namespace.REQUIREMENT_RESOLUTION_DIRECTIVE));
 	}
 
-	private static void addProvidedWires(Map<ModuleCapability, List<ModuleWire>> toAdd, List<ModuleWire> existing, final List<ModuleCapability> orderedCapabilities) {
+	private static void addProvidedWires(Map<ModuleCapability, List<ModuleWire>> toAdd,
+			NamespaceList.Builder<ModuleWire> existing, NamespaceList.Builder<ModuleCapability> capabilities) {
 		if (toAdd == null)
 			return;
-		int originalSize = existing.size();
-		for (ModuleCapability capability : orderedCapabilities) {
+		for (ModuleCapability capability : capabilities) {
 			List<ModuleWire> newWires = toAdd.get(capability);
 			if (newWires != null) {
 				existing.addAll(newWires);
 			}
 		}
-		if (originalSize != 0) {
-			Collections.sort(existing, new Comparator<ModuleWire>() {
-				@Override
-				public int compare(ModuleWire w1, ModuleWire w2) {
-					int index1 = orderedCapabilities.indexOf(w1.getCapability());
-					int index2 = orderedCapabilities.indexOf(w2.getCapability());
-					return index1 - index2;
-				}
-			});
-		}
 	}
 
-	private static void addRequiredWires(List<ModuleWire> toAdd, List<ModuleWire> existing, final List<ModuleRequirement> orderedRequirements) {
-		if (toAdd == null)
-			return;
-		int originalSize = existing.size();
-		existing.addAll(toAdd);
-		if (originalSize != 0) {
-			Collections.sort(existing, new Comparator<ModuleWire>() {
-				@Override
-				public int compare(ModuleWire w1, ModuleWire w2) {
-					int index1 = orderedRequirements.indexOf(w1.getRequirement());
-					int index2 = orderedRequirements.indexOf(w2.getRequirement());
-					return index1 - index2;
-				}
-			});
-		}
-	}
-
-	private static void fastForward(ListIterator<?> listIterator) {
-		while (listIterator.hasNext())
-			listIterator.next();
-	}
-
-	static void rewind(ListIterator<?> listIterator) {
-		while (listIterator.hasPrevious())
-			listIterator.previous();
-	}
-
-	private static ModuleWiring createWiringDelta(ModuleRevision revision, ModuleWiring existingWiring, Map<ModuleCapability, List<ModuleWire>> providedWireMap, List<ModuleWire> requiredWires) {
+	private static ModuleWiring createWiringDelta(ModuleRevision revision, ModuleWiring existingWiring, Map<ModuleCapability, List<ModuleWire>> providedWireMap, NamespaceList<ModuleWire> requiredWires) {
 		// No null checks are done here on the wires since this is a copy.
-		List<ModuleWire> existingProvidedWires = existingWiring.getProvidedWires().copyList();
-		List<ModuleCapability> existingCapabilities = existingWiring.getCapabilities().copyList();
-		List<ModuleWire> existingRequiredWires = existingWiring.getRequiredWires().copyList();
-		List<ModuleRequirement> existingRequirements = existingWiring.getRequirements().copyList();
+		NamespaceList.Builder<ModuleWire> existingProvidedWires = existingWiring.getProvidedWires().createBuilder();
+		NamespaceList.Builder<ModuleCapability> existingCapabilities = existingWiring.getCapabilities().createBuilder();
+		NamespaceList.Builder<ModuleWire> existingRequiredWires = existingWiring.getRequiredWires().createBuilder();
+		NamespaceList.Builder<ModuleRequirement> existingRequirements = existingWiring.getRequirements().createBuilder();
 
 		// First, add newly resolved fragment capabilities and requirements
 		if (providedWireMap != null) {
@@ -517,7 +402,7 @@
 			ModuleCapability hostCapability = hostCapabilities.isEmpty() ? null : hostCapabilities.get(0);
 			List<ModuleWire> newHostWires = hostCapability == null ? null : providedWireMap.get(hostCapability);
 			if (newHostWires != null) {
-				addPayloadContent(newHostWires, existingCapabilities.listIterator(), existingRequirements.listIterator());
+				addPayloadContent(newHostWires, existingCapabilities, existingRequirements);
 			}
 		}
 
@@ -526,11 +411,13 @@
 
 		// Also need to include any new required wires that may have be added for fragment hosts
 		// Also will be needed for dynamic imports
-		addRequiredWires(requiredWires, existingRequiredWires, existingRequirements);
+		if (requiredWires != null) {
+			existingRequiredWires.addAll(requiredWires);
+		}
 
 		InternalUtils.filterCapabilityPermissions(existingCapabilities);
-		return new ModuleWiring(revision, existingCapabilities, existingRequirements, existingProvidedWires,
-				existingRequiredWires, existingWiring.getSubstitutedNames());
+		return new ModuleWiring(revision, existingCapabilities.build(), existingRequirements.build(),
+				existingProvidedWires.build(), existingRequiredWires.build(), existingWiring.getSubstitutedNames());
 	}
 
 	static boolean isSingleton(ModuleRevision revision) {
@@ -721,11 +608,10 @@
 		}
 
 		List<Capability> filterProviders(Requirement requirement, List<ModuleCapability> candidates, boolean filterResolvedHosts) {
-			ListIterator<ModuleCapability> iCandidates = candidates.listIterator();
-			filterDisabled(iCandidates);
-			removeNonEffectiveCapabilities(iCandidates);
-			removeSubstituted(iCandidates);
-			filterPermissions((BundleRequirement) requirement, iCandidates);
+			filterDisabled(candidates);
+			removeNonEffectiveCapabilities(candidates);
+			removeSubstituted(candidates);
+			filterPermissions((BundleRequirement) requirement, candidates);
 
 			List<ModuleCapability> filteredMatches = null;
 			if (DEBUG_PROVIDERS || DEBUG_HOOKS) {
@@ -800,9 +686,8 @@
 			}
 		}
 
-		private void filterPermissions(BundleRequirement requirement, ListIterator<ModuleCapability> iCandidates) {
-			rewind(iCandidates);
-			if (System.getSecurityManager() == null || !iCandidates.hasNext()) {
+		private void filterPermissions(BundleRequirement requirement, List<ModuleCapability> candidates) {
+			if (System.getSecurityManager() == null) {
 				return;
 			}
 
@@ -811,18 +696,16 @@
 				return;
 			}
 
-			candidates: while (iCandidates.hasNext()) {
-				ModuleCapability candidate = iCandidates.next();
+			candidates.removeIf(candidate -> {
 				// TODO this is a hack for when a bundle imports and exports the same package
 				if (PackageNamespace.PACKAGE_NAMESPACE.equals(requirement.getNamespace())) {
 					if (requirement.getRevision().equals(candidate.getRevision())) {
-						continue candidates;
+						return false;
 					}
 				}
 				Permission requirePermission = InternalUtils.getRequirePermission(candidate);
 				Permission providePermission = InternalUtils.getProvidePermission(candidate);
 				if (!requirement.getRevision().getBundle().hasPermission(requirePermission)) {
-					iCandidates.remove();
 					if (DEBUG_PROVIDERS) {
 						Debug.println(new StringBuilder("RESOLVER: Capability filtered because requirer did not have permission") //$NON-NLS-1$
 								.append(SEPARATOR).append(TAB) //
@@ -833,8 +716,8 @@
 								.append(candidate.getResource()) //
 								.toString());
 					}
+					return true;
 				} else if (!candidate.getRevision().getBundle().hasPermission(providePermission)) {
-					iCandidates.remove();
 					if (DEBUG_PROVIDERS) {
 						Debug.println(new StringBuilder("RESOLVER: Capability filtered because provider did not have permission") //$NON-NLS-1$
 								.append(SEPARATOR).append(TAB) //
@@ -845,16 +728,15 @@
 								.append(candidate.getResource()) //
 								.toString());
 					}
+					return true;
 				}
-			}
+				return false;
+			});
 		}
 
-		private void filterDisabled(ListIterator<ModuleCapability> iCandidates) {
-			rewind(iCandidates);
-			while (iCandidates.hasNext()) {
-				Capability capability = iCandidates.next();
+		private void filterDisabled(List<ModuleCapability> candidates) {
+			candidates.removeIf(capability -> {
 				if (disabled.contains(capability.getResource())) {
-					iCandidates.remove();
 					if (DEBUG_PROVIDERS) {
 						Debug.println(new StringBuilder("RESOLVER: Capability filtered because it was disabled") //$NON-NLS-1$
 								.append(SEPARATOR).append(TAB) //
@@ -865,17 +747,16 @@
 								.append(capability.getResource()) //
 								.toString());
 					}
+					return true;
 				}
-			}
+				return false;
+			});
 		}
 
-		private void removeSubstituted(ListIterator<ModuleCapability> iCapabilities) {
-			rewind(iCapabilities);
-			while (iCapabilities.hasNext()) {
-				ModuleCapability capability = iCapabilities.next();
+		private void removeSubstituted(List<ModuleCapability> capabilities) {
+			capabilities.removeIf(capability -> {
 				ModuleWiring wiring = wirings.get(capability.getRevision());
 				if (wiring != null && wiring.isSubtituted(capability)) {
-					iCapabilities.remove();
 					if (DEBUG_PROVIDERS) {
 						Debug.println(new StringBuilder("RESOLVER: Capability filtered because it was substituted") //$NON-NLS-1$
 								.append(SEPARATOR).append(TAB) //
@@ -886,8 +767,10 @@
 								.append(capability.getResource()) //
 								.toString());
 					}
+					return true;
 				}
-			}
+				return false;
+			});
 		}
 
 		@Override
@@ -942,7 +825,7 @@
 				Requirement fragmentRequirement = ModuleContainer.createRequirement(EquinoxFragmentNamespace.FRAGMENT_NAMESPACE, Collections.<String, String> singletonMap(Namespace.REQUIREMENT_FILTER_DIRECTIVE, matchFilter), Collections.<String, Object> emptyMap());
 				List<ModuleCapability> candidates = moduleDatabase.findCapabilities(fragmentRequirement);
 				// filter out disabled fragments and singletons
-				filterDisabled(candidates.listIterator());
+				filterDisabled(candidates);
 				for (ModuleCapability candidate : candidates) {
 					ModuleRequirement hostReq = candidate.getRevision().getModuleRequirements(HostNamespace.HOST_NAMESPACE).get(0);
 					for (ModuleCapability hostCap : hostCaps) {
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleWiring.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleWiring.java
index e63df7f..9e6777e 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleWiring.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/container/ModuleWiring.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2017 IBM Corporation and others.
+ * Copyright (c) 2012, 2021 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -13,10 +13,6 @@
  *******************************************************************************/
 package org.eclipse.osgi.container;
 
-import static org.eclipse.osgi.internal.container.NamespaceList.CAPABILITY;
-import static org.eclipse.osgi.internal.container.NamespaceList.REQUIREMENT;
-import static org.eclipse.osgi.internal.container.NamespaceList.WIRE;
-
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -26,7 +22,6 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Set;
 import java.util.concurrent.Callable;
 import java.util.concurrent.atomic.AtomicReference;
@@ -75,17 +70,6 @@
 	volatile boolean isValid = true;
 	private final AtomicReference<Set<String>> dynamicMissRef = new AtomicReference<>();
 
-	ModuleWiring(ModuleRevision revision, List<ModuleCapability> capabilities, List<ModuleRequirement> requirements,
-			List<ModuleWire> providedWires, List<ModuleWire> requiredWires, Collection<String> substitutedPkgNames) {
-		super();
-		this.revision = revision;
-		this.capabilities = new NamespaceList<>(capabilities, CAPABILITY);
-		this.requirements = new NamespaceList<>(requirements, REQUIREMENT);
-		this.providedWires = new NamespaceList<>(providedWires, WIRE);
-		this.requiredWires = new NamespaceList<>(requiredWires, WIRE);
-		this.substitutedPkgNames = substitutedPkgNames.isEmpty() ? Collections.<String> emptyList() : substitutedPkgNames;
-	}
-
 	ModuleWiring(ModuleRevision revision, NamespaceList<ModuleCapability> capabilities,
 			NamespaceList<ModuleRequirement> requirements, NamespaceList<ModuleWire> providedWires,
 			NamespaceList<ModuleWire> requiredWires, Collection<String> substitutedPkgNames) {
@@ -156,7 +140,7 @@
 		if (!isValid) {
 			return null;
 		}
-		List<ModuleRequirement> persistentRequriements = requirements.copyList();
+		List<ModuleRequirement> persistentRequriements = new ArrayList<>(requirements.getList(null));
 		for (Iterator<ModuleRequirement> iRequirements = persistentRequriements.iterator(); iRequirements.hasNext();) {
 			ModuleRequirement requirement = iRequirements.next();
 			if (PackageNamespace.PACKAGE_NAMESPACE.equals(requirement.getNamespace())) {
@@ -213,7 +197,7 @@
 		if (!isValid) {
 			return null;
 		}
-		List<ModuleWire> persistentWires = allWires.copyList();
+		List<ModuleWire> persistentWires = new ArrayList<>(allWires.getList(null));
 		for (Iterator<ModuleWire> iWires = persistentWires.iterator(); iWires.hasNext();) {
 			ModuleWire wire = iWires.next();
 			if (PackageNamespace.PACKAGE_NAMESPACE.equals(wire.getRequirement().getNamespace())) {
@@ -436,15 +420,9 @@
 		ModuleDatabase moduleDatabase = revision.getRevisions().getContainer().moduleDatabase;
 		moduleDatabase.writeLock();
 		try {
-			List<ModuleRequirement> updatedRequirements = requirements.copyList();
-			Entry<Integer, Integer> packageStartEnd = requirements
-					.getNamespaceIndex(PackageNamespace.PACKAGE_NAMESPACE);
-			if (packageStartEnd == null) {
-				updatedRequirements.addAll(newRequirements);
-			} else {
-				updatedRequirements.addAll(packageStartEnd.getValue(), newRequirements);
-			}
-			requirements = new NamespaceList<>(updatedRequirements, REQUIREMENT);
+			NamespaceList.Builder<ModuleRequirement> requirmentsBuilder = requirements.createBuilder();
+			requirmentsBuilder.addAll(newRequirements);
+			requirements = requirmentsBuilder.build();
 		} finally {
 			moduleDatabase.writeUnlock();
 		}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/container/NamespaceList.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/container/NamespaceList.java
index 74375c1..ce320de 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/container/NamespaceList.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/container/NamespaceList.java
@@ -10,154 +10,402 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Hannes Wellmann - Bug 573025: introduce and apply NamespaceList.Builder
  *******************************************************************************/
 package org.eclipse.osgi.internal.container;
 
-import java.util.AbstractMap;
+import java.util.AbstractCollection;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
+import java.util.NoSuchElementException;
 import java.util.function.Function;
+import java.util.function.Predicate;
 import org.eclipse.osgi.container.ModuleCapability;
 import org.eclipse.osgi.container.ModuleRequirement;
 import org.eclipse.osgi.container.ModuleWire;
 
 /**
- * An immutable list of elements for which each element has a name space. All
- * elements are kept in a single list to avoid creating a list for each name
- * space. The elements provided at construction are assumed to be ordered such
- * that all elements with the same name space are ordered together in one
- * continuous sequence An indexes list is used to keep track of the beginning
- * indexes for each namespace contained in the list. Assuming the number of
- * namespaces used by a bundle is relatively small compared to the overall
- * number of elements this makes it faster to find the first index for a
- * specific namespace without having to iterate over the complete list of
- * elements.
+ * An immutable list of elements for which each element has a namespace.
+ * <p>
+ * The elements are stored in a map where each key is a namespace and the
+ * associated value is the list of all elements with that namespace in this
+ * NamespaceList. Within one namespace the element's order is stable. Due to
+ * this internal structure access to the elements of off one or all namespace(s)
+ * has always constant runtime regardless of the number of namespaces present.
+ * <p>
  * 
- * @param <E> The element type which will have a name space associated with it
+ * @param <E> the type of elements in this list, which have a name-space
+ *            associated
  */
 public class NamespaceList<E> {
 
-	public final static Function<ModuleWire, String> WIRE = wire -> {
-		return wire.getCapability().getNamespace();
-	};
+	public final static Function<ModuleWire, String> WIRE = wire -> wire.getCapability().getNamespace();
 	public final static Function<ModuleCapability, String> CAPABILITY = ModuleCapability::getNamespace;
 	public final static Function<ModuleRequirement, String> REQUIREMENT = ModuleRequirement::getNamespace;
 
+	/**
+	 * Returns an empty NamespaceList.
+	 * <p>
+	 * The required argument is used to derive the type of elements and if a builder
+	 * is created from the returned list.
+	 * </p>
+	 * 
+	 * @param <E>          the type of elements in the NamespaceList
+	 * @param getNamespace the function to compute the namespace of an element
+	 * @return an empty NamespaceList
+	 */
+	public static <E> NamespaceList<E> empty(Function<E, String> getNamespace) {
+		return new NamespaceList<>(getNamespace, Collections.emptyMap(), Collections.emptyList());
+	}
+
 	private final List<E> elements;
 	private final Map<String, List<E>> namespaces;
+	private final Function<E, String> getNamespace;
+
+	NamespaceList(Function<E, String> getNamespace, Map<String, List<E>> namespaces, List<E> fullList) {
+		this.getNamespace = getNamespace;
+		this.namespaces = namespaces;
+		this.elements = fullList;
+	}
+
+	Map<String, List<E>> namespaces() {
+		return namespaces;
+	}
 
 	/**
-	 * Create a new name space list with the specified elements. The elements must
-	 * be sorted properly by name space.
+	 * Returns {@code true} if this NamespaceList contains no elements.
 	 * 
-	 * @param elements     the ordered list of elements
-	 * @param getNamespace a function that retrieves the namespace for each element
+	 * @return {@code true} if this list contains no elements
 	 */
-	public NamespaceList(List<E> elements, Function<E, String> getNamespace) {
-		List<E> unmodifiableElements = Collections.unmodifiableList(elements);
-
-		Map<String, List<E>> tmpNamespaces = new LinkedHashMap<>(5);
-		int size = unmodifiableElements.size();
-		int currentStart = 0;
-		String current = null;
-
-		boolean consolidated = false;
-		for (int i = 0; i < size; i++) {
-			String namespace = getNamespace.apply(unmodifiableElements.get(i));
-			if (current == null) {
-				current = namespace;
-			}
-			if (!current.equals(namespace)) {
-				int currentEnd = i;
-				consolidated |= addNamespaceList(tmpNamespaces, unmodifiableElements, current, currentStart,
-						currentEnd);
-				current = namespace;
-				currentStart = i;
-			}
-		}
-		if (size > 0) {
-			// add the last namespace
-			consolidated |= addNamespaceList(tmpNamespaces, unmodifiableElements, current, currentStart, size);
-		}
-		if (consolidated) {
-			List<E> tmpElements = new ArrayList<>(size);
-			for (Entry<String, List<E>> e : tmpNamespaces.entrySet()) {
-				tmpElements.addAll(e.getValue());
-			}
-			this.elements = Collections.unmodifiableList(tmpElements);
-		} else {
-			this.elements = unmodifiableElements;
-		}
-		this.namespaces = Collections.unmodifiableMap(tmpNamespaces);
-	}
-
-	private boolean addNamespaceList(Map<String, List<E>> tmpNamespaces, List<E> unmodifiableElements, String namespace,
-			int start, int end) {
-		List<E> namespaceList = unmodifiableElements.subList(start, end);
-		List<E> existing = tmpNamespaces.get(namespace);
-		boolean consolidated = false;
-		if (existing != null) {
-			// This should be an error, consolidate the lists
-			List<E> consolidateList = new ArrayList<>(existing);
-			consolidateList.addAll(namespaceList);
-			namespaceList = Collections.unmodifiableList(consolidateList);
-			consolidated = true;
-		}
-		tmpNamespaces.put(namespace, namespaceList);
-		return consolidated;
-	}
-
 	public boolean isEmpty() {
 		return elements.isEmpty();
 	}
 
 	/**
-	 * returns an unmodifiable list of elements with the specified name space. An
-	 * empty list is returned if there are no elements with the specified namespace.
-	 * A {@code null} namespace can be used to get all elements.
+	 * Returns an immutable list of elements with the specified namespace.
+	 * <p>
+	 * An empty list is returned if there are no elements with the specified
+	 * namespace. For the {@code null} namespace the elements of all namespaces are
+	 * returned as flat.
+	 * </p>
 	 * 
-	 * @param namespace the name space of the elements to return. May be
-	 *                  {@code null}
+	 * @param namespace the namespace of the elements to return. May be {@code null}
 	 * @return The list of elements found
 	 */
 	public List<E> getList(String namespace) {
 		if (namespace == null) {
 			return elements;
 		}
-
 		return namespaces.getOrDefault(namespace, Collections.emptyList());
 	}
 
 	/**
-	 * Returns the beginning index (inclusively) and ending index (exclusively) or
-	 * {@code null} if no element has the specified namespace
+	 * Returns a new {@link Builder NamespaceList.Builder} that contains all
+	 * elements of this NamespaceList.
+	 * <p>
+	 * The returned builder uses the same function to compute the namespace of an
+	 * element like this NamespaceList.
+	 * </p>
 	 * 
-	 * @param namespace the name space to find the indexes for
-	 * @return indexes found for the namespace or {@code null} if no elements exist
-	 *         with the name space.
+	 * @return a new builder containing all elements of this list
 	 */
-	public Entry<Integer, Integer> getNamespaceIndex(String namespace) {
-		int startIndex = 0;
-		for (Entry<String, ? extends List<E>> entry : namespaces.entrySet()) {
-			if (entry.getKey().equals(namespace)) {
-				int end = startIndex + entry.getValue().size();
-				return new AbstractMap.SimpleEntry<>(startIndex, end);
-			}
-			startIndex += entry.getValue().size();
-		}
-		return null;
+	public Builder<E> createBuilder() {
+		Builder<E> builder = Builder.create(getNamespace);
+		builder.addAll(this);
+		return builder;
 	}
 
 	/**
-	 * Returns a copy of all the elements in this list
+	 * A reusable builder to create {@link NamespaceList NamespaceLists}.
 	 * 
-	 * @return a copy of all the elements in this list
+	 * @param <E> the type of elements in this builder
+	 * @author Hannes Wellmann
 	 */
-	public List<E> copyList() {
-		return new ArrayList<>(elements);
+	public static class Builder<E> extends AbstractCollection<E> {
+
+		/**
+		 * Returns a new {@link Builder NamespaceList.Builder} that uses the specified
+		 * function to compute the namespace of its elements.
+		 * 
+		 * @param <E>          the type of elements in this builder
+		 * @param getNamespace the function to compute the namespace of an element
+		 * @return a new builder
+		 */
+		public static <E> Builder<E> create(Function<E, String> getNamespace) {
+			return new Builder<>(getNamespace, 3);
+		}
+
+		private final Function<E, String> getNamespace;
+		private LinkedHashMap<String, List<E>> namespaceElements;
+		private int size = 0;
+
+		private List<E> lastBuildElements;
+
+		private Builder(Function<E, String> getNamespace, int expectedNamespaces) {
+			this.getNamespace = getNamespace;
+			this.namespaceElements = new LinkedHashMap<>(expectedNamespaces * 4 / 3 + 1);
+		}
+
+		@Override
+		public int size() {
+			return size;
+		}
+
+		@Override
+		public Iterator<E> iterator() {
+			prepareModification();
+
+			final Iterator<? extends List<E>> outer = namespaceElements.values().iterator();
+			return new Iterator<E>() {
+				Iterator<E> inner = Collections.emptyIterator();
+				List<E> lastInnerList = null;
+
+				@Override
+				public boolean hasNext() {
+					while (!inner.hasNext() && outer.hasNext()) {
+						lastInnerList = outer.next();
+						inner = lastInnerList.iterator();
+					}
+					return inner.hasNext();
+				}
+
+				public E next() {
+					if (!hasNext()) {
+						throw new NoSuchElementException();
+					}
+					return inner.next();
+				}
+
+				@Override
+				@SuppressWarnings("synthetic-access")
+				public void remove() {
+					inner.remove();
+					Builder.this.size--;
+					if (lastInnerList.isEmpty()) {
+						outer.remove();
+					}
+				}
+			};
+		}
+
+		@Override
+		public void clear() {
+			namespaceElements = new LinkedHashMap<>(); // could have been build before, so map must not be cleared
+			lastBuildElements = null;
+			size = 0;
+		}
+
+		// --- addition ---
+
+		@Override
+		public boolean add(E e) {
+			prepareModification();
+
+			String namespace = getNamespace.apply(e);
+			getNamespaceList(namespace).add(e);
+			this.size++;
+			return true;
+		}
+
+		@Override
+		public boolean addAll(Collection<? extends E> c) {
+			if (c.isEmpty()) {
+				return false;
+			}
+			prepareModification();
+
+			String currentNamespace = null; // $NON-NLS-1$
+			List<E> currentNamespaceList = null;
+			for (E e : c) {
+				String namespace = getNamespace.apply(e);
+				// optimization if elements are already grouped per namespace
+				if (currentNamespace == null || !currentNamespace.equals(namespace)) {
+					currentNamespace = namespace;
+					currentNamespaceList = getNamespaceList(namespace);
+				}
+				currentNamespaceList.add(e);
+			}
+			this.size += c.size();
+			return true;
+		}
+
+		/**
+		 * Adds all elements in the specified NamespaceList to this builder.
+		 * 
+		 * @param list the NamespaceList containing elements to be added
+		 * @return true if any element was added to this builder
+		 */
+		public boolean addAll(NamespaceList<E> list) {
+			if (list.isEmpty()) {
+				return false;
+			}
+			prepareModification();
+
+			list.namespaces().forEach((n, es) -> {
+				getNamespaceList(n).addAll(es);
+				this.size += es.size();
+			});
+			return true;
+		}
+
+		private List<E> getNamespaceList(String namespace) {
+			return namespaceElements.computeIfAbsent(namespace, n -> new ArrayList<>());
+		}
+
+		public void addAfterLastMatch(E toAdd, Predicate<E> matcher) {
+			prepareModification();
+
+			String namespace = getNamespace.apply(toAdd);
+			List<E> namespaceList = getNamespaceList(namespace);
+			addAfterLastMatch(toAdd, namespaceList, matcher);
+			this.size++;
+		}
+
+		private void addAfterLastMatch(E e, List<E> list, Predicate<E> matcher) {
+			for (int i = list.size() - 1; 0 <= i; i--) {
+				if (matcher.test(list.get(i))) {
+					list.add(i + 1, e);
+					return;
+				}
+			}
+			list.add(0, e);
+		}
+
+		// --- removal ---
+
+		@Override
+		public boolean remove(Object o) {
+			@SuppressWarnings("unchecked")
+			E e = (E) o;
+			String namespace;
+			try {
+				namespace = getNamespace.apply(e);
+			} catch (ClassCastException ex) {
+				return false; // e does not seem to be of type E after all
+			}
+			prepareModification();
+
+			int sizeBefore = this.size;
+			removeNamespaceElement(namespace, e);
+			return this.size < sizeBefore;
+		}
+
+		private void removeNamespaceElement(String namespace, E element) {
+			namespaceElements.computeIfPresent(namespace, (n, es) -> {
+				if (es.remove(element)) {
+					this.size--;
+				}
+				return es.isEmpty() ? null : es;
+			});
+		}
+
+		@Override
+		public boolean removeAll(Collection<?> c) {
+			if (c.isEmpty()) {
+				return false;
+			}
+			prepareModification();
+
+			// this is more efficient than the super implementation
+			boolean removed = false;
+			for (Object e : c) {
+				removed |= remove(e);
+			}
+			return removed;
+		}
+
+		/**
+		 * Removes from this builder all elements of each namespace that satisfies the
+		 * specified predicate.
+		 * 
+		 * @param filter the predicate which returns true for a namespace to remove
+		 */
+		public void removeNamespaceIf(Predicate<String> filter) {
+			prepareModification();
+
+			namespaceElements.entrySet().removeIf(e -> {
+				if (filter.test(e.getKey())) {
+					this.size -= e.getValue().size();
+					return true;
+				}
+				return false;
+			});
+		}
+
+		@Override
+		public boolean removeIf(Predicate<? super E> filter) {
+			prepareModification();
+
+			int s = size;
+			namespaceElements.values().removeIf(es -> removeElementsIf(es, filter) == null);
+			return size < s;
+		}
+
+		/**
+		 * Removes from this builder those elements of the specified namespace that
+		 * satisfy the specified predicate.
+		 * 
+		 * @param namespace the namespace of
+		 * @param filter    the predicate which returns true for elements to remove
+		 */
+		public void removeElementsOfNamespaceIf(String namespace, Predicate<? super E> filter) {
+			prepareModification();
+
+			namespaceElements.computeIfPresent(namespace, (n, es) -> removeElementsIf(es, filter));
+		}
+
+		private List<E> removeElementsIf(List<E> list, Predicate<? super E> filter) {
+			int sizeBefore = list.size();
+			list.removeIf(filter);
+			this.size -= sizeBefore - list.size();
+			return list.isEmpty() ? null : list;
+		}
+
+		// --- build ---
+
+		/**
+		 * Returns an immutable {@link NamespaceList} containing a snapshot of the
+		 * current elements of this builder.
+		 * <p>
+		 * The content of this builder is not changed by this call and subsequent
+		 * modifications to this builder do not reflect into the returned list (the
+		 * returned list is not connected to this builder at all).
+		 * </p>
+		 * 
+		 * @return a {@link NamespaceList} reflecting the current state of this builder
+		 */
+		public NamespaceList<E> build() {
+			if (size == 0) {
+				return empty(getNamespace);
+			}
+			if (lastBuildElements == null) {
+				lastBuildElements = new ArrayList<>(size);
+				namespaceElements.values().forEach(lastBuildElements::addAll);
+				lastBuildElements = Collections.unmodifiableList(lastBuildElements);
+
+				int[] start = new int[] { 0 };
+				namespaceElements.replaceAll((n, es) -> {
+					int from = start[0];
+					int to = start[0] += es.size();
+					return lastBuildElements.subList(from, to);
+				});
+			}
+			return new NamespaceList<>(getNamespace, namespaceElements, lastBuildElements);
+		}
+
+		private void prepareModification() {
+			if (lastBuildElements != null) {
+				// this builder was build before. Create a copy of the Map and their
+				// namespace-lists for subsequent modification
+				namespaceElements = new LinkedHashMap<>(namespaceElements);
+				namespaceElements.replaceAll((n, es) -> new ArrayList<>(es));
+				lastBuildElements = null;
+			}
+		}
 	}
 }