Repository: logging-log4j2
Updated Branches:
  refs/heads/master 35eda8aa4 -> d676967bd


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d676967b/log4j-api/src/test/java/org/apache/logging/log4j/util/SortedStringArrayMapTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-api/src/test/java/org/apache/logging/log4j/util/SortedStringArrayMapTest.java
 
b/log4j-api/src/test/java/org/apache/logging/log4j/util/SortedStringArrayMapTest.java
new file mode 100644
index 0000000..00f98e6
--- /dev/null
+++ 
b/log4j-api/src/test/java/org/apache/logging/log4j/util/SortedStringArrayMapTest.java
@@ -0,0 +1,772 @@
+package org.apache.logging.log4j.util;/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.reflect.Field;
+import java.util.ConcurrentModificationException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests the SortedStringArrayMap class.
+ */
+public class SortedStringArrayMapTest {
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testConstructorDisallowsNegativeCapacity() throws Exception {
+        new SortedStringArrayMap(-1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testConstructorDisallowsZeroCapacity() throws Exception {
+        new SortedStringArrayMap(0);
+    }
+
+    @Test
+    public void testConstructorIgnoresNull() throws Exception {
+        assertEquals(0, new SortedStringArrayMap(null).size());
+    }
+
+    @Test
+    public void testToString() {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "avalue");
+        original.putValue("B", "Bvalue");
+        original.putValue("3", "3value");
+        assertEquals("{3=3value, B=Bvalue, a=avalue}", original.toString());
+    }
+
+    @Test
+    public void testSerialization() throws Exception {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "avalue");
+        original.putValue("B", "Bvalue");
+        original.putValue("3", "3value");
+
+        final byte[] binary = serialize(original);
+        final SortedStringArrayMap copy = deserialize(binary);
+        assertEquals(original, copy);
+    }
+
+    private byte[] serialize(final SortedStringArrayMap data) throws 
IOException {
+        final ByteArrayOutputStream arr = new ByteArrayOutputStream();
+        final ObjectOutputStream out = new ObjectOutputStream(arr);
+        out.writeObject(data);
+        return arr.toByteArray();
+    }
+
+    private SortedStringArrayMap deserialize(final byte[] binary) throws 
IOException, ClassNotFoundException {
+        final ByteArrayInputStream inArr = new ByteArrayInputStream(binary);
+        final ObjectInputStream in = new ObjectInputStream(inArr);
+        final SortedStringArrayMap result = (SortedStringArrayMap) 
in.readObject();
+        return result;
+    }
+
+    @Test
+    public void testPutAll() throws Exception {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "avalue");
+        original.putValue("B", "Bvalue");
+        original.putValue("3", "3value");
+
+        final SortedStringArrayMap other = new SortedStringArrayMap();
+        other.putAll(original);
+        assertEquals(original, other);
+
+        other.putValue("3", "otherValue");
+        assertNotEquals(original, other);
+
+        other.putValue("3", null);
+        assertNotEquals(original, other);
+
+        other.putValue("3", "3value");
+        assertEquals(original, other);
+    }
+
+    @Test
+    public void testEquals() {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "avalue");
+        original.putValue("B", "Bvalue");
+        original.putValue("3", "3value");
+        assertEquals(original, original); // equal to itself
+
+        final SortedStringArrayMap other = new SortedStringArrayMap();
+        other.putValue("a", "avalue");
+        assertNotEquals(original, other);
+
+        other.putValue("B", "Bvalue");
+        assertNotEquals(original, other);
+
+        other.putValue("3", "3value");
+        assertEquals(original, other);
+
+        other.putValue("3", "otherValue");
+        assertNotEquals(original, other);
+
+        other.putValue("3", null);
+        assertNotEquals(original, other);
+
+        other.putValue("3", "3value");
+        assertEquals(original, other);
+    }
+
+    @Test
+    public void testToMap() throws Exception {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "avalue");
+        original.putValue("B", "Bvalue");
+        original.putValue("3", "3value");
+
+        final Map<String, Object> expected = new HashMap<>();
+        expected.put("a", "avalue");
+        expected.put("B", "Bvalue");
+        expected.put("3", "3value");
+
+        assertEquals(expected, original.toMap());
+
+        try {
+            original.toMap().put("abc", "xyz");
+        } catch (final UnsupportedOperationException ex) {
+            fail("Expected map to be mutable, but " + ex);
+        }
+    }
+
+    @Test
+    public void testPutAll_KeepsExistingValues() {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "aaa");
+        original.putValue("b", "bbb");
+        original.putValue("c", "ccc");
+        assertEquals("size", 3, original.size());
+
+        // add empty context data
+        original.putAll(new SortedStringArrayMap());
+        assertEquals("size after put empty", 3, original.size());
+        assertEquals("aaa", original.getValue("a"));
+        assertEquals("bbb", original.getValue("b"));
+        assertEquals("ccc", original.getValue("c"));
+
+        final SortedStringArrayMap other = new SortedStringArrayMap();
+        other.putValue("1", "111");
+        other.putValue("2", "222");
+        other.putValue("3", "333");
+        original.putAll(other);
+
+        assertEquals("size after put other", 6, original.size());
+        assertEquals("aaa", original.getValue("a"));
+        assertEquals("bbb", original.getValue("b"));
+        assertEquals("ccc", original.getValue("c"));
+        assertEquals("111", original.getValue("1"));
+        assertEquals("222", original.getValue("2"));
+        assertEquals("333", original.getValue("3"));
+    }
+
+    @Test
+    public void testPutAllSelfDoesNotModify() {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "aaa");
+        original.putValue("b", "bbb");
+        original.putValue("c", "ccc");
+        assertEquals("size", 3, original.size());
+
+        // putAll with self
+        original.putAll(original);
+        assertEquals("size after put empty", 3, original.size());
+        assertEquals("aaa", original.getValue("a"));
+        assertEquals("bbb", original.getValue("b"));
+        assertEquals("ccc", original.getValue("c"));
+    }
+
+    @Test(expected = ConcurrentModificationException.class)
+    public void testConcurrentModificationBiConsumerPut() {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "aaa");
+        original.forEach(new BiConsumer<String, Object>() {
+            @Override
+            public void accept(final String s, final Object o) {
+                original.putValue("c", "other");
+            }
+        });
+    }
+
+    @Test(expected = ConcurrentModificationException.class)
+    public void testConcurrentModificationBiConsumerPutValue() {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "aaa");
+        original.forEach(new BiConsumer<String, Object>() {
+            @Override
+            public void accept(final String s, final Object o) {
+                original.putValue("c", "other");
+            }
+        });
+    }
+
+    @Test(expected = ConcurrentModificationException.class)
+    public void testConcurrentModificationBiConsumerRemove() {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "aaa");
+        original.forEach(new BiConsumer<String, Object>() {
+            @Override
+            public void accept(final String s, final Object o) {
+                original.remove("a");
+            }
+        });
+    }
+
+    @Test(expected = ConcurrentModificationException.class)
+    public void testConcurrentModificationBiConsumerClear() {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "aaa");
+        original.forEach(new BiConsumer<String, Object>() {
+            @Override
+            public void accept(final String s, final Object o) {
+                original.clear();
+            }
+        });
+    }
+
+    @Test(expected = ConcurrentModificationException.class)
+    public void testConcurrentModificationTriConsumerPut() {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "aaa");
+        original.forEach(new TriConsumer<String, Object, Object>() {
+            @Override
+            public void accept(final String s, final Object o, final Object 
o2) {
+                original.putValue("c", "other");
+            }
+        }, null);
+    }
+
+    @Test(expected = ConcurrentModificationException.class)
+    public void testConcurrentModificationTriConsumerPutValue() {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "aaa");
+        original.forEach(new TriConsumer<String, Object, Object>() {
+            @Override
+            public void accept(final String s, final Object o, final Object 
o2) {
+                original.putValue("c", "other");
+            }
+        }, null);
+    }
+
+    @Test(expected = ConcurrentModificationException.class)
+    public void testConcurrentModificationTriConsumerRemove() {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "aaa");
+        original.forEach(new TriConsumer<String, Object, Object>() {
+            @Override
+            public void accept(final String s, final Object o, final Object 
o2) {
+                original.remove("a");
+            }
+        }, null);
+    }
+
+    @Test(expected = ConcurrentModificationException.class)
+    public void testConcurrentModificationTriConsumerClear() {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "aaa");
+        original.forEach(new TriConsumer<String, Object, Object>() {
+            @Override
+            public void accept(final String s, final Object o, final Object 
o2) {
+                original.clear();
+            }
+        }, null);
+    }
+
+    @Test
+    public void testInitiallyNotFrozen() {
+        assertFalse(new SortedStringArrayMap().isFrozen());
+    }
+
+    @Test
+    public void testIsFrozenAfterCallingFreeze() {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        assertFalse("before freeze", original.isFrozen());
+        original.freeze();
+        assertTrue("after freeze", original.isFrozen());
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testFreezeProhibitsPutValue() {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.freeze();
+        original.putValue("a", "aaa");
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testFreezeProhibitsRemove() {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("b", "bbb");
+        original.freeze();
+        original.remove("b"); // existing key: modifies the collection
+    }
+
+    @Test
+    public void testFreezeAllowsRemoveOfNonExistingKey() {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("b", "bbb");
+        original.freeze();
+        original.remove("a"); // no actual modification
+    }
+
+    @Test
+    public void testFreezeAllowsRemoveIfEmpty() {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.freeze();
+        original.remove("a"); // no exception
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testFreezeProhibitsClear() {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "aaa");
+        original.freeze();
+        original.clear();
+    }
+
+    @Test
+    public void testFreezeAllowsClearIfEmpty() {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.freeze();
+        original.clear();
+    }
+
+    @Test
+    public void testPutInsertsInAlphabeticOrder() throws Exception {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "avalue");
+        original.putValue("B", "Bvalue");
+        original.putValue("3", "3value");
+        original.putValue("c", "cvalue");
+        original.putValue("d", "dvalue");
+
+        assertEquals("avalue", original.getValue("a"));
+        assertEquals("avalue", original.getValueAt(2));
+
+        assertEquals("Bvalue", original.getValue("B"));
+        assertEquals("Bvalue", original.getValueAt(1));
+
+        assertEquals("3value", original.getValue("3"));
+        assertEquals("3value", original.getValueAt(0));
+
+        assertEquals("cvalue", original.getValue("c"));
+        assertEquals("cvalue", original.getValueAt(3));
+
+        assertEquals("dvalue", original.getValue("d"));
+        assertEquals("dvalue", original.getValueAt(4));
+    }
+
+    @Test
+    public void testPutValueInsertsInAlphabeticOrder() throws Exception {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "avalue");
+        original.putValue("B", "Bvalue");
+        original.putValue("3", "3value");
+        original.putValue("c", "cvalue");
+        original.putValue("d", "dvalue");
+
+        assertEquals("avalue", original.getValue("a"));
+        assertEquals("avalue", original.getValueAt(2));
+
+        assertEquals("Bvalue", original.getValue("B"));
+        assertEquals("Bvalue", original.getValueAt(1));
+
+        assertEquals("3value", original.getValue("3"));
+        assertEquals("3value", original.getValueAt(0));
+
+        assertEquals("cvalue", original.getValue("c"));
+        assertEquals("cvalue", original.getValueAt(3));
+
+        assertEquals("dvalue", original.getValue("d"));
+        assertEquals("dvalue", original.getValueAt(4));
+    }
+
+    @Test
+    public void testNullKeysAllowed() {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "avalue");
+        original.putValue("B", "Bvalue");
+        original.putValue("3", "3value");
+        original.putValue("c", "cvalue");
+        original.putValue("d", "dvalue");
+        assertEquals(5, original.size());
+        assertEquals("{3=3value, B=Bvalue, a=avalue, c=cvalue, d=dvalue}", 
original.toString());
+
+        original.putValue(null, "nullvalue");
+        assertEquals(6, original.size());
+        assertEquals("{null=nullvalue, 3=3value, B=Bvalue, a=avalue, c=cvalue, 
d=dvalue}", original.toString());
+
+        original.putValue(null, "otherNullvalue");
+        assertEquals("{null=otherNullvalue, 3=3value, B=Bvalue, a=avalue, 
c=cvalue, d=dvalue}", original.toString());
+        assertEquals(6, original.size());
+
+        original.putValue(null, "nullvalue");
+        assertEquals(6, original.size());
+        assertEquals("{null=nullvalue, 3=3value, B=Bvalue, a=avalue, c=cvalue, 
d=dvalue}", original.toString());
+
+        original.putValue(null, "abc");
+        assertEquals(6, original.size());
+        assertEquals("{null=abc, 3=3value, B=Bvalue, a=avalue, c=cvalue, 
d=dvalue}", original.toString());
+    }
+
+    @Test
+    public void testNullKeysCopiedToAsMap() {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "avalue");
+        original.putValue("B", "Bvalue");
+        original.putValue("3", "3value");
+        original.putValue("c", "cvalue");
+        original.putValue("d", "dvalue");
+        assertEquals(5, original.size());
+
+        final HashMap<String, String> expected = new HashMap<>();
+        expected.put("a", "avalue");
+        expected.put("B", "Bvalue");
+        expected.put("3", "3value");
+        expected.put("c", "cvalue");
+        expected.put("d", "dvalue");
+        assertEquals("initial", expected, original.toMap());
+
+        original.putValue(null, "nullvalue");
+        expected.put(null, "nullvalue");
+        assertEquals(6, original.size());
+        assertEquals("with null key", expected, original.toMap());
+
+        original.putValue(null, "otherNullvalue");
+        expected.put(null, "otherNullvalue");
+        assertEquals(6, original.size());
+        assertEquals("with null key value2", expected, original.toMap());
+
+        original.putValue(null, "nullvalue");
+        expected.put(null, "nullvalue");
+        assertEquals(6, original.size());
+        assertEquals("with null key value1 again", expected, original.toMap());
+
+        original.putValue(null, "abc");
+        expected.put(null, "abc");
+        assertEquals(6, original.size());
+        assertEquals("with null key value3", expected, original.toMap());
+    }
+
+    @Test
+    public void testRemove() {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "avalue");
+        assertEquals(1, original.size());
+        assertEquals("avalue", original.getValue("a"));
+
+        original.remove("a");
+        assertEquals(0, original.size());
+        assertNull("no a val", original.getValue("a"));
+
+        original.remove("B");
+        assertEquals(0, original.size());
+        assertNull("no B val", original.getValue("B"));
+    }
+
+    @Test
+    public void testRemoveNullsOutRemovedSlot() throws Exception {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "avalue");
+        original.putValue("b", "bvalue");
+        original.putValue("c", "cvalue");
+        original.putValue("d", "dvalue");
+        original.remove("a");
+        original.remove("b");
+        original.remove("c");
+        original.remove("d");
+        assertNull(original.getValueAt(0));
+
+        // ensure slots in the values array are nulled out
+        final Field f = SortedStringArrayMap.class.getDeclaredField("values");
+        f.setAccessible(true);
+        final Object[] values = (Object[]) f.get(original);
+        for (int i = 0; i < values.length; i++) {
+            assertNull(values[i]);
+        }
+    }
+
+    @Test
+    public void testRemoveWhenFull() throws Exception {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "avalue");
+        original.putValue("b", "bvalue");
+        original.putValue("c", "cvalue");
+        original.putValue("d", "dvalue"); // default capacity = 4
+        original.remove("d");
+    }
+
+    @Test
+    public void testNullValuesArePreserved() {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "avalue");
+        assertEquals(1, original.size());
+        assertEquals("avalue", original.getValue("a"));
+
+        original.putValue("a", null);
+        assertEquals(1, original.size());
+        assertNull("no a val", original.getValue("a"));
+
+        original.putValue("B", null);
+        assertEquals(2, original.size());
+        assertNull("no B val", original.getValue("B"));
+    }
+
+    @Test
+    public void testGet() throws Exception {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "avalue");
+        original.putValue("B", "Bvalue");
+        original.putValue("3", "3value");
+
+        assertEquals("avalue", original.getValue("a"));
+        assertEquals("Bvalue", original.getValue("B"));
+        assertEquals("3value", original.getValue("3"));
+
+        original.putValue("0", "0value");
+        assertEquals("0value", original.getValue("0"));
+        assertEquals("3value", original.getValue("3"));
+        assertEquals("Bvalue", original.getValue("B"));
+        assertEquals("avalue", original.getValue("a"));
+    }
+
+    @Test
+    public void testGetValue_GetValueAt() throws Exception {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "avalue");
+        original.putValue("B", "Bvalue");
+        original.putValue("3", "3value");
+
+        assertEquals("avalue", original.getValue("a"));
+        assertEquals("avalue", original.getValueAt(2));
+
+        assertEquals("Bvalue", original.getValue("B"));
+        assertEquals("Bvalue", original.getValueAt(1));
+
+        assertEquals("3value", original.getValue("3"));
+        assertEquals("3value", original.getValueAt(0));
+
+        original.putValue("0", "0value");
+        assertEquals("0value", original.getValue("0"));
+        assertEquals("0value", original.getValueAt(0));
+        assertEquals("3value", original.getValue("3"));
+        assertEquals("3value", original.getValueAt(1));
+        assertEquals("Bvalue", original.getValue("B"));
+        assertEquals("Bvalue", original.getValueAt(2));
+        assertEquals("avalue", original.getValue("a"));
+        assertEquals("avalue", original.getValueAt(3));
+    }
+
+    @Test
+    public void testClear() throws Exception {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "avalue");
+        original.putValue("B", "Bvalue");
+        original.putValue("3", "3value");
+        assertEquals(3, original.size());
+
+        original.clear();
+        assertEquals(0, original.size());
+
+        // ensure slots in the values array are nulled out
+        final Field f = SortedStringArrayMap.class.getDeclaredField("values");
+        f.setAccessible(true);
+        final Object[] values = (Object[]) f.get(original);
+        for (int i = 0; i < values.length; i++) {
+            assertNull(values[i]);
+        }
+    }
+
+    @Test
+    public void testIndexOfKey() throws Exception {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "avalue");
+        assertEquals(0, original.indexOfKey("a"));
+
+        original.putValue("B", "Bvalue");
+        assertEquals(1, original.indexOfKey("a"));
+        assertEquals(0, original.indexOfKey("B"));
+
+        original.putValue("3", "3value");
+        assertEquals(2, original.indexOfKey("a"));
+        assertEquals(1, original.indexOfKey("B"));
+        assertEquals(0, original.indexOfKey("3"));
+
+        original.putValue("A", "AAA");
+        assertEquals(3, original.indexOfKey("a"));
+        assertEquals(2, original.indexOfKey("B"));
+        assertEquals(1, original.indexOfKey("A"));
+        assertEquals(0, original.indexOfKey("3"));
+
+        original.putValue("C", "CCC");
+        assertEquals(4, original.indexOfKey("a"));
+        assertEquals(3, original.indexOfKey("C"));
+        assertEquals(2, original.indexOfKey("B"));
+        assertEquals(1, original.indexOfKey("A"));
+        assertEquals(0, original.indexOfKey("3"));
+
+        original.putValue("2", "222");
+        assertEquals(5, original.indexOfKey("a"));
+        assertEquals(4, original.indexOfKey("C"));
+        assertEquals(3, original.indexOfKey("B"));
+        assertEquals(2, original.indexOfKey("A"));
+        assertEquals(1, original.indexOfKey("3"));
+        assertEquals(0, original.indexOfKey("2"));
+    }
+
+    @Test
+    public void testContainsKey() throws Exception {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        assertFalse("a", original.containsKey("a"));
+        assertFalse("B", original.containsKey("B"));
+        assertFalse("3", original.containsKey("3"));
+        assertFalse("A", original.containsKey("A"));
+
+        original.putValue("a", "avalue");
+        assertTrue("a", original.containsKey("a"));
+        assertFalse("B", original.containsKey("B"));
+        assertFalse("3", original.containsKey("3"));
+        assertFalse("A", original.containsKey("A"));
+
+        original.putValue("B", "Bvalue");
+        assertTrue("a", original.containsKey("a"));
+        assertTrue("B", original.containsKey("B"));
+        assertFalse("3", original.containsKey("3"));
+        assertFalse("A", original.containsKey("A"));
+
+        original.putValue("3", "3value");
+        assertTrue("a", original.containsKey("a"));
+        assertTrue("B", original.containsKey("B"));
+        assertTrue("3", original.containsKey("3"));
+        assertFalse("A", original.containsKey("A"));
+
+        original.putValue("A", "AAA");
+        assertTrue("a", original.containsKey("a"));
+        assertTrue("B", original.containsKey("B"));
+        assertTrue("3", original.containsKey("3"));
+        assertTrue("A", original.containsKey("A"));
+    }
+
+    @Test
+    public void testGetValueAt() throws Exception {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "avalue");
+        assertEquals("a", original.getKeyAt(0));
+        assertEquals("avalue", original.getValueAt(0));
+
+        original.putValue("B", "Bvalue");
+        assertEquals("B", original.getKeyAt(0));
+        assertEquals("Bvalue", original.getValueAt(0));
+        assertEquals("a", original.getKeyAt(1));
+        assertEquals("avalue", original.getValueAt(1));
+
+        original.putValue("3", "3value");
+        assertEquals("3", original.getKeyAt(0));
+        assertEquals("3value", original.getValueAt(0));
+        assertEquals("B", original.getKeyAt(1));
+        assertEquals("Bvalue", original.getValueAt(1));
+        assertEquals("a", original.getKeyAt(2));
+        assertEquals("avalue", original.getValueAt(2));
+    }
+
+    @Test
+    public void testSizeAndIsEmpty() throws Exception {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        assertEquals(0, original.size());
+        assertTrue("initial", original.isEmpty());
+
+        original.putValue("a", "avalue");
+        assertEquals(1, original.size());
+        assertFalse("size=" + original.size(), original.isEmpty());
+
+        original.putValue("B", "Bvalue");
+        assertEquals(2, original.size());
+        assertFalse("size=" + original.size(), original.isEmpty());
+
+        original.putValue("3", "3value");
+        assertEquals(3, original.size());
+        assertFalse("size=" + original.size(), original.isEmpty());
+
+        original.remove("B");
+        assertEquals(2, original.size());
+        assertFalse("size=" + original.size(), original.isEmpty());
+
+        original.remove("3");
+        assertEquals(1, original.size());
+        assertFalse("size=" + original.size(), original.isEmpty());
+
+        original.remove("a");
+        assertEquals(0, original.size());
+        assertTrue("size=" + original.size(), original.isEmpty());
+    }
+
+    @Test
+    public void testForEachBiConsumer() throws Exception {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "avalue");
+        original.putValue("B", "Bvalue");
+        original.putValue("3", "3value");
+
+        original.forEach(new BiConsumer<String, String>() {
+            int count = 0;
+            @Override
+            public void accept(final String key, final String value) {
+                assertEquals("key", key, original.getKeyAt(count));
+                assertEquals("val", value, original.getValueAt(count));
+                count++;
+                assertTrue("count should not exceed size but was " + count, 
count <= original.size());
+            }
+        });
+    }
+
+    static class State {
+        SortedStringArrayMap data;
+        int count;
+    }
+    static TriConsumer<String, String, State> COUNTER = new 
TriConsumer<String, String, State>() {
+        @Override
+        public void accept(final String key, final String value, final State 
state) {
+            assertEquals("key", key, state.data.getKeyAt(state.count));
+            assertEquals("val", value, state.data.getValueAt(state.count));
+            state.count++;
+            assertTrue("count should not exceed size but was " + state.count,
+                    state.count <= state.data.size());
+        }
+    };
+
+    @Test
+    public void testForEachTriConsumer() throws Exception {
+        final SortedStringArrayMap original = new SortedStringArrayMap();
+        original.putValue("a", "avalue");
+        original.putValue("B", "Bvalue");
+        original.putValue("3", "3value");
+
+        final State state = new State();
+        state.data = original;
+        original.forEach(COUNTER, state);
+        assertEquals(state.count, original.size());
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d676967b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataFactory.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataFactory.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataFactory.java
index 8f65b61..19dd179 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataFactory.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/ContextDataFactory.java
@@ -16,19 +16,19 @@
  */
 package org.apache.logging.log4j.core.impl;
 
-import org.apache.logging.log4j.util.ArrayContextData;
+import org.apache.logging.log4j.util.SortedStringArrayMap;
 import org.apache.logging.log4j.spi.MutableContextData;
 import org.apache.logging.log4j.util.PropertiesUtil;
 
 /**
  * Factory for creating MutableContextData instances.
  * <p>
- * By default returns {@code ArrayContextData} objects. Can be configured by 
setting system property
+ * By default returns {@code SortedStringArrayMap} objects. Can be configured 
by setting system property
  * {@code "log4j2.ContextData"} to the fully qualified class name of a class 
implementing the
  * {@code MutableContextData} interface. The class must have a public default 
constructor.
  * </p>
  *
- * @see ArrayContextData
+ * @see SortedStringArrayMap
  * @since 2.7
  */
 public class ContextDataFactory {
@@ -36,11 +36,11 @@ public class ContextDataFactory {
     @SuppressWarnings("unchecked")
     public static MutableContextData createContextData() {
         final String CLASS = 
PropertiesUtil.getProperties().getStringProperty("log4j2.ContextData",
-                ArrayContextData.class.getName());
+                SortedStringArrayMap.class.getName());
         try {
             return (MutableContextData) Class.forName(CLASS).newInstance();
         } catch (final Exception any) {
-            return new ArrayContextData();
+            return new SortedStringArrayMap();
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d676967b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataAttributeConverterTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataAttributeConverterTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataAttributeConverterTest.java
index 32dc8e5..dc9e3db 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataAttributeConverterTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataAttributeConverterTest.java
@@ -16,7 +16,7 @@
  */
 package org.apache.logging.log4j.core.appender.db.jpa.converter;
 
-import org.apache.logging.log4j.util.ArrayContextData;
+import org.apache.logging.log4j.util.SortedStringArrayMap;
 import org.apache.logging.log4j.spi.MutableContextData;
 import org.junit.After;
 import org.junit.Before;
@@ -39,7 +39,7 @@ public class ContextDataAttributeConverterTest {
 
     @Test
     public void testConvertToDatabaseColumn01() {
-        final MutableContextData map = new ArrayContextData();
+        final MutableContextData map = new SortedStringArrayMap();
         map.putValue("test1", "another1");
         map.putValue("key2", "value2");
 
@@ -49,7 +49,7 @@ public class ContextDataAttributeConverterTest {
 
     @Test
     public void testConvertToDatabaseColumn02() {
-        final MutableContextData map = new ArrayContextData();
+        final MutableContextData map = new SortedStringArrayMap();
         map.putValue("someKey", "coolValue");
         map.putValue("anotherKey", "testValue");
         map.putValue("myKey", "yourValue");

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d676967b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataJsonAttributeConverterTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataJsonAttributeConverterTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataJsonAttributeConverterTest.java
index 18cf06a..7582111 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataJsonAttributeConverterTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/db/jpa/converter/ContextDataJsonAttributeConverterTest.java
@@ -17,7 +17,7 @@
 package org.apache.logging.log4j.core.appender.db.jpa.converter;
 
 import org.apache.logging.log4j.spi.ContextData;
-import org.apache.logging.log4j.util.ArrayContextData;
+import org.apache.logging.log4j.util.SortedStringArrayMap;
 import org.apache.logging.log4j.spi.MutableContextData;
 import org.junit.After;
 import org.junit.Before;
@@ -40,7 +40,7 @@ public class ContextDataJsonAttributeConverterTest {
 
     @Test
     public void testConvert01() {
-        final MutableContextData map = new ArrayContextData();
+        final MutableContextData map = new SortedStringArrayMap();
         map.putValue("test1", "another1");
         map.putValue("key2", "value2");
 
@@ -56,7 +56,7 @@ public class ContextDataJsonAttributeConverterTest {
 
     @Test
     public void testConvert02() {
-        final MutableContextData map = new ArrayContextData();
+        final MutableContextData map = new SortedStringArrayMap();
         map.putValue("someKey", "coolValue");
         map.putValue("anotherKey", "testValue");
         map.putValue("myKey", "yourValue");

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d676967b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java
index 058b997..0cf48c7 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/Log4jLogEventTest.java
@@ -40,7 +40,7 @@ import org.apache.logging.log4j.core.util.DummyNanoClock;
 import org.apache.logging.log4j.message.Message;
 import org.apache.logging.log4j.message.ObjectMessage;
 import org.apache.logging.log4j.message.SimpleMessage;
-import org.apache.logging.log4j.util.ArrayContextData;
+import org.apache.logging.log4j.util.SortedStringArrayMap;
 import org.apache.logging.log4j.spi.MutableContextData;
 import org.apache.logging.log4j.util.Strings;
 import org.junit.AfterClass;
@@ -285,7 +285,7 @@ public class Log4jLogEventTest {
 
     @Test
     public void testBuilderCorrectlyCopiesAllEventAttributesInclContextData() {
-        final MutableContextData contextData = new ArrayContextData();
+        final MutableContextData contextData = new SortedStringArrayMap();
         contextData.putValue("A", "B");
         final ContextStack contextStack = ThreadContext.getImmutableStack();
         final Exception exception = new Exception("test");
@@ -334,7 +334,7 @@ public class Log4jLogEventTest {
 
     @Test
     public void testBuilderCorrectlyCopiesMutableLogEvent() throws Exception {
-        final MutableContextData contextData = new ArrayContextData();
+        final MutableContextData contextData = new SortedStringArrayMap();
         contextData.putValue("A", "B");
         final ContextStack contextStack = ThreadContext.getImmutableStack();
         final Exception exception = new Exception("test");

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d676967b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java
----------------------------------------------------------------------
diff --git 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java
 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java
index a97dc6a..04593b8 100644
--- 
a/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java
+++ 
b/log4j-core/src/test/java/org/apache/logging/log4j/core/impl/MutableLogEventTest.java
@@ -29,7 +29,7 @@ import org.apache.logging.log4j.MarkerManager;
 import org.apache.logging.log4j.ThreadContext;
 import org.apache.logging.log4j.message.ParameterizedMessage;
 import org.apache.logging.log4j.message.SimpleMessage;
-import org.apache.logging.log4j.util.ArrayContextData;
+import org.apache.logging.log4j.util.SortedStringArrayMap;
 import org.apache.logging.log4j.spi.MutableContextData;
 import org.apache.logging.log4j.spi.MutableThreadContextStack;
 import org.junit.Test;
@@ -44,7 +44,7 @@ public class MutableLogEventTest {
     private static final ThreadContext.ContextStack STACK = new 
MutableThreadContextStack(Arrays.asList("abc", "xyz"));
 
     private static MutableContextData createContextData() {
-        final MutableContextData result = new ArrayContextData();
+        final MutableContextData result = new SortedStringArrayMap();
         result.putValue("a", "1");
         result.putValue("b", "2");
         return result;

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d676967b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ArrayContextDataVsHashMapBenchmark.java
----------------------------------------------------------------------
diff --git 
a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ArrayContextDataVsHashMapBenchmark.java
 
b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ArrayContextDataVsHashMapBenchmark.java
deleted file mode 100644
index 63f43f2..0000000
--- 
a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ArrayContextDataVsHashMapBenchmark.java
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.perf.jmh;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Random;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.logging.log4j.perf.nogc.OpenHashMapContextData;
-import org.apache.logging.log4j.util.ArrayContextData;
-import org.apache.logging.log4j.util.BiConsumer;
-import org.apache.logging.log4j.util.TriConsumer;
-import org.openjdk.jmh.annotations.Benchmark;
-import org.openjdk.jmh.annotations.BenchmarkMode;
-import org.openjdk.jmh.annotations.Fork;
-import org.openjdk.jmh.annotations.Measurement;
-import org.openjdk.jmh.annotations.Mode;
-import org.openjdk.jmh.annotations.OutputTimeUnit;
-import org.openjdk.jmh.annotations.Param;
-import org.openjdk.jmh.annotations.Scope;
-import org.openjdk.jmh.annotations.Setup;
-import org.openjdk.jmh.annotations.State;
-import org.openjdk.jmh.annotations.Warmup;
-
-/**
- * Compares performance of ArrayContextDataVsHashMapBenchmark.
- */
-// ============================== HOW TO RUN THIS TEST: 
====================================
-// (Quick build: mvn -DskipTests=true clean package -pl log4j-perf -am )
-//
-// single thread:
-// java -jar log4j-perf/target/benchmarks.jar 
".*ArrayContextDataVsHashMapBenchmark.*" -f 1 -wi 10 -i 20 -tu ns -bm sample
-//
-//
-// Usage help:
-// java -jar log4j-perf/target/benchmarks.jar -help
-//
-
-@BenchmarkMode(Mode.AverageTime)
-@OutputTimeUnit(TimeUnit.NANOSECONDS)
-@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
-@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
-@Fork(1)
-@State(Scope.Benchmark)
-public class ArrayContextDataVsHashMapBenchmark {
-
-    //@Param({"1", "2", "5", "11", "23", "47", "95", "191", "383"})
-    //@Param({"1", "5", "50", "500"})
-    @Param({ "5",  "500"})
-    public int count;
-
-    //@Param({"5", "50"})
-    @Param({"20"})
-    public int length;
-
-    private String[] keys;
-    private static Object value = new Object();
-    private HashMap<String, Object> map;
-    private ArrayContextData contextData;
-    private OpenHashMapContextData<String, Object> hashContextData;
-
-    private HashMap<String, Object> populatedMap;
-    private ArrayContextData populatedContextData;
-    private OpenHashMapContextData<String, Object> populatedHashContextData;
-
-    @Setup
-    public void setup() {
-        hashContextData = new OpenHashMapContextData<>();
-        contextData = new ArrayContextData();
-        map = new HashMap<>();
-
-        keys = new String[count];
-        final Random r = new Random();
-        for (int j = 0; j < keys.length; j++) {
-            final char[] str = new char[length];
-            for (int i = 0; i < str.length; i++) {
-                str[i] = (char) r.nextInt();
-            }
-            keys[j] = new String(str);
-        }
-
-        populatedMap = new HashMap<>();
-        for (int i = 0; i < count; i++) {
-            populatedMap.put(keys[i], value);
-        }
-        populatedContextData = new ArrayContextData();
-        for (int i = 0; i < count; i++) {
-            populatedContextData.putValue(keys[i], value);
-        }
-        populatedHashContextData = new OpenHashMapContextData<>();
-        for (int i = 0; i < count; i++) {
-            populatedHashContextData.putValue(keys[i], value);
-        }
-    }
-
-    @Benchmark
-    public ArrayContextData putAllArrayContextData() {
-        contextData.clear();
-        contextData.putAll(populatedContextData);
-        return contextData;
-    }
-
-    @Benchmark
-    public OpenHashMapContextData<String, Object> putAllHashContextData() {
-        hashContextData.clear();
-        hashContextData.putAll(populatedHashContextData);
-        return hashContextData;
-    }
-
-    @Benchmark
-    public Map putAllMap() {
-        map.clear();
-        map.putAll(populatedMap);
-        return map;
-    }
-
-    @Benchmark
-    public ArrayContextData cloneArrayContextData() {
-        return new ArrayContextData(populatedContextData);
-    }
-
-    @Benchmark
-    public OpenHashMapContextData<String, Object> cloneHashContextData() {
-        return new OpenHashMapContextData<>(populatedHashContextData);
-    }
-
-    @Benchmark
-    public Map cloneMap() {
-        return new HashMap(populatedMap);
-    }
-
-    static TriConsumer<String, Object, int[]> COUNTER = new 
TriConsumer<String, Object, int[]>() {
-        @Override
-        public void accept(final String s, final Object o, final int[] result) 
{
-            result[0] += s.hashCode() + o.hashCode();
-        }
-    };
-
-    @Benchmark
-    public int iterateArrayContextDataTriConsumer() {
-        final int[] result = {0};
-
-        populatedContextData.forEach(COUNTER, result);
-        return result[0];
-    }
-
-    @Benchmark
-    public int iterateHashContextDataTriConsumer() {
-        final int[] result = {0};
-
-        populatedHashContextData.forEach(COUNTER, result);
-        return result[0];
-    }
-
-    @Benchmark
-    public int iterateArrayContextDataBiConsumer() {
-        final int[] result = {0};
-
-        populatedContextData.forEach(new BiConsumer<String, Object>() {
-            @Override
-            public void accept(final String s, final Object o) {
-                result[0] += s.hashCode() + o.hashCode();
-            }
-        });
-        return result[0];
-    }
-
-    @Benchmark
-    public int iterateHashContextDataBiConsumer() {
-        final int[] result = {0};
-
-        populatedHashContextData.forEach(new BiConsumer<String, Object>() {
-            @Override
-            public void accept(final String s, final Object o) {
-                result[0] += s.hashCode() + o.hashCode();
-            }
-        });
-        return result[0];
-    }
-
-    @Benchmark
-    public int iterateMap() {
-        final int[] result = {0};
-
-        for (final Map.Entry<String, Object> entry : populatedMap.entrySet()) {
-            result[0] += entry.getKey().hashCode() + 
entry.getValue().hashCode();
-        }
-        return result[0];
-    }
-
-    @Benchmark
-    public Object getValueArrayContextData() {
-        return populatedContextData.getValue(keys[count - 1]);
-    }
-
-    @Benchmark
-    public Object getValueHashContextData() {
-        return populatedHashContextData.getValue(keys[count - 1]);
-    }
-
-    @Benchmark
-    public Object getValueMap() {
-        return populatedMap.get(keys[count - 1]);
-    }
-
-    @Benchmark
-    public int putArrayContextData() {
-        populatedContextData.putValue("someKey", "someValue");
-        return populatedContextData.size();
-    }
-
-    @Benchmark
-    public int putHashContextData() {
-        hashContextData.put("someKey", "someValue");
-        return hashContextData.size();
-    }
-
-    @Benchmark
-    public int putMap() {
-        map.put("someKey", "someValue");
-        return map.size();
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d676967b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/SortedArrayVsHashMapBenchmark.java
----------------------------------------------------------------------
diff --git 
a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/SortedArrayVsHashMapBenchmark.java
 
b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/SortedArrayVsHashMapBenchmark.java
new file mode 100644
index 0000000..f2692de
--- /dev/null
+++ 
b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/SortedArrayVsHashMapBenchmark.java
@@ -0,0 +1,239 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.perf.jmh;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.logging.log4j.perf.nogc.OpenHashMapContextData;
+import org.apache.logging.log4j.util.SortedStringArrayMap;
+import org.apache.logging.log4j.util.BiConsumer;
+import org.apache.logging.log4j.util.TriConsumer;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+
+/**
+ * Compares performance of SortedStringArrayMap vs. OpenHashMap vs. JDK 
HashMap.
+ */
+// ============================== HOW TO RUN THIS TEST: 
====================================
+// (Quick build: mvn -DskipTests=true clean package -pl log4j-perf -am )
+//
+// single thread:
+// java -jar log4j-perf/target/benchmarks.jar 
".*SortedArrayVsHashMapBenchmark.*" -f 1 -wi 10 -i 20 -tu ns -bm sample
+//
+//
+// Usage help:
+// java -jar log4j-perf/target/benchmarks.jar -help
+//
+
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
+@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
+@Fork(1)
+@State(Scope.Benchmark)
+public class SortedArrayVsHashMapBenchmark {
+
+    //@Param({"1", "2", "5", "11", "23", "47", "95", "191", "383"})
+    //@Param({"1", "5", "50", "500"})
+    @Param({ "5",  "500"})
+    public int count;
+
+    //@Param({"5", "50"})
+    @Param({"20"})
+    public int length;
+
+    private String[] keys;
+    private static Object value = new Object();
+    private HashMap<String, Object> map;
+    private SortedStringArrayMap sortedStringArrayMap;
+    private OpenHashMapContextData<String, Object> openHashMapContextData;
+
+    private HashMap<String, Object> populatedMap;
+    private SortedStringArrayMap populatedSortedStringArrayMap;
+    private OpenHashMapContextData<String, Object> 
populatedOpenHashContextData;
+
+    @Setup
+    public void setup() {
+        openHashMapContextData = new OpenHashMapContextData<>();
+        sortedStringArrayMap = new SortedStringArrayMap();
+        map = new HashMap<>();
+
+        keys = new String[count];
+        final Random r = new Random();
+        for (int j = 0; j < keys.length; j++) {
+            final char[] str = new char[length];
+            for (int i = 0; i < str.length; i++) {
+                str[i] = (char) r.nextInt();
+            }
+            keys[j] = new String(str);
+        }
+
+        populatedMap = new HashMap<>();
+        for (int i = 0; i < count; i++) {
+            populatedMap.put(keys[i], value);
+        }
+        populatedSortedStringArrayMap = new SortedStringArrayMap();
+        for (int i = 0; i < count; i++) {
+            populatedSortedStringArrayMap.putValue(keys[i], value);
+        }
+        populatedOpenHashContextData = new OpenHashMapContextData<>();
+        for (int i = 0; i < count; i++) {
+            populatedOpenHashContextData.putValue(keys[i], value);
+        }
+    }
+
+    @Benchmark
+    public SortedStringArrayMap putAllArrayContextData() {
+        sortedStringArrayMap.clear();
+        sortedStringArrayMap.putAll(populatedSortedStringArrayMap);
+        return sortedStringArrayMap;
+    }
+
+    @Benchmark
+    public OpenHashMapContextData<String, Object> putAllHashContextData() {
+        openHashMapContextData.clear();
+        openHashMapContextData.putAll(populatedOpenHashContextData);
+        return openHashMapContextData;
+    }
+
+    @Benchmark
+    public Map putAllMap() {
+        map.clear();
+        map.putAll(populatedMap);
+        return map;
+    }
+
+    @Benchmark
+    public SortedStringArrayMap cloneArrayContextData() {
+        return new SortedStringArrayMap(populatedSortedStringArrayMap);
+    }
+
+    @Benchmark
+    public OpenHashMapContextData<String, Object> cloneHashContextData() {
+        return new OpenHashMapContextData<>(populatedOpenHashContextData);
+    }
+
+    @Benchmark
+    public Map cloneMap() {
+        return new HashMap(populatedMap);
+    }
+
+    static TriConsumer<String, Object, int[]> COUNTER = new 
TriConsumer<String, Object, int[]>() {
+        @Override
+        public void accept(final String s, final Object o, final int[] result) 
{
+            result[0] += s.hashCode() + o.hashCode();
+        }
+    };
+
+    @Benchmark
+    public int iterateArrayContextDataTriConsumer() {
+        final int[] result = {0};
+
+        populatedSortedStringArrayMap.forEach(COUNTER, result);
+        return result[0];
+    }
+
+    @Benchmark
+    public int iterateHashContextDataTriConsumer() {
+        final int[] result = {0};
+
+        populatedOpenHashContextData.forEach(COUNTER, result);
+        return result[0];
+    }
+
+    @Benchmark
+    public int iterateArrayContextDataBiConsumer() {
+        final int[] result = {0};
+
+        populatedSortedStringArrayMap.forEach(new BiConsumer<String, Object>() 
{
+            @Override
+            public void accept(final String s, final Object o) {
+                result[0] += s.hashCode() + o.hashCode();
+            }
+        });
+        return result[0];
+    }
+
+    @Benchmark
+    public int iterateHashContextDataBiConsumer() {
+        final int[] result = {0};
+
+        populatedOpenHashContextData.forEach(new BiConsumer<String, Object>() {
+            @Override
+            public void accept(final String s, final Object o) {
+                result[0] += s.hashCode() + o.hashCode();
+            }
+        });
+        return result[0];
+    }
+
+    @Benchmark
+    public int iterateMap() {
+        final int[] result = {0};
+
+        for (final Map.Entry<String, Object> entry : populatedMap.entrySet()) {
+            result[0] += entry.getKey().hashCode() + 
entry.getValue().hashCode();
+        }
+        return result[0];
+    }
+
+    @Benchmark
+    public Object getValueArrayContextData() {
+        return populatedSortedStringArrayMap.getValue(keys[count - 1]);
+    }
+
+    @Benchmark
+    public Object getValueHashContextData() {
+        return populatedOpenHashContextData.getValue(keys[count - 1]);
+    }
+
+    @Benchmark
+    public Object getValueMap() {
+        return populatedMap.get(keys[count - 1]);
+    }
+
+    @Benchmark
+    public int putArrayContextData() {
+        populatedSortedStringArrayMap.putValue("someKey", "someValue");
+        return populatedSortedStringArrayMap.size();
+    }
+
+    @Benchmark
+    public int putHashContextData() {
+        openHashMapContextData.put("someKey", "someValue");
+        return openHashMapContextData.size();
+    }
+
+    @Benchmark
+    public int putMap() {
+        map.put("someKey", "someValue");
+        return map.size();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d676967b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ThreadContextBenchmark.java
----------------------------------------------------------------------
diff --git 
a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ThreadContextBenchmark.java
 
b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ThreadContextBenchmark.java
index f2a950d..e7d37ee 100644
--- 
a/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ThreadContextBenchmark.java
+++ 
b/log4j-perf/src/main/java/org/apache/logging/log4j/perf/jmh/ThreadContextBenchmark.java
@@ -36,7 +36,7 @@ import org.apache.logging.log4j.spi.DefaultThreadContextMap;
 import org.apache.logging.log4j.spi.GarbageFreeOpenHashMapThreadContextMap;
 import org.apache.logging.log4j.spi.MutableContextData;
 import org.apache.logging.log4j.spi.ThreadContextMap;
-import org.apache.logging.log4j.util.ArrayContextData;
+import org.apache.logging.log4j.util.SortedStringArrayMap;
 import org.openjdk.jmh.annotations.Benchmark;
 import org.openjdk.jmh.annotations.BenchmarkMode;
 import org.openjdk.jmh.annotations.Fork;
@@ -110,7 +110,7 @@ public class ThreadContextBenchmark {
         System.out.println(threadContextMapAlias + ": Injector = " + injector);
 
         reusableContextData = threadContextMapAlias.contains("Array")
-                ? new ArrayContextData()
+                ? new SortedStringArrayMap()
                 : new OpenHashMapContextData<>();
 
         keys = new String[count];

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/d676967b/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/MDCContextMap.java
----------------------------------------------------------------------
diff --git 
a/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/MDCContextMap.java 
b/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/MDCContextMap.java
index 0593e3c..a96537f 100644
--- a/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/MDCContextMap.java
+++ b/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/MDCContextMap.java
@@ -21,7 +21,7 @@ import java.util.Map.Entry;
 
 import org.apache.logging.log4j.spi.MutableContextData;
 import org.apache.logging.log4j.spi.ThreadContextMap2;
-import org.apache.logging.log4j.util.ArrayContextData;
+import org.apache.logging.log4j.util.SortedStringArrayMap;
 import org.slf4j.MDC;
 
 /**
@@ -29,7 +29,7 @@ import org.slf4j.MDC;
  */
 public class MDCContextMap implements ThreadContextMap2 {
 
-    private static final MutableContextData EMPTY_CONTEXT_DATA = new 
ArrayContextData();
+    private static final MutableContextData EMPTY_CONTEXT_DATA = new 
SortedStringArrayMap();
     static {
         EMPTY_CONTEXT_DATA.freeze();
     }
@@ -89,7 +89,7 @@ public class MDCContextMap implements ThreadContextMap2 {
         if (copy.isEmpty()) {
             return EMPTY_CONTEXT_DATA;
         }
-        final MutableContextData result = new ArrayContextData();
+        final MutableContextData result = new SortedStringArrayMap();
         for (Entry<String, String> entry : copy.entrySet()) {
             result.putValue(entry.getKey(), entry.getValue());
         }

Reply via email to