LOG4J2-1447 LOG4J2-1349 renamed MutableContextData to StringMap, ContextData to 
ReadOnlyStringMap


Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/800db9c7
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/800db9c7
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/800db9c7

Branch: refs/heads/master
Commit: 800db9c7adb74cc89d031211902ff1569601e79c
Parents: b4fb664
Author: rpopma <rpo...@apache.org>
Authored: Fri Sep 23 01:58:26 2016 +0900
Committer: rpopma <rpo...@apache.org>
Committed: Fri Sep 23 01:58:26 2016 +0900

----------------------------------------------------------------------
 .../CopyOnWriteSortedArrayThreadContextMap.java |  66 +-
 .../log4j/spi/DefaultThreadContextMap.java      |   4 +-
 .../GarbageFreeSortedArrayThreadContextMap.java |  66 +-
 .../logging/log4j/spi/ThreadContextMap2.java    |   8 +-
 .../apache/logging/log4j/util/BiConsumer.java   |   2 +-
 .../apache/logging/log4j/util/ContextData.java  | 106 ---
 .../logging/log4j/util/MutableContextData.java  |  98 ---
 .../logging/log4j/util/ReadOnlyStringMap.java   | 106 +++
 .../log4j/util/SortedArrayStringMap.java        | 477 ++++++++++++
 .../log4j/util/SortedStringArrayMap.java        | 477 ------------
 .../apache/logging/log4j/util/StringMap.java    |  98 +++
 .../apache/logging/log4j/util/TriConsumer.java  |   2 +-
 .../log4j/util/SortedArrayStringMapTest.java    | 772 +++++++++++++++++++
 .../log4j/util/SortedStringArrayMapTest.java    | 772 -------------------
 .../logging/log4j/core/AbstractLogEvent.java    |   4 +-
 .../logging/log4j/core/ContextDataInjector.java |  30 +-
 .../org/apache/logging/log4j/core/LogEvent.java |   8 +-
 .../db/jpa/AbstractLogEventWrapperEntity.java   |   8 +-
 .../ContextDataAttributeConverter.java          |  10 +-
 .../ContextDataJsonAttributeConverter.java      |  14 +-
 .../logging/log4j/core/async/AsyncLogger.java   |   8 +-
 .../log4j/core/async/RingBufferLogEvent.java    |  12 +-
 .../async/RingBufferLogEventTranslator.java     |   4 +-
 .../core/filter/DynamicThresholdFilter.java     |   6 +-
 .../logging/log4j/core/filter/MapFilter.java    |   4 +-
 .../core/filter/ThreadContextMapFilter.java     |   6 +-
 .../log4j/core/impl/ContextDataFactory.java     |  24 +-
 .../core/impl/ContextDataInjectorFactory.java   |   6 +-
 .../logging/log4j/core/impl/Log4jLogEvent.java  |  38 +-
 .../log4j/core/impl/MutableLogEvent.java        |  12 +-
 .../core/impl/ReusableLogEventFactory.java      |   4 +-
 .../core/impl/ThreadContextDataInjector.java    |  80 +-
 .../ContextDataAsEntryListDeserializer.java     |   8 +-
 .../ContextDataAsEntryListSerializer.java       |   6 +-
 .../core/jackson/ContextDataDeserializer.java   |   8 +-
 .../core/jackson/ContextDataSerializer.java     |   6 +-
 .../log4j/core/jackson/LogEventJsonMixIn.java   |   4 +-
 .../jackson/LogEventWithContextListMixIn.java   |   4 +-
 .../log4j/core/lookup/ContextMapLookup.java     |   4 +-
 .../log4j/core/pattern/MdcPatternConverter.java |   8 +-
 .../core/appender/db/jpa/TestBaseEntity.java    |   4 +-
 .../ContextDataAttributeConverterTest.java      |   8 +-
 .../ContextDataJsonAttributeConverterTest.java  |  14 +-
 .../core/async/RingBufferLogEventTest.java      |  16 +-
 .../log4j/core/impl/Log4jLogEventTest.java      |   8 +-
 .../log4j/core/impl/MutableLogEventTest.java    |  10 +-
 .../log4j/flume/appender/FlumeEvent.java        |   4 +-
 .../perf/jmh/SortedArrayVsHashMapBenchmark.java |  18 +-
 .../log4j/perf/jmh/ThreadContextBenchmark.java  |  12 +-
 .../log4j/perf/nogc/OpenHashMapContextData.java |  24 +-
 .../CopyOnWriteOpenHashMapThreadContextMap.java |   8 +-
 .../GarbageFreeOpenHashMapThreadContextMap.java |   8 +-
 .../org/apache/logging/slf4j/MDCContextMap.java |  10 +-
 53 files changed, 1762 insertions(+), 1762 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/800db9c7/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContextMap.java
----------------------------------------------------------------------
diff --git 
a/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContextMap.java
 
b/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContextMap.java
index 7326202..ca2e22d 100644
--- 
a/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContextMap.java
+++ 
b/log4j-api/src/main/java/org/apache/logging/log4j/spi/CopyOnWriteSortedArrayThreadContextMap.java
@@ -20,13 +20,13 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
-import org.apache.logging.log4j.util.ContextData;
-import org.apache.logging.log4j.util.MutableContextData;
+import org.apache.logging.log4j.util.ReadOnlyStringMap;
+import org.apache.logging.log4j.util.SortedArrayStringMap;
+import org.apache.logging.log4j.util.StringMap;
 import org.apache.logging.log4j.util.PropertiesUtil;
-import org.apache.logging.log4j.util.SortedStringArrayMap;
 
 /**
- * {@code SortedStringArrayMap}-based implementation of the {@code 
ThreadContextMap} interface that creates a copy of
+ * {@code SortedArrayStringMap}-based implementation of the {@code 
ThreadContextMap} interface that creates a copy of
  * the data structure on every modification. Any particular instance of the 
data structure is a snapshot of the
  * ThreadContext at some point in time and can safely be passed off to other 
threads.  Since it is
  * expected that the Map will be passed to many more log events than the 
number of keys it contains the performance
@@ -52,12 +52,12 @@ class CopyOnWriteSortedArrayThreadContextMap implements 
ThreadContextMap2, CopyO
      */
     protected static final String PROPERTY_NAME_INITIAL_CAPACITY = 
"log4j2.ThreadContext.initial.capacity";
 
-    private static final MutableContextData EMPTY_CONTEXT_DATA = new 
SortedStringArrayMap();
+    private static final StringMap EMPTY_CONTEXT_DATA = new 
SortedArrayStringMap();
     static {
         EMPTY_CONTEXT_DATA.freeze();
     }
 
-    private final ThreadLocal<MutableContextData> localMap;
+    private final ThreadLocal<StringMap> localMap;
 
     public CopyOnWriteSortedArrayThreadContextMap() {
         this.localMap = createThreadLocalMap();
@@ -65,14 +65,14 @@ class CopyOnWriteSortedArrayThreadContextMap implements 
ThreadContextMap2, CopyO
 
     // LOG4J2-479: by default, use a plain ThreadLocal, only use 
InheritableThreadLocal if configured.
     // (This method is package protected for JUnit tests.)
-    private ThreadLocal<MutableContextData> createThreadLocalMap() {
+    private ThreadLocal<StringMap> createThreadLocalMap() {
         final PropertiesUtil managerProps = PropertiesUtil.getProperties();
         final boolean inheritable = 
managerProps.getBooleanProperty(INHERITABLE_MAP);
         if (inheritable) {
-            return new InheritableThreadLocal<MutableContextData>() {
+            return new InheritableThreadLocal<StringMap>() {
                 @Override
-                protected MutableContextData childValue(final 
MutableContextData parentValue) {
-                    return parentValue != null ? 
createMutableContextData(parentValue) : null;
+                protected StringMap childValue(final StringMap parentValue) {
+                    return parentValue != null ? createStringMap(parentValue) 
: null;
                 }
             };
         }
@@ -81,34 +81,34 @@ class CopyOnWriteSortedArrayThreadContextMap implements 
ThreadContextMap2, CopyO
     }
 
     /**
-     * Returns an implementation of the {@code MutableContextData} used to 
back this thread context map.
+     * Returns an implementation of the {@code StringMap} used to back this 
thread context map.
      * <p>
      * Subclasses may override.
      * </p>
-     * @return an implementation of the {@code MutableContextData} used to 
back this thread context map
+     * @return an implementation of the {@code StringMap} used to back this 
thread context map
      */
-    protected MutableContextData createMutableContextData() {
-        return new 
SortedStringArrayMap(PropertiesUtil.getProperties().getIntegerProperty(
+    protected StringMap createStringMap() {
+        return new 
SortedArrayStringMap(PropertiesUtil.getProperties().getIntegerProperty(
                 PROPERTY_NAME_INITIAL_CAPACITY, DEFAULT_INITIAL_CAPACITY));
     }
 
     /**
-     * Returns an implementation of the {@code MutableContextData} used to 
back this thread context map, pre-populated
+     * Returns an implementation of the {@code StringMap} used to back this 
thread context map, pre-populated
      * with the contents of the specified context data.
      * <p>
      * Subclasses may override.
      * </p>
      * @param original the key-value pairs to initialize the returned context 
data with
-     * @return an implementation of the {@code MutableContextData} used to 
back this thread context map
+     * @return an implementation of the {@code StringMap} used to back this 
thread context map
      */
-    protected MutableContextData createMutableContextData(final ContextData 
original) {
-        return new SortedStringArrayMap(original);
+    protected StringMap createStringMap(final ReadOnlyStringMap original) {
+        return new SortedArrayStringMap(original);
     }
 
     @Override
     public void put(final String key, final String value) {
-        MutableContextData map = localMap.get();
-        map = map == null ? createMutableContextData() : 
createMutableContextData(map);
+        StringMap map = localMap.get();
+        map = map == null ? createStringMap() : createStringMap(map);
         map.putValue(key, value);
         map.freeze();
         localMap.set(map);
@@ -119,8 +119,8 @@ class CopyOnWriteSortedArrayThreadContextMap implements 
ThreadContextMap2, CopyO
         if (values == null || values.isEmpty()) {
             return;
         }
-        MutableContextData map = localMap.get();
-        map = map == null ? createMutableContextData() : 
createMutableContextData(map);
+        StringMap map = localMap.get();
+        map = map == null ? createStringMap() : createStringMap(map);
         for (final Map.Entry<String, String> entry : values.entrySet()) {
             map.putValue(entry.getKey(), entry.getValue());
         }
@@ -130,15 +130,15 @@ class CopyOnWriteSortedArrayThreadContextMap implements 
ThreadContextMap2, CopyO
 
     @Override
     public String get(final String key) {
-        final MutableContextData map = localMap.get();
+        final StringMap map = localMap.get();
         return map == null ? null : (String) map.getValue(key);
     }
 
     @Override
     public void remove(final String key) {
-        final MutableContextData map = localMap.get();
+        final StringMap map = localMap.get();
         if (map != null) {
-            final MutableContextData copy = createMutableContextData(map);
+            final StringMap copy = createStringMap(map);
             copy.remove(key);
             copy.freeze();
             localMap.set(copy);
@@ -152,13 +152,13 @@ class CopyOnWriteSortedArrayThreadContextMap implements 
ThreadContextMap2, CopyO
 
     @Override
     public boolean containsKey(final String key) {
-        final MutableContextData map = localMap.get();
+        final StringMap map = localMap.get();
         return map != null && map.containsKey(key);
     }
 
     @Override
     public Map<String, String> getCopy() {
-        final MutableContextData map = localMap.get();
+        final StringMap map = localMap.get();
         return map == null ? new HashMap<String, String>() : map.toMap();
     }
 
@@ -166,26 +166,26 @@ class CopyOnWriteSortedArrayThreadContextMap implements 
ThreadContextMap2, CopyO
      * {@inheritDoc}
      */
     @Override
-    public MutableContextData getReadOnlyContextData() {
-        final MutableContextData map = localMap.get();
+    public StringMap getReadOnlyContextData() {
+        final StringMap map = localMap.get();
         return map == null ? EMPTY_CONTEXT_DATA : map;
     }
 
     @Override
     public Map<String, String> getImmutableMapOrNull() {
-        final MutableContextData map = localMap.get();
+        final StringMap map = localMap.get();
         return map == null ? null : Collections.unmodifiableMap(map.toMap());
     }
 
     @Override
     public boolean isEmpty() {
-        final MutableContextData map = localMap.get();
+        final StringMap map = localMap.get();
         return map == null || map.size() == 0;
     }
 
     @Override
     public String toString() {
-        final MutableContextData map = localMap.get();
+        final StringMap map = localMap.get();
         return map == null ? "{}" : map.toString();
     }
 
@@ -193,7 +193,7 @@ class CopyOnWriteSortedArrayThreadContextMap implements 
ThreadContextMap2, CopyO
     public int hashCode() {
         final int prime = 31;
         int result = 1;
-        final MutableContextData map = this.localMap.get();
+        final StringMap map = this.localMap.get();
         result = prime * result + ((map == null) ? 0 : map.hashCode());
         return result;
     }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/800db9c7/log4j-api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextMap.java
----------------------------------------------------------------------
diff --git 
a/log4j-api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextMap.java
 
b/log4j-api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextMap.java
index 386ff6b..4a016be 100644
--- 
a/log4j-api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextMap.java
+++ 
b/log4j-api/src/main/java/org/apache/logging/log4j/spi/DefaultThreadContextMap.java
@@ -21,7 +21,7 @@ import java.util.HashMap;
 import java.util.Map;
 
 import org.apache.logging.log4j.util.BiConsumer;
-import org.apache.logging.log4j.util.ContextData;
+import org.apache.logging.log4j.util.ReadOnlyStringMap;
 import org.apache.logging.log4j.util.PropertiesUtil;
 import org.apache.logging.log4j.util.TriConsumer;
 
@@ -31,7 +31,7 @@ import org.apache.logging.log4j.util.TriConsumer;
  * expected that the Map will be passed to many more log events than the 
number of keys it contains the performance
  * should be much better than if the Map was copied for each event.
  */
-public class DefaultThreadContextMap implements ThreadContextMap, ContextData {
+public class DefaultThreadContextMap implements ThreadContextMap, 
ReadOnlyStringMap {
 
     /**
      * Property name ({@value} ) for selecting {@code InheritableThreadLocal} 
(value "true") or plain

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/800db9c7/log4j-api/src/main/java/org/apache/logging/log4j/spi/GarbageFreeSortedArrayThreadContextMap.java
----------------------------------------------------------------------
diff --git 
a/log4j-api/src/main/java/org/apache/logging/log4j/spi/GarbageFreeSortedArrayThreadContextMap.java
 
b/log4j-api/src/main/java/org/apache/logging/log4j/spi/GarbageFreeSortedArrayThreadContextMap.java
index 7a31d02..afba53c 100644
--- 
a/log4j-api/src/main/java/org/apache/logging/log4j/spi/GarbageFreeSortedArrayThreadContextMap.java
+++ 
b/log4j-api/src/main/java/org/apache/logging/log4j/spi/GarbageFreeSortedArrayThreadContextMap.java
@@ -20,13 +20,13 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
-import org.apache.logging.log4j.util.ContextData;
-import org.apache.logging.log4j.util.MutableContextData;
+import org.apache.logging.log4j.util.ReadOnlyStringMap;
+import org.apache.logging.log4j.util.StringMap;
 import org.apache.logging.log4j.util.PropertiesUtil;
-import org.apache.logging.log4j.util.SortedStringArrayMap;
+import org.apache.logging.log4j.util.SortedArrayStringMap;
 
 /**
- * {@code SortedStringArrayMap}-based implementation of the {@code 
ThreadContextMap} interface that attempts not to
+ * {@code SortedArrayStringMap}-based implementation of the {@code 
ThreadContextMap} interface that attempts not to
  * create temporary objects. Adding and removing key-value pairs will not 
create temporary objects.
  * <p>
  * This implementation does <em>not</em> make a copy of its contents on every 
operation, so this data structure cannot
@@ -52,7 +52,7 @@ class GarbageFreeSortedArrayThreadContextMap implements 
ThreadContextMap2  {
      */
     protected static final String PROPERTY_NAME_INITIAL_CAPACITY = 
"log4j2.ThreadContext.initial.capacity";
 
-    protected final ThreadLocal<MutableContextData> localMap;
+    protected final ThreadLocal<StringMap> localMap;
 
     public GarbageFreeSortedArrayThreadContextMap() {
         this.localMap = createThreadLocalMap();
@@ -60,14 +60,14 @@ class GarbageFreeSortedArrayThreadContextMap implements 
ThreadContextMap2  {
 
     // LOG4J2-479: by default, use a plain ThreadLocal, only use 
InheritableThreadLocal if configured.
     // (This method is package protected for JUnit tests.)
-    private ThreadLocal<MutableContextData> createThreadLocalMap() {
+    private ThreadLocal<StringMap> createThreadLocalMap() {
         final PropertiesUtil managerProps = PropertiesUtil.getProperties();
         final boolean inheritable = 
managerProps.getBooleanProperty(INHERITABLE_MAP);
         if (inheritable) {
-            return new InheritableThreadLocal<MutableContextData>() {
+            return new InheritableThreadLocal<StringMap>() {
                 @Override
-                protected MutableContextData childValue(final 
MutableContextData parentValue) {
-                    return parentValue != null ? 
createMutableContextData(parentValue) : null;
+                protected StringMap childValue(final StringMap parentValue) {
+                    return parentValue != null ? createStringMap(parentValue) 
: null;
                 }
             };
         }
@@ -76,34 +76,34 @@ class GarbageFreeSortedArrayThreadContextMap implements 
ThreadContextMap2  {
     }
 
     /**
-     * Returns an implementation of the {@code MutableContextData} used to 
back this thread context map.
+     * Returns an implementation of the {@code StringMap} used to back this 
thread context map.
      * <p>
      * Subclasses may override.
      * </p>
-     * @return an implementation of the {@code MutableContextData} used to 
back this thread context map
+     * @return an implementation of the {@code StringMap} used to back this 
thread context map
      */
-    protected MutableContextData createMutableContextData() {
-        return new 
SortedStringArrayMap(PropertiesUtil.getProperties().getIntegerProperty(
+    protected StringMap createStringMap() {
+        return new 
SortedArrayStringMap(PropertiesUtil.getProperties().getIntegerProperty(
                 PROPERTY_NAME_INITIAL_CAPACITY, DEFAULT_INITIAL_CAPACITY));
     }
 
     /**
-     * Returns an implementation of the {@code MutableContextData} used to 
back this thread context map, pre-populated
+     * Returns an implementation of the {@code StringMap} used to back this 
thread context map, pre-populated
      * with the contents of the specified context data.
      * <p>
      * Subclasses may override.
      * </p>
      * @param original the key-value pairs to initialize the returned context 
data with
-     * @return an implementation of the {@code MutableContextData} used to 
back this thread context map
+     * @return an implementation of the {@code StringMap} used to back this 
thread context map
      */
-    protected MutableContextData createMutableContextData(final ContextData 
original) {
-        return new SortedStringArrayMap(original);
+    protected StringMap createStringMap(final ReadOnlyStringMap original) {
+        return new SortedArrayStringMap(original);
     }
 
-    private MutableContextData getThreadLocalMap() {
-        MutableContextData map = localMap.get();
+    private StringMap getThreadLocalMap() {
+        StringMap map = localMap.get();
         if (map == null) {
-            map = createMutableContextData();
+            map = createStringMap();
             localMap.set(map);
         }
         return map;
@@ -119,7 +119,7 @@ class GarbageFreeSortedArrayThreadContextMap implements 
ThreadContextMap2  {
         if (values == null || values.isEmpty()) {
             return;
         }
-        final MutableContextData map = getThreadLocalMap();
+        final StringMap map = getThreadLocalMap();
         for (final Map.Entry<String, String> entry : values.entrySet()) {
             map.putValue(entry.getKey(), entry.getValue());
         }
@@ -127,13 +127,13 @@ class GarbageFreeSortedArrayThreadContextMap implements 
ThreadContextMap2  {
 
     @Override
     public String get(final String key) {
-        final MutableContextData map = localMap.get();
+        final StringMap map = localMap.get();
         return map == null ? null : (String) map.getValue(key);
     }
 
     @Override
     public void remove(final String key) {
-        final MutableContextData map = localMap.get();
+        final StringMap map = localMap.get();
         if (map != null) {
             map.remove(key);
         }
@@ -141,7 +141,7 @@ class GarbageFreeSortedArrayThreadContextMap implements 
ThreadContextMap2  {
 
     @Override
     public void clear() {
-        final MutableContextData map = localMap.get();
+        final StringMap map = localMap.get();
         if (map != null) {
             map.clear();
         }
@@ -149,13 +149,13 @@ class GarbageFreeSortedArrayThreadContextMap implements 
ThreadContextMap2  {
 
     @Override
     public boolean containsKey(final String key) {
-        final MutableContextData map = localMap.get();
+        final StringMap map = localMap.get();
         return map != null && map.containsKey(key);
     }
 
     @Override
     public Map<String, String> getCopy() {
-        final MutableContextData map = localMap.get();
+        final StringMap map = localMap.get();
         return map == null ? new HashMap<String, String>() : map.toMap();
     }
 
@@ -163,10 +163,10 @@ class GarbageFreeSortedArrayThreadContextMap implements 
ThreadContextMap2  {
      * {@inheritDoc}
      */
     @Override
-    public MutableContextData getReadOnlyContextData() {
-        MutableContextData map = localMap.get();
+    public StringMap getReadOnlyContextData() {
+        StringMap map = localMap.get();
         if (map == null) {
-            map = createMutableContextData();
+            map = createStringMap();
             localMap.set(map);
         }
         return map;
@@ -174,19 +174,19 @@ class GarbageFreeSortedArrayThreadContextMap implements 
ThreadContextMap2  {
 
     @Override
     public Map<String, String> getImmutableMapOrNull() {
-        final MutableContextData map = localMap.get();
+        final StringMap map = localMap.get();
         return map == null ? null : Collections.unmodifiableMap(map.toMap());
     }
 
     @Override
     public boolean isEmpty() {
-        final MutableContextData map = localMap.get();
+        final StringMap map = localMap.get();
         return map == null || map.size() == 0;
     }
 
     @Override
     public String toString() {
-        final MutableContextData map = localMap.get();
+        final StringMap map = localMap.get();
         return map == null ? "{}" : map.toString();
     }
 
@@ -194,7 +194,7 @@ class GarbageFreeSortedArrayThreadContextMap implements 
ThreadContextMap2  {
     public int hashCode() {
         final int prime = 31;
         int result = 1;
-        final MutableContextData map = this.localMap.get();
+        final StringMap map = this.localMap.get();
         result = prime * result + ((map == null) ? 0 : map.hashCode());
         return result;
     }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/800db9c7/log4j-api/src/main/java/org/apache/logging/log4j/spi/ThreadContextMap2.java
----------------------------------------------------------------------
diff --git 
a/log4j-api/src/main/java/org/apache/logging/log4j/spi/ThreadContextMap2.java 
b/log4j-api/src/main/java/org/apache/logging/log4j/spi/ThreadContextMap2.java
index 6b7d086..6c07b2e 100644
--- 
a/log4j-api/src/main/java/org/apache/logging/log4j/spi/ThreadContextMap2.java
+++ 
b/log4j-api/src/main/java/org/apache/logging/log4j/spi/ThreadContextMap2.java
@@ -18,7 +18,7 @@ package org.apache.logging.log4j.spi;
 
 import java.util.Map;
 
-import org.apache.logging.log4j.util.MutableContextData;
+import org.apache.logging.log4j.util.StringMap;
 
 /**
  * Extension service provider interface to implement additional custom MDC 
behavior for
@@ -42,10 +42,10 @@ public interface ThreadContextMap2 extends ThreadContextMap 
{
 
     /**
      * Returns the context data for reading. Note that regardless of whether 
the returned context data has been
-     * {@linkplain MutableContextData#freeze() frozen} (made read-only) or 
not, callers should not attempt to modify
+     * {@linkplain StringMap#freeze() frozen} (made read-only) or not, callers 
should not attempt to modify
      * the returned data structure.
      *
-     * @return the {@code MutableContextData}
+     * @return the {@code StringMap}
      */
-    MutableContextData getReadOnlyContextData();
+    StringMap getReadOnlyContextData();
 }

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/800db9c7/log4j-api/src/main/java/org/apache/logging/log4j/util/BiConsumer.java
----------------------------------------------------------------------
diff --git 
a/log4j-api/src/main/java/org/apache/logging/log4j/util/BiConsumer.java 
b/log4j-api/src/main/java/org/apache/logging/log4j/util/BiConsumer.java
index 713b50b..1d2ca59 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/BiConsumer.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/BiConsumer.java
@@ -21,7 +21,7 @@ package org.apache.logging.log4j.util;
  *
  * @param <K> type of the first argument
  * @param <V> type of the second argument
- * @see ContextData
+ * @see ReadOnlyStringMap
  * @since 2.7
  */
 public interface BiConsumer<K, V> {

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/800db9c7/log4j-api/src/main/java/org/apache/logging/log4j/util/ContextData.java
----------------------------------------------------------------------
diff --git 
a/log4j-api/src/main/java/org/apache/logging/log4j/util/ContextData.java 
b/log4j-api/src/main/java/org/apache/logging/log4j/util/ContextData.java
deleted file mode 100644
index f580a75..0000000
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/ContextData.java
+++ /dev/null
@@ -1,106 +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.util;
-
-import java.io.Serializable;
-import java.util.Map;
-
-/**
- * A read-only collection of String keys and values of arbitrary type.
- *
- * @since 2.7
- */
-public interface ContextData extends Serializable {
-    /**
-     * Returns a non-{@code null} mutable {@code Map<String, String>} 
containing a snapshot of this data structure.
-     *
-     * @return a mutable copy of this data structure in {@code Map<String, 
String>} form
-     */
-    Map<String, String> toMap();
-
-    /**
-     * Returns {@code true} if this data structure contains the specified key, 
{@code false} otherwise.
-     *
-     * @param key the key whose presence to check. May be {@code null}.
-     * @return {@code true} if this data structure contains the specified key, 
{@code false} otherwise
-     */
-    boolean containsKey(String key);
-
-    /**
-     * Performs the given action for each key-value pair in this data structure
-     * until all entries have been processed or the action throws an exception.
-     * <p>
-     * Some implementations may not support structural modifications (adding 
new elements or removing elements) while
-     * iterating over the contents. In such implementations, attempts to add 
or remove elements from the
-     * {@code BiConsumer}'s {@link BiConsumer#accept(Object, Object)} accept} 
method may cause a
-     * {@code ConcurrentModificationException} to be thrown.
-     * </p>
-     *
-     * @param action The action to be performed for each key-value pair in 
this collection
-     * @param <V> type of the value
-     * @throws java.util.ConcurrentModificationException some implementations 
may not support structural modifications
-     *          to this data structure while iterating over the contents with 
{@link #forEach(BiConsumer)} or
-     *          {@link #forEach(TriConsumer, Object)}.
-     */
-    <V> void forEach(final BiConsumer<String, ? super V> action);
-
-    /**
-     * Performs the given action for each key-value pair in this data structure
-     * until all entries have been processed or the action throws an exception.
-     * <p>
-     * The third parameter lets callers pass in a stateful object to be 
modified with the key-value pairs,
-     * so the TriConsumer implementation itself can be stateless and 
potentially reusable.
-     * </p>
-     * <p>
-     * Some implementations may not support structural modifications (adding 
new elements or removing elements) while
-     * iterating over the contents. In such implementations, attempts to add 
or remove elements from the
-     * {@code TriConsumer}'s {@link TriConsumer#accept(Object, Object, Object) 
accept} method may cause a
-     * {@code ConcurrentModificationException} to be thrown.
-     * </p>
-     *
-     * @param action The action to be performed for each key-value pair in 
this collection
-     * @param state the object to be passed as the third parameter to each 
invocation on the specified
-     *          triconsumer
-     * @param <V> type of the value
-     * @param <S> type of the third parameter
-     * @throws java.util.ConcurrentModificationException some implementations 
may not support structural modifications
-     *          to this data structure while iterating over the contents with 
{@link #forEach(BiConsumer)} or
-     *          {@link #forEach(TriConsumer, Object)}.
-     */
-    <V, S> void forEach(final TriConsumer<String, ? super V, S> action, final 
S state);
-
-    /**
-     * Returns the value for the specified key, or {@code null} if the 
specified key does not exist in this collection.
-     *
-     * @param key the key whose value to return
-     * @return the value for the specified key or {@code null}
-     */
-    <V> V getValue(final String key);
-
-    /**
-     * Returns {@code true} if this collection is empty (size is zero), {@code 
false} otherwise.
-     * @return {@code true} if this collection is empty (size is zero)
-     */
-    boolean isEmpty();
-
-    /**
-     * Returns the number of key-value pairs in this collection.
-     *
-     * @return the number of key-value pairs in this collection
-     */
-    int size();
-}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/800db9c7/log4j-api/src/main/java/org/apache/logging/log4j/util/MutableContextData.java
----------------------------------------------------------------------
diff --git 
a/log4j-api/src/main/java/org/apache/logging/log4j/util/MutableContextData.java 
b/log4j-api/src/main/java/org/apache/logging/log4j/util/MutableContextData.java
deleted file mode 100644
index 3a08fe4..0000000
--- 
a/log4j-api/src/main/java/org/apache/logging/log4j/util/MutableContextData.java
+++ /dev/null
@@ -1,98 +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.util;
-
-/**
- * Exposes methods to add and remove key-value pairs to and from {@code 
ContextData}.
- *
- * @see ContextData
- * @since 2.7
- */
-public interface MutableContextData extends ContextData {
-
-    /**
-     * Removes all key-value pairs from this collection.
-     * @throws java.util.ConcurrentModificationException some implementations 
may not support structural modifications
-     *          to this data structure while iterating over the contents with 
{@link #forEach(BiConsumer)} or
-     *          {@link #forEach(TriConsumer, Object)}.
-     * @throws UnsupportedOperationException if this collection has been 
{@linkplain #isFrozen() frozen}.
-     */
-    void clear();
-
-    /**
-     * Indicates whether some other object is "equal to" this one.
-     *
-     * @param obj
-     *            the reference object with which to compare.
-     * @return {@code true} if this object is the same as the obj argument; 
{@code false} otherwise.
-     * @see #hashCode()
-     */
-    @Override
-    boolean equals(final Object obj);
-
-    /**
-     * Makes this collection immutable. Attempts to modify the collection 
after the {@code freeze()} method was called
-     * will result in an {@code UnsupportedOperationException} being thrown.
-     */
-    void freeze();
-
-    /**
-     * Returns a hash code value for the object.
-     * @return a hash code value for this object.
-     */
-    @Override
-    int hashCode();
-
-    /**
-     * Returns {@code true} if this object has been {@linkplain #freeze() 
frozen}, {@code false} otherwise.
-     * @return  {@code true} if this object has been {@linkplain #freeze() 
frozen}, {@code false} otherwise
-     */
-    boolean isFrozen();
-
-    /**
-     * Copy all key-value pairs from the specified {@code ContextData} into 
this {@code MutableContextData}.
-     * @param source the {@code ContextData} to copy key-value pairs from
-     * @throws java.util.ConcurrentModificationException some implementations 
may not support structural modifications
-     *          to this data structure while iterating over the contents with 
{@link #forEach(BiConsumer)} or
-     *          {@link #forEach(TriConsumer, Object)}.
-     * @throws UnsupportedOperationException if this collection has been 
{@linkplain #isFrozen() frozen}.
-     */
-    void putAll(final ContextData source);
-
-    /**
-     * Puts the specified key-value pair into the collection.
-     *
-     * @param key the key to add or remove. Keys may be {@code null}.
-     * @param value the value to add. Values may be {@code null}.
-     * @throws java.util.ConcurrentModificationException some implementations 
may not support structural modifications
-     *          to this data structure while iterating over the contents with 
{@link #forEach(BiConsumer)} or
-     *          {@link #forEach(TriConsumer, Object)}.
-     * @throws UnsupportedOperationException if this collection has been 
{@linkplain #isFrozen() frozen}.
-     */
-    void putValue(final String key, final Object value);
-
-    /**
-     * Removes the key-value pair for the specified key from this data 
structure.
-     *
-     * @param key the key to remove. May be {@code null}.
-     * @throws java.util.ConcurrentModificationException some implementations 
may not support structural modifications
-     *          to this data structure while iterating over the contents with 
{@link #forEach(BiConsumer)} or
-     *          {@link #forEach(TriConsumer, Object)}.
-     * @throws UnsupportedOperationException if this collection has been 
{@linkplain #isFrozen() frozen}.
-     */
-    void remove(final String key);
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/800db9c7/log4j-api/src/main/java/org/apache/logging/log4j/util/ReadOnlyStringMap.java
----------------------------------------------------------------------
diff --git 
a/log4j-api/src/main/java/org/apache/logging/log4j/util/ReadOnlyStringMap.java 
b/log4j-api/src/main/java/org/apache/logging/log4j/util/ReadOnlyStringMap.java
new file mode 100644
index 0000000..9964761
--- /dev/null
+++ 
b/log4j-api/src/main/java/org/apache/logging/log4j/util/ReadOnlyStringMap.java
@@ -0,0 +1,106 @@
+/*
+ * 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.util;
+
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * A read-only collection of String keys and values of arbitrary type.
+ *
+ * @since 2.7
+ */
+public interface ReadOnlyStringMap extends Serializable {
+    /**
+     * Returns a non-{@code null} mutable {@code Map<String, String>} 
containing a snapshot of this data structure.
+     *
+     * @return a mutable copy of this data structure in {@code Map<String, 
String>} form
+     */
+    Map<String, String> toMap();
+
+    /**
+     * Returns {@code true} if this data structure contains the specified key, 
{@code false} otherwise.
+     *
+     * @param key the key whose presence to check. May be {@code null}.
+     * @return {@code true} if this data structure contains the specified key, 
{@code false} otherwise
+     */
+    boolean containsKey(String key);
+
+    /**
+     * Performs the given action for each key-value pair in this data structure
+     * until all entries have been processed or the action throws an exception.
+     * <p>
+     * Some implementations may not support structural modifications (adding 
new elements or removing elements) while
+     * iterating over the contents. In such implementations, attempts to add 
or remove elements from the
+     * {@code BiConsumer}'s {@link BiConsumer#accept(Object, Object)} accept} 
method may cause a
+     * {@code ConcurrentModificationException} to be thrown.
+     * </p>
+     *
+     * @param action The action to be performed for each key-value pair in 
this collection
+     * @param <V> type of the value
+     * @throws java.util.ConcurrentModificationException some implementations 
may not support structural modifications
+     *          to this data structure while iterating over the contents with 
{@link #forEach(BiConsumer)} or
+     *          {@link #forEach(TriConsumer, Object)}.
+     */
+    <V> void forEach(final BiConsumer<String, ? super V> action);
+
+    /**
+     * Performs the given action for each key-value pair in this data structure
+     * until all entries have been processed or the action throws an exception.
+     * <p>
+     * The third parameter lets callers pass in a stateful object to be 
modified with the key-value pairs,
+     * so the TriConsumer implementation itself can be stateless and 
potentially reusable.
+     * </p>
+     * <p>
+     * Some implementations may not support structural modifications (adding 
new elements or removing elements) while
+     * iterating over the contents. In such implementations, attempts to add 
or remove elements from the
+     * {@code TriConsumer}'s {@link TriConsumer#accept(Object, Object, Object) 
accept} method may cause a
+     * {@code ConcurrentModificationException} to be thrown.
+     * </p>
+     *
+     * @param action The action to be performed for each key-value pair in 
this collection
+     * @param state the object to be passed as the third parameter to each 
invocation on the specified
+     *          triconsumer
+     * @param <V> type of the value
+     * @param <S> type of the third parameter
+     * @throws java.util.ConcurrentModificationException some implementations 
may not support structural modifications
+     *          to this data structure while iterating over the contents with 
{@link #forEach(BiConsumer)} or
+     *          {@link #forEach(TriConsumer, Object)}.
+     */
+    <V, S> void forEach(final TriConsumer<String, ? super V, S> action, final 
S state);
+
+    /**
+     * Returns the value for the specified key, or {@code null} if the 
specified key does not exist in this collection.
+     *
+     * @param key the key whose value to return
+     * @return the value for the specified key or {@code null}
+     */
+    <V> V getValue(final String key);
+
+    /**
+     * Returns {@code true} if this collection is empty (size is zero), {@code 
false} otherwise.
+     * @return {@code true} if this collection is empty (size is zero)
+     */
+    boolean isEmpty();
+
+    /**
+     * Returns the number of key-value pairs in this collection.
+     *
+     * @return the number of key-value pairs in this collection
+     */
+    int size();
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/800db9c7/log4j-api/src/main/java/org/apache/logging/log4j/util/SortedArrayStringMap.java
----------------------------------------------------------------------
diff --git 
a/log4j-api/src/main/java/org/apache/logging/log4j/util/SortedArrayStringMap.java
 
b/log4j-api/src/main/java/org/apache/logging/log4j/util/SortedArrayStringMap.java
new file mode 100644
index 0000000..c1ed512
--- /dev/null
+++ 
b/log4j-api/src/main/java/org/apache/logging/log4j/util/SortedArrayStringMap.java
@@ -0,0 +1,477 @@
+/*
+ * 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.util;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.util.Arrays;
+import java.util.ConcurrentModificationException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * <em>Consider this class private.</em>
+ * Array-based implementation of the {@code ReadOnlyStringMap} interface. Keys 
are held in a sorted array.
+ * <p>
+ * This is not a generic collection, but makes some trade-offs to optimize for 
the Log4j ReadOnlyStringMap use case:
+ * </p>
+ * <ul>
+ *   <li>Garbage-free iteration over key-value pairs with {@code BiConsumer} 
and {@code TriConsumer}.</li>
+ *   <li>Fast copy. If the ThreadContextMap is also an instance of {@code 
SortedArrayStringMap}, the full thread context
+ *     data can be transferred with two array copies and two field 
updates.</li>
+ *   <li>Acceptable performance for small data sets. The current 
implementation stores keys in a sorted array, values
+ *     are stored in a separate array at the same index.
+ *     Worst-case performance of {@code get} and {@code containsKey} is O(log 
N),
+ *     worst-case performance of {@code put} and {@code remove} is O(N log N).
+ *     The expectation is that for the small values of {@code N} (less than 
100) that are the vast majority of
+ *     ThreadContext use cases, the constants dominate performance more than 
the asymptotic performance of the
+ *     algorithms used.
+ *     </li>
+ *     <li>Compact representation.</li>
+ * </ul>
+ *
+ * @since 2.7
+ */
+public class SortedArrayStringMap implements StringMap {
+
+    /**
+     * The default initial capacity.
+     */
+    private static final int DEFAULT_INITIAL_CAPACITY = 4;
+    private static final long serialVersionUID = -5748905872274478116L;
+    private static final int HASHVAL = 31;
+
+    private static final TriConsumer<String, Object, StringMap> PUT_ALL = new 
TriConsumer<String, Object, StringMap>() {
+        @Override
+        public void accept(final String key, final Object value, final 
StringMap contextData) {
+            contextData.putValue(key, value);
+        }
+    };
+
+    /**
+     * An empty array instance to share when the table is not inflated.
+     */
+    private static final String[] EMPTY = {};
+    private static final String FROZEN = "Frozen collection cannot be 
modified";
+
+    private transient String[] keys = EMPTY;
+    private transient Object[] values = EMPTY;
+
+    /**
+     * The number of key-value mappings contained in this map.
+     */
+    private transient int size;
+
+    /**
+     * The next size value at which to resize (capacity * load factor).
+     * @serial
+     */
+    // If table == EMPTY_TABLE then this is the initial capacity at which the
+    // table will be created when inflated.
+    private int threshold;
+    private boolean immutable;
+    private transient boolean iterating;
+
+    public SortedArrayStringMap() {
+        this(DEFAULT_INITIAL_CAPACITY);
+    }
+
+    public SortedArrayStringMap(final int initialCapacity) {
+        if (initialCapacity < 1) {
+            throw new IllegalArgumentException("Initial capacity must be at 
least one but was " + initialCapacity);
+        }
+        threshold = ceilingNextPowerOfTwo(initialCapacity);
+    }
+
+    public SortedArrayStringMap(final ReadOnlyStringMap other) {
+        if (other instanceof SortedArrayStringMap) {
+            initFrom0((SortedArrayStringMap) other);
+        } else if (other != null) {
+            resize(ceilingNextPowerOfTwo(other.size()));
+            other.forEach(PUT_ALL, this);
+        }
+    }
+
+    private void assertNotFrozen() {
+        if (immutable) {
+            throw new UnsupportedOperationException(FROZEN);
+        }
+    }
+
+    private void assertNoConcurrentModification() {
+        if (iterating) {
+            throw new ConcurrentModificationException();
+        }
+    }
+
+    @Override
+    public void clear() {
+        if (keys == EMPTY) {
+            return;
+        }
+        assertNotFrozen();
+        assertNoConcurrentModification();
+
+        Arrays.fill(keys, 0, size, null);
+        Arrays.fill(values, 0, size, null);
+        size = 0;
+    }
+
+    @Override
+    public boolean containsKey(final String key) {
+        return indexOfKey(key) >= 0;
+    }
+
+    @Override
+    public Map<String, String> toMap() {
+        final Map<String, String> result = new HashMap<>(size());
+        for (int i = 0; i < size(); i++) {
+            final Object value = getValueAt(i);
+            result.put(getKeyAt(i), value == null ? null : 
String.valueOf(value));
+        }
+        return result;
+    }
+
+    @Override
+    public void freeze() {
+        immutable = true;
+    }
+
+    @Override
+    public boolean isFrozen() {
+        return immutable;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <V> V getValue(final String key) {
+        final int index = indexOfKey(key);
+        if (index < 0) {
+            return null;
+        }
+        return (V) values[index];
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return size == 0;
+    }
+
+    int indexOfKey(final String key) {
+        if (keys == EMPTY) {
+            return -1;
+        }
+        if (key == null) { // null key is located at the start of the array
+            return nullKeyIndex(); // insert at index zero
+        }
+        final int start = size > 0 && keys[0] == null ? 1 : 0;
+        return Arrays.binarySearch(keys, start, size, key);
+    }
+
+    private int nullKeyIndex() {
+        return size > 0 && keys[0] == null ? 0 : ~0;
+    }
+
+    @Override
+    public void putValue(final String key, final Object value) {
+        assertNotFrozen();
+        assertNoConcurrentModification();
+
+        if (keys == EMPTY) {
+            inflateTable(threshold);
+        }
+        final int index = indexOfKey(key);
+        if (index >= 0) {
+            keys[index] = key;
+            values[index] = value;
+        } else { // not found, so insert.
+            insertAt(~index, key, value);
+        }
+    }
+
+    private void insertAt(final int index, final String key, final Object 
value) {
+        ensureCapacity();
+        System.arraycopy(keys, index, keys, index + 1, size - index);
+        System.arraycopy(values, index, values, index + 1, size - index);
+        keys[index] = key;
+        values[index] = value;
+        size++;
+    }
+
+    @Override
+    public void putAll(final ReadOnlyStringMap source) {
+        if (source == this) {
+            return; // this.putAll(this) does not modify this collection
+        }
+        assertNotFrozen();
+        assertNoConcurrentModification();
+
+        if (source instanceof SortedArrayStringMap && this.size == 0) {
+            initFrom0((SortedArrayStringMap) source);
+        } else if (source != null) {
+            source.forEach(PUT_ALL, this);
+        }
+    }
+
+    private void initFrom0(final SortedArrayStringMap other) {
+        if (keys.length < other.size) {
+            keys = new String[other.threshold];
+            values = new Object[other.threshold];
+        }
+        System.arraycopy(other.keys, 0, keys, 0, other.size);
+        System.arraycopy(other.values, 0, values, 0, other.size);
+
+        size = other.size;
+        threshold = other.threshold;
+    }
+
+    private void ensureCapacity() {
+        if (size >= threshold) {
+            resize(threshold * 2);
+        }
+    }
+
+    private void resize(final int newCapacity) {
+        final String[] oldKeys = keys;
+        final Object[] oldValues = values;
+
+        keys = new String[newCapacity];
+        values = new Object[newCapacity];
+
+        System.arraycopy(oldKeys, 0, keys, 0, size);
+        System.arraycopy(oldValues, 0, values, 0, size);
+
+        threshold = newCapacity;
+    }
+
+    /**
+     * Inflates the table.
+     */
+    private void inflateTable(final int toSize) {
+        threshold = toSize;
+        keys = new String[toSize];
+        values = new Object[toSize];
+    }
+
+    @Override
+    public void remove(final String key) {
+        if (keys == EMPTY) {
+            return;
+        }
+
+        final int index = indexOfKey(key);
+        if (index >= 0) {
+            assertNotFrozen();
+            assertNoConcurrentModification();
+
+            System.arraycopy(keys, index + 1, keys, index, size - 1 - index);
+            System.arraycopy(values, index + 1, values, index, size - 1 - 
index);
+            keys[size - 1] = null;
+            values[size - 1] = null;
+            size--;
+        }
+    }
+
+    String getKeyAt(final int index) {
+        if (index < 0 || index >= size) {
+            return null;
+        }
+        return keys[index];
+    }
+
+    @SuppressWarnings("unchecked")
+    <V> V getValueAt(final int index) {
+        if (index < 0 || index >= size) {
+            return null;
+        }
+        return (V) values[index];
+    }
+
+    @Override
+    public int size() {
+        return size;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <V> void forEach(final BiConsumer<String, ? super V> action) {
+        iterating = true;
+        try {
+            for (int i = 0; i < size; i++) {
+                action.accept(keys[i], (V) values[i]);
+            }
+        } finally {
+            iterating = false;
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <V, T> void forEach(final TriConsumer<String, ? super V, T> action, 
final T state) {
+        iterating = true;
+        try {
+            for (int i = 0; i < size; i++) {
+                action.accept(keys[i], (V) values[i], state);
+            }
+        } finally {
+            iterating = false;
+        }
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (!(obj instanceof SortedArrayStringMap)) {
+            return false;
+        }
+        final SortedArrayStringMap other = (SortedArrayStringMap) obj;
+        if (this.size() != other.size()) {
+            return false;
+        }
+        for (int i = 0; i < size(); i++) {
+            if (!Objects.equals(keys[i], other.keys[i])) {
+                return false;
+            }
+            if (!Objects.equals(values[i], other.values[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 37;
+        result = HASHVAL * result + size;
+        result = HASHVAL * result + hashCode(keys, size);
+        result = HASHVAL * result + hashCode(values, size);
+        return result;
+    }
+
+    private static int hashCode(final Object[] values, final int length) {
+        int result = 1;
+        for (int i = 0; i < length; i++) {
+            result = HASHVAL * result + (values[i] == null ? 0 : 
values[i].hashCode());
+        }
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder(256);
+        sb.append('{');
+        for (int i = 0; i < size; i++) {
+            if (i > 0) {
+                sb.append(", ");
+            }
+            sb.append(keys[i]).append('=');
+            sb.append(values[i] == this ? "(this map)" : values[i]);
+        }
+        sb.append('}');
+        return sb.toString();
+    }
+
+    /**
+     * Save the state of the {@code SortedArrayStringMap} instance to a stream 
(i.e.,
+     * serialize it).
+     *
+     * @serialData The <i>capacity</i> of the SortedArrayStringMap (the length 
of the
+     *             bucket array) is emitted (int), followed by the
+     *             <i>size</i> (an int, the number of key-value
+     *             mappings), followed by the key (Object) and value (Object)
+     *             for each key-value mapping.  The key-value mappings are
+     *             emitted in no particular order.
+     */
+    private void writeObject(final java.io.ObjectOutputStream s) throws 
IOException {
+        // Write out the threshold, and any hidden stuff
+        s.defaultWriteObject();
+
+        // Write out number of buckets
+        if (keys == EMPTY) {
+            s.writeInt(ceilingNextPowerOfTwo(threshold));
+        } else {
+            s.writeInt(keys.length);
+        }
+
+        // Write out size (number of Mappings)
+        s.writeInt(size);
+
+        // Write out keys and values (alternating)
+        if (size > 0) {
+            for (int i = 0; i < size; i++) {
+                s.writeObject(keys[i]);
+                s.writeObject(values[i]);
+            }
+        }
+    }
+
+
+    /**
+     * Calculate the next power of 2, greater than or equal to x.
+     * <p>
+     * From Hacker's Delight, Chapter 3, Harry S. Warren Jr.
+     *
+     * @param x Value to round up
+     * @return The next power of 2 from x inclusive
+     */
+    private static int ceilingNextPowerOfTwo(final int x) {
+        final int BITS_PER_INT = 32;
+        return 1 << (BITS_PER_INT - Integer.numberOfLeadingZeros(x - 1));
+    }
+
+    /**
+     * Reconstitute the {@code SortedArrayStringMap} instance from a stream 
(i.e.,
+     * deserialize it).
+     */
+    private void readObject(final java.io.ObjectInputStream s)  throws 
IOException, ClassNotFoundException {
+        // Read in the threshold (ignored), and any hidden stuff
+        s.defaultReadObject();
+
+        // set other fields that need values
+        keys = EMPTY;
+        values = EMPTY;
+
+        // Read in number of buckets
+        final int capacity = s.readInt();
+        if (capacity < 0) {
+            throw new InvalidObjectException("Illegal capacity: " + capacity);
+        }
+
+        // Read number of mappings
+        final int mappings = s.readInt();
+        if (mappings < 0) {
+            throw new InvalidObjectException("Illegal mappings count: " + 
mappings);
+        }
+
+        // allocate the bucket array;
+        if (mappings > 0) {
+            inflateTable(capacity);
+        } else {
+            threshold = capacity;
+        }
+
+        // Read the keys and values, and put the mappings in the arrays
+        for (int i = 0; i < mappings; i++) {
+            keys[i] = (String) s.readObject();
+            values[i] = s.readObject();
+        }
+        size = mappings;
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/800db9c7/log4j-api/src/main/java/org/apache/logging/log4j/util/SortedStringArrayMap.java
----------------------------------------------------------------------
diff --git 
a/log4j-api/src/main/java/org/apache/logging/log4j/util/SortedStringArrayMap.java
 
b/log4j-api/src/main/java/org/apache/logging/log4j/util/SortedStringArrayMap.java
deleted file mode 100644
index 3d30069..0000000
--- 
a/log4j-api/src/main/java/org/apache/logging/log4j/util/SortedStringArrayMap.java
+++ /dev/null
@@ -1,477 +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.util;
-
-import java.io.IOException;
-import java.io.InvalidObjectException;
-import java.util.Arrays;
-import java.util.ConcurrentModificationException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * <em>Consider this class private.</em>
- * Array-based implementation of the {@code ContextData} interface. Keys are 
held in a sorted array.
- * <p>
- * This is not a generic collection, but makes some trade-offs to optimize for 
the Log4j ContextData use case:
- * </p>
- * <ul>
- *   <li>Garbage-free iteration over key-value pairs with {@code BiConsumer} 
and {@code TriConsumer}.</li>
- *   <li>Fast copy. If the ThreadContextMap is also an instance of {@code 
SortedStringArrayMap}, the full thread context
- *     data can be transferred with two array copies and two field 
updates.</li>
- *   <li>Acceptable performance for small data sets. The current 
implementation stores keys in a sorted array, values
- *     are stored in a separate array at the same index.
- *     Worst-case performance of {@code get} and {@code containsKey} is O(log 
N),
- *     worst-case performance of {@code put} and {@code remove} is O(N log N).
- *     The expectation is that for the small values of {@code N} (less than 
100) that are the vast majority of
- *     ThreadContext use cases, the constants dominate performance more than 
the asymptotic performance of the
- *     algorithms used.
- *     </li>
- *     <li>Compact representation.</li>
- * </ul>
- *
- * @since 2.7
- */
-public class SortedStringArrayMap implements MutableContextData {
-
-    /**
-     * The default initial capacity.
-     */
-    private static final int DEFAULT_INITIAL_CAPACITY = 4;
-    private static final long serialVersionUID = -5748905872274478116L;
-    private static final int HASHVAL = 31;
-
-    private static final TriConsumer<String, Object, MutableContextData> 
PUT_ALL = new TriConsumer<String, Object, MutableContextData>() {
-        @Override
-        public void accept(final String key, final Object value, final 
MutableContextData contextData) {
-            contextData.putValue(key, value);
-        }
-    };
-
-    /**
-     * An empty array instance to share when the table is not inflated.
-     */
-    private static final String[] EMPTY = {};
-    private static final String FROZEN = "Frozen collection cannot be 
modified";
-
-    private transient String[] keys = EMPTY;
-    private transient Object[] values = EMPTY;
-
-    /**
-     * The number of key-value mappings contained in this map.
-     */
-    private transient int size;
-
-    /**
-     * The next size value at which to resize (capacity * load factor).
-     * @serial
-     */
-    // If table == EMPTY_TABLE then this is the initial capacity at which the
-    // table will be created when inflated.
-    private int threshold;
-    private boolean immutable;
-    private transient boolean iterating;
-
-    public SortedStringArrayMap() {
-        this(DEFAULT_INITIAL_CAPACITY);
-    }
-
-    public SortedStringArrayMap(final int initialCapacity) {
-        if (initialCapacity < 1) {
-            throw new IllegalArgumentException("Initial capacity must be at 
least one but was " + initialCapacity);
-        }
-        threshold = ceilingNextPowerOfTwo(initialCapacity);
-    }
-
-    public SortedStringArrayMap(final ContextData other) {
-        if (other instanceof SortedStringArrayMap) {
-            initFrom0((SortedStringArrayMap) other);
-        } else if (other != null) {
-            resize(ceilingNextPowerOfTwo(other.size()));
-            other.forEach(PUT_ALL, this);
-        }
-    }
-
-    private void assertNotFrozen() {
-        if (immutable) {
-            throw new UnsupportedOperationException(FROZEN);
-        }
-    }
-
-    private void assertNoConcurrentModification() {
-        if (iterating) {
-            throw new ConcurrentModificationException();
-        }
-    }
-
-    @Override
-    public void clear() {
-        if (keys == EMPTY) {
-            return;
-        }
-        assertNotFrozen();
-        assertNoConcurrentModification();
-
-        Arrays.fill(keys, 0, size, null);
-        Arrays.fill(values, 0, size, null);
-        size = 0;
-    }
-
-    @Override
-    public boolean containsKey(final String key) {
-        return indexOfKey(key) >= 0;
-    }
-
-    @Override
-    public Map<String, String> toMap() {
-        final Map<String, String> result = new HashMap<>(size());
-        for (int i = 0; i < size(); i++) {
-            final Object value = getValueAt(i);
-            result.put(getKeyAt(i), value == null ? null : 
String.valueOf(value));
-        }
-        return result;
-    }
-
-    @Override
-    public void freeze() {
-        immutable = true;
-    }
-
-    @Override
-    public boolean isFrozen() {
-        return immutable;
-    }
-
-    @SuppressWarnings("unchecked")
-    @Override
-    public <V> V getValue(final String key) {
-        final int index = indexOfKey(key);
-        if (index < 0) {
-            return null;
-        }
-        return (V) values[index];
-    }
-
-    @Override
-    public boolean isEmpty() {
-        return size == 0;
-    }
-
-    int indexOfKey(final String key) {
-        if (keys == EMPTY) {
-            return -1;
-        }
-        if (key == null) { // null key is located at the start of the array
-            return nullKeyIndex(); // insert at index zero
-        }
-        final int start = size > 0 && keys[0] == null ? 1 : 0;
-        return Arrays.binarySearch(keys, start, size, key);
-    }
-
-    private int nullKeyIndex() {
-        return size > 0 && keys[0] == null ? 0 : ~0;
-    }
-
-    @Override
-    public void putValue(final String key, final Object value) {
-        assertNotFrozen();
-        assertNoConcurrentModification();
-
-        if (keys == EMPTY) {
-            inflateTable(threshold);
-        }
-        final int index = indexOfKey(key);
-        if (index >= 0) {
-            keys[index] = key;
-            values[index] = value;
-        } else { // not found, so insert.
-            insertAt(~index, key, value);
-        }
-    }
-
-    private void insertAt(final int index, final String key, final Object 
value) {
-        ensureCapacity();
-        System.arraycopy(keys, index, keys, index + 1, size - index);
-        System.arraycopy(values, index, values, index + 1, size - index);
-        keys[index] = key;
-        values[index] = value;
-        size++;
-    }
-
-    @Override
-    public void putAll(final ContextData source) {
-        if (source == this) {
-            return; // this.putAll(this) does not modify this collection
-        }
-        assertNotFrozen();
-        assertNoConcurrentModification();
-
-        if (source instanceof SortedStringArrayMap && this.size == 0) {
-            initFrom0((SortedStringArrayMap) source);
-        } else if (source != null) {
-            source.forEach(PUT_ALL, this);
-        }
-    }
-
-    private void initFrom0(final SortedStringArrayMap other) {
-        if (keys.length < other.size) {
-            keys = new String[other.threshold];
-            values = new Object[other.threshold];
-        }
-        System.arraycopy(other.keys, 0, keys, 0, other.size);
-        System.arraycopy(other.values, 0, values, 0, other.size);
-
-        size = other.size;
-        threshold = other.threshold;
-    }
-
-    private void ensureCapacity() {
-        if (size >= threshold) {
-            resize(threshold * 2);
-        }
-    }
-
-    private void resize(final int newCapacity) {
-        final String[] oldKeys = keys;
-        final Object[] oldValues = values;
-
-        keys = new String[newCapacity];
-        values = new Object[newCapacity];
-
-        System.arraycopy(oldKeys, 0, keys, 0, size);
-        System.arraycopy(oldValues, 0, values, 0, size);
-
-        threshold = newCapacity;
-    }
-
-    /**
-     * Inflates the table.
-     */
-    private void inflateTable(final int toSize) {
-        threshold = toSize;
-        keys = new String[toSize];
-        values = new Object[toSize];
-    }
-
-    @Override
-    public void remove(final String key) {
-        if (keys == EMPTY) {
-            return;
-        }
-
-        final int index = indexOfKey(key);
-        if (index >= 0) {
-            assertNotFrozen();
-            assertNoConcurrentModification();
-
-            System.arraycopy(keys, index + 1, keys, index, size - 1 - index);
-            System.arraycopy(values, index + 1, values, index, size - 1 - 
index);
-            keys[size - 1] = null;
-            values[size - 1] = null;
-            size--;
-        }
-    }
-
-    String getKeyAt(final int index) {
-        if (index < 0 || index >= size) {
-            return null;
-        }
-        return keys[index];
-    }
-
-    @SuppressWarnings("unchecked")
-    <V> V getValueAt(final int index) {
-        if (index < 0 || index >= size) {
-            return null;
-        }
-        return (V) values[index];
-    }
-
-    @Override
-    public int size() {
-        return size;
-    }
-
-    @SuppressWarnings("unchecked")
-    @Override
-    public <V> void forEach(final BiConsumer<String, ? super V> action) {
-        iterating = true;
-        try {
-            for (int i = 0; i < size; i++) {
-                action.accept(keys[i], (V) values[i]);
-            }
-        } finally {
-            iterating = false;
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    @Override
-    public <V, T> void forEach(final TriConsumer<String, ? super V, T> action, 
final T state) {
-        iterating = true;
-        try {
-            for (int i = 0; i < size; i++) {
-                action.accept(keys[i], (V) values[i], state);
-            }
-        } finally {
-            iterating = false;
-        }
-    }
-
-    @Override
-    public boolean equals(final Object obj) {
-        if (obj == this) {
-            return true;
-        }
-        if (!(obj instanceof SortedStringArrayMap)) {
-            return false;
-        }
-        final SortedStringArrayMap other = (SortedStringArrayMap) obj;
-        if (this.size() != other.size()) {
-            return false;
-        }
-        for (int i = 0; i < size(); i++) {
-            if (!Objects.equals(keys[i], other.keys[i])) {
-                return false;
-            }
-            if (!Objects.equals(values[i], other.values[i])) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = 37;
-        result = HASHVAL * result + size;
-        result = HASHVAL * result + hashCode(keys, size);
-        result = HASHVAL * result + hashCode(values, size);
-        return result;
-    }
-
-    private static int hashCode(final Object[] values, final int length) {
-        int result = 1;
-        for (int i = 0; i < length; i++) {
-            result = HASHVAL * result + (values[i] == null ? 0 : 
values[i].hashCode());
-        }
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder(256);
-        sb.append('{');
-        for (int i = 0; i < size; i++) {
-            if (i > 0) {
-                sb.append(", ");
-            }
-            sb.append(keys[i]).append('=');
-            sb.append(values[i] == this ? "(this map)" : values[i]);
-        }
-        sb.append('}');
-        return sb.toString();
-    }
-
-    /**
-     * Save the state of the {@code SortedStringArrayMap} instance to a stream 
(i.e.,
-     * serialize it).
-     *
-     * @serialData The <i>capacity</i> of the SortedStringArrayMap (the length 
of the
-     *             bucket array) is emitted (int), followed by the
-     *             <i>size</i> (an int, the number of key-value
-     *             mappings), followed by the key (Object) and value (Object)
-     *             for each key-value mapping.  The key-value mappings are
-     *             emitted in no particular order.
-     */
-    private void writeObject(final java.io.ObjectOutputStream s) throws 
IOException {
-        // Write out the threshold, and any hidden stuff
-        s.defaultWriteObject();
-
-        // Write out number of buckets
-        if (keys == EMPTY) {
-            s.writeInt(ceilingNextPowerOfTwo(threshold));
-        } else {
-            s.writeInt(keys.length);
-        }
-
-        // Write out size (number of Mappings)
-        s.writeInt(size);
-
-        // Write out keys and values (alternating)
-        if (size > 0) {
-            for (int i = 0; i < size; i++) {
-                s.writeObject(keys[i]);
-                s.writeObject(values[i]);
-            }
-        }
-    }
-
-
-    /**
-     * Calculate the next power of 2, greater than or equal to x.
-     * <p>
-     * From Hacker's Delight, Chapter 3, Harry S. Warren Jr.
-     *
-     * @param x Value to round up
-     * @return The next power of 2 from x inclusive
-     */
-    private static int ceilingNextPowerOfTwo(final int x) {
-        final int BITS_PER_INT = 32;
-        return 1 << (BITS_PER_INT - Integer.numberOfLeadingZeros(x - 1));
-    }
-
-    /**
-     * Reconstitute the {@code SortedStringArrayMap} instance from a stream 
(i.e.,
-     * deserialize it).
-     */
-    private void readObject(final java.io.ObjectInputStream s)  throws 
IOException, ClassNotFoundException {
-        // Read in the threshold (ignored), and any hidden stuff
-        s.defaultReadObject();
-
-        // set other fields that need values
-        keys = EMPTY;
-        values = EMPTY;
-
-        // Read in number of buckets
-        final int capacity = s.readInt();
-        if (capacity < 0) {
-            throw new InvalidObjectException("Illegal capacity: " + capacity);
-        }
-
-        // Read number of mappings
-        final int mappings = s.readInt();
-        if (mappings < 0) {
-            throw new InvalidObjectException("Illegal mappings count: " + 
mappings);
-        }
-
-        // allocate the bucket array;
-        if (mappings > 0) {
-            inflateTable(capacity);
-        } else {
-            threshold = capacity;
-        }
-
-        // Read the keys and values, and put the mappings in the arrays
-        for (int i = 0; i < mappings; i++) {
-            keys[i] = (String) s.readObject();
-            values[i] = s.readObject();
-        }
-        size = mappings;
-    }
-}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/800db9c7/log4j-api/src/main/java/org/apache/logging/log4j/util/StringMap.java
----------------------------------------------------------------------
diff --git 
a/log4j-api/src/main/java/org/apache/logging/log4j/util/StringMap.java 
b/log4j-api/src/main/java/org/apache/logging/log4j/util/StringMap.java
new file mode 100644
index 0000000..43ea71c
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/StringMap.java
@@ -0,0 +1,98 @@
+/*
+ * 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.util;
+
+/**
+ * Exposes methods to add and remove key-value pairs to and from {@code 
ReadOnlyStringMap}.
+ *
+ * @see ReadOnlyStringMap
+ * @since 2.7
+ */
+public interface StringMap extends ReadOnlyStringMap {
+
+    /**
+     * Removes all key-value pairs from this collection.
+     * @throws java.util.ConcurrentModificationException some implementations 
may not support structural modifications
+     *          to this data structure while iterating over the contents with 
{@link #forEach(BiConsumer)} or
+     *          {@link #forEach(TriConsumer, Object)}.
+     * @throws UnsupportedOperationException if this collection has been 
{@linkplain #isFrozen() frozen}.
+     */
+    void clear();
+
+    /**
+     * Indicates whether some other object is "equal to" this one.
+     *
+     * @param obj
+     *            the reference object with which to compare.
+     * @return {@code true} if this object is the same as the obj argument; 
{@code false} otherwise.
+     * @see #hashCode()
+     */
+    @Override
+    boolean equals(final Object obj);
+
+    /**
+     * Makes this collection immutable. Attempts to modify the collection 
after the {@code freeze()} method was called
+     * will result in an {@code UnsupportedOperationException} being thrown.
+     */
+    void freeze();
+
+    /**
+     * Returns a hash code value for the object.
+     * @return a hash code value for this object.
+     */
+    @Override
+    int hashCode();
+
+    /**
+     * Returns {@code true} if this object has been {@linkplain #freeze() 
frozen}, {@code false} otherwise.
+     * @return  {@code true} if this object has been {@linkplain #freeze() 
frozen}, {@code false} otherwise
+     */
+    boolean isFrozen();
+
+    /**
+     * Copy all key-value pairs from the specified {@code ReadOnlyStringMap} 
into this {@code StringMap}.
+     * @param source the {@code ReadOnlyStringMap} to copy key-value pairs from
+     * @throws java.util.ConcurrentModificationException some implementations 
may not support structural modifications
+     *          to this data structure while iterating over the contents with 
{@link #forEach(BiConsumer)} or
+     *          {@link #forEach(TriConsumer, Object)}.
+     * @throws UnsupportedOperationException if this collection has been 
{@linkplain #isFrozen() frozen}.
+     */
+    void putAll(final ReadOnlyStringMap source);
+
+    /**
+     * Puts the specified key-value pair into the collection.
+     *
+     * @param key the key to add or remove. Keys may be {@code null}.
+     * @param value the value to add. Values may be {@code null}.
+     * @throws java.util.ConcurrentModificationException some implementations 
may not support structural modifications
+     *          to this data structure while iterating over the contents with 
{@link #forEach(BiConsumer)} or
+     *          {@link #forEach(TriConsumer, Object)}.
+     * @throws UnsupportedOperationException if this collection has been 
{@linkplain #isFrozen() frozen}.
+     */
+    void putValue(final String key, final Object value);
+
+    /**
+     * Removes the key-value pair for the specified key from this data 
structure.
+     *
+     * @param key the key to remove. May be {@code null}.
+     * @throws java.util.ConcurrentModificationException some implementations 
may not support structural modifications
+     *          to this data structure while iterating over the contents with 
{@link #forEach(BiConsumer)} or
+     *          {@link #forEach(TriConsumer, Object)}.
+     * @throws UnsupportedOperationException if this collection has been 
{@linkplain #isFrozen() frozen}.
+     */
+    void remove(final String key);
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/800db9c7/log4j-api/src/main/java/org/apache/logging/log4j/util/TriConsumer.java
----------------------------------------------------------------------
diff --git 
a/log4j-api/src/main/java/org/apache/logging/log4j/util/TriConsumer.java 
b/log4j-api/src/main/java/org/apache/logging/log4j/util/TriConsumer.java
index 9b83eff..e9ba09e 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/TriConsumer.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/TriConsumer.java
@@ -22,7 +22,7 @@ package org.apache.logging.log4j.util;
  * @param <K> type of the first argument
  * @param <V> type of the second argument
  * @param <S> type of the third argument
- * @see ContextData
+ * @see ReadOnlyStringMap
  * @since 2.7
  */
 public interface TriConsumer<K, V, S> {

Reply via email to