You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucenenet.apache.org by sy...@apache.org on 2016/10/02 15:02:35 UTC

[09/10] lucenenet git commit: Fixed several CharArraySet/CharArrayMap bugs that were causing tests to fail, and re-enabled those tests. Refactored both classes to be more .NET-like.

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/87d05125/src/Lucene.Net.Analysis.Common/Analysis/Util/CharArrayMap.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Analysis.Common/Analysis/Util/CharArrayMap.cs b/src/Lucene.Net.Analysis.Common/Analysis/Util/CharArrayMap.cs
index 37d204f..64a650c 100644
--- a/src/Lucene.Net.Analysis.Common/Analysis/Util/CharArrayMap.cs
+++ b/src/Lucene.Net.Analysis.Common/Analysis/Util/CharArrayMap.cs
@@ -3,7 +3,9 @@ using Lucene.Net.Util;
 using System;
 using System.Collections;
 using System.Collections.Generic;
+using System.ComponentModel;
 using System.Diagnostics;
+using System.Globalization;
 using System.Linq;
 using System.Text;
 
@@ -27,45 +29,72 @@ namespace Lucene.Net.Analysis.Util
 	 */
 
     /// <summary>
-    /// A simple class that stores key Strings as char[]'s in a
+    /// A simple class that stores key <see cref="string"/>s as <see cref="char[]"/>'s in a
     /// hash table. Note that this is not a general purpose
     /// class.  For example, it cannot remove items from the
     /// map, nor does it resize its hash table to be smaller,
     /// etc.  It is designed to be quick to retrieve items
-    /// by char[] keys without the necessity of converting
-    /// to a String first.
+    /// by <see cref="char[]"/> keys without the necessity of converting
+    /// to a <see cref="string"/> first.
     /// 
     /// <a name="version"></a>
-    /// <para>You must specify the required <seealso cref="Version"/>
-    /// compatibility when creating <seealso cref="CharArrayMap"/>:
-    /// <ul>
-    ///   <li> As of 3.1, supplementary characters are
-    ///       properly lowercased.</li>
-    /// </ul>
+    /// <para>You must specify the required <see cref="LuceneVersion"/>
+    /// compatibility when creating <see cref="CharArrayMap"/>:
+    /// <list type="bullet">
+    ///   <item> As of 3.1, supplementary characters are
+    ///       properly lowercased.</item>
+    /// </list>
     /// Before 3.1 supplementary characters could not be
     /// lowercased correctly due to the lack of Unicode 4
     /// support in JDK 1.4. To use instances of
-    /// <seealso cref="CharArrayMap"/> with the behavior before Lucene
-    /// 3.1 pass a <seealso cref="Version"/> &lt; 3.1 to the constructors.
+    /// <see cref="CharArrayMap"/> with the behavior before Lucene
+    /// 3.1 pass a <see cref="LuceneVersion"/> &lt; 3.1 to the constructors.
     /// </para>
     /// </summary>
-    public class CharArrayMap<V> : IDictionary<object, V>
+    public class CharArrayMap<TValue> : ICharArrayMap, IDictionary<string, TValue>
     {
         // private only because missing generics
-        private static readonly CharArrayMap<V> EMPTY_MAP = new EmptyCharArrayMap<V>();
+        private static readonly CharArrayMap<TValue> EMPTY_MAP = new CharArrayMap.EmptyCharArrayMap<TValue>();
 
         private const int INIT_SIZE = 8;
         private readonly CharacterUtils charUtils;
         private readonly bool ignoreCase;
         private int count;
-        internal readonly LuceneVersion matchVersion; // package private because used in CharArraySet
+        private readonly LuceneVersion matchVersion; // package private because used in CharArraySet
         internal char[][] keys; // package private because used in CharArraySet's non Set-conform CharArraySetIterator
-        internal V[] values; // package private because used in CharArraySet's non Set-conform CharArraySetIterator
+        internal MapValue[] values; // package private because used in CharArraySet's non Set-conform CharArraySetIterator
 
         /// <summary>
-        /// LUCENENET: Added in .NET to prevent infinite recursion when accessing the Keys collection
+        /// LUCENENET: Moved this from CharArraySet so it doesn't need to know the generic type of CharArrayMap
         /// </summary>
-        private IDictionary<object, V> innerDictionary = new Dictionary<object, V>();
+        internal static readonly MapValue PLACEHOLDER = new MapValue();
+
+        /// <summary>
+        /// LUCENENET SPECIFIC type used to act as a placeholder. Since <c>null</c>
+        /// means that our value is not populated, we need an instance of something
+        /// to indicate it is. Using an instance of <see cref="V"/> would only work if
+        /// we could constrain it with the new() constraint, which isn't possible because
+        /// some types such as <see cref="string"/> don't have a default constructor.
+        /// So, this is a workaround that allows any type regardless of the type of constructor.
+        /// 
+        /// <para>
+        /// Note also that we gain the ability to use value types for <see cref="V"/>, but
+        /// also create a difference in behavior from Java Lucene where the actual values 
+        /// returned could be <c>null</c>.
+        /// </para>
+        /// </summary>
+        internal class MapValue
+        {
+            public TValue value = default(TValue);
+
+            public MapValue()
+            { }
+
+            public MapValue(TValue value)
+            {
+                this.value = value;
+            }
+        }
 
         /// <summary>
         /// Create map with enough capacity to hold startSize terms
@@ -76,8 +105,8 @@ namespace Lucene.Net.Analysis.Util
         /// <param name="startSize">
         ///          the initial capacity </param>
         /// <param name="ignoreCase">
-        ///          <code>false</code> if and only if the set should be case sensitive
-        ///          otherwise <code>true</code>. </param>
+        ///          <c>false</c> if and only if the set should be case sensitive;
+        ///          otherwise <c>true</c>. </param>
         public CharArrayMap(LuceneVersion matchVersion, int startSize, bool ignoreCase)
         {
             this.ignoreCase = ignoreCase;
@@ -87,7 +116,7 @@ namespace Lucene.Net.Analysis.Util
                 size <<= 1;
             }
             keys = new char[size][];
-            values = new V[size];
+            values = new MapValue[size];
             this.charUtils = CharacterUtils.GetInstance(matchVersion);
             this.matchVersion = matchVersion;
         }
@@ -99,11 +128,11 @@ namespace Lucene.Net.Analysis.Util
         ///          compatibility match version see <a href="#version">Version
         ///          note</a> above for details. </param>
         /// <param name="c">
-        ///          a map whose mappings to be copied </param>
+        ///          a map (<see cref="IDictionary{string, V}"/>) whose mappings to be copied </param>
         /// <param name="ignoreCase">
-        ///          <code>false</code> if and only if the set should be case sensitive
-        ///          otherwise <code>true</code>. </param>
-        public CharArrayMap(LuceneVersion matchVersion, IDictionary<object, V> c, bool ignoreCase)
+        ///          <c>false</c> if and only if the set should be case sensitive;
+        ///          otherwise <c>true</c>. </param>
+        public CharArrayMap(LuceneVersion matchVersion, IDictionary<string, TValue> c, bool ignoreCase)
             : this(matchVersion, c.Count, ignoreCase)
         {
             foreach (var v in c)
@@ -113,27 +142,9 @@ namespace Lucene.Net.Analysis.Util
         }
 
         /// <summary>
-        /// LUCENENET specific, in order to support the KeySet functionality.
+        /// Create set from the supplied map (used internally for readonly maps...)
         /// </summary>
-        /// <param name="keys"></param>
-        private CharArrayMap(char[][] keys, V[] values, bool ignoreCase, int count, CharacterUtils charUtils, LuceneVersion matchVersion, Dictionary<object, V> innerDictionary)
-        {
-            this.keys = keys;
-            this.values = values;
-            this.ignoreCase = ignoreCase;
-            this.count = count;
-            this.charUtils = charUtils;
-            this.matchVersion = matchVersion;
-
-            foreach (var kvp in innerDictionary)
-            {
-                this.innerDictionary[kvp.Key] = kvp.Value;
-            }
-        }
-
-        /// <summary>
-        /// Create set from the supplied map (used internally for readonly maps...) </summary>
-        private CharArrayMap(CharArrayMap<V> toCopy)
+        internal CharArrayMap(CharArrayMap<TValue> toCopy)
         {
             this.keys = toCopy.keys;
             this.values = toCopy.values;
@@ -141,59 +152,127 @@ namespace Lucene.Net.Analysis.Util
             this.count = toCopy.count;
             this.charUtils = toCopy.charUtils;
             this.matchVersion = toCopy.matchVersion;
-
-            foreach (var kvp in toCopy.innerDictionary)
-            {
-                this.innerDictionary[kvp.Key] = kvp.Value;
-            }
         }
 
-        public virtual void Add(KeyValuePair<object, V> item)
+        /// <summary>
+        /// Adds the <see cref="KeyValuePair{string, V}.Value"/> for the passed in <see cref="KeyValuePair{string, V}.Key"/>.
+        /// Note that the <see cref="KeyValuePair{string, V}"/> instance is not added to the dictionary.
+        /// </summary>
+        /// <param name="item">A <see cref="KeyValuePair{string, V}"/> whose <see cref="KeyValuePair{string, V}.Value"/> 
+        /// will be added for the corresponding <see cref="KeyValuePair{string, V}.Key"/>. </param>
+        public virtual void Add(KeyValuePair<string, TValue> item)
         {
-            Put(item.Key, item.Value);
+            Add(item.Key, item.Value);
         }
 
-        public virtual void Add(object key, V value)
+        /// <summary>
+        /// Adds the <paramref name="value"/> for the passed in <paramref name="key"/>.
+        /// </summary>
+        /// <param name="key">The string-able type to be added/updated in the dictionary.</param>
+        /// <param name="value">The corresponding value for the given <paramref name="key"/>.</param>
+        public virtual void Add(string key, TValue value)
         {
+            if (ContainsKey(key))
+            {
+                throw new ArgumentException("The key " + key + " already exists in the dictionary");
+            }
             Put(key, value);
         }
 
         /// <summary>
-        /// Clears all entries in this map. This method is supported for reusing, but not <seealso cref="Map#remove"/>. </summary>
+        /// Clears all entries in this map. This method is supported for reusing, but not 
+        /// <see cref="IDictionary{TKey, TValue}.Remove(TKey)"/>. 
+        /// </summary>
         public virtual void Clear()
         {
             count = 0;
             Arrays.Fill(keys, null);
-            Arrays.Fill(values, default(V));
-            innerDictionary.Clear();
+            Arrays.Fill(values, null);
+        }
+
+        /// <summary>
+        /// Not supported. 
+        /// </summary>
+        [Obsolete("Not applicable in this class.")]
+        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
+        public virtual bool Contains(KeyValuePair<string, TValue> item)
+        {
+            throw new NotSupportedException();
+        }
+
+        /// <summary>
+        /// Copies all items in the current dictionary the <paramref name="array"/> starting at the <see cref="arrayIndex"/>.
+        /// The array is assumed to already be dimensioned to fit the elements in this dictionary; otherwise a <see cref="ArgumentOutOfRangeException"/>
+        /// will be thrown.
+        /// </summary>
+        /// <param name="array">The array to copy the items into.</param>
+        /// <param name="arrayIndex">A 32-bit integer that represents the index in <see cref="array"/> at which copying begins.</param>
+        public virtual void CopyTo(KeyValuePair<string, TValue>[] array, int arrayIndex)
+        {
+            var iter = (EntryIterator)EntrySet().GetEnumerator();
+            for (int i = arrayIndex; iter.MoveNext(); i++)
+            {
+                array[i] = new KeyValuePair<string, TValue>(iter.Current.Key, iter.CurrentValue);
+            }
+        }
+
+        /// <summary>
+        /// Copies all items in the current <see cref="CharArrayMap{TValue}"/> to the passed in
+        /// <see cref="CharArrayMap{TValue}"/>.
+        /// </summary>
+        /// <param name="map"></param>
+        public virtual void CopyTo(CharArrayMap<TValue> map)
+        {
+            var iter = (EntryIterator)EntrySet().GetEnumerator();
+            while(iter.MoveNext())
+            {
+                map.Put(iter.Current.Key, iter.CurrentValue);
+            }
         }
 
-        public virtual bool Contains(KeyValuePair<object, V> item)
+        /// <summary>
+        /// <c>true</c> if the <paramref name="length"/> chars of <paramref name="text"/> starting at <paramref name="offset"/>
+        /// are in the <see cref="KeySet"/> 
+        /// </summary>
+        public virtual bool ContainsKey(char[] text, int offset, int length)
         {
-            throw new NotImplementedException();
+            return keys[GetSlot(text, offset, length)] != null;
         }
 
-        public virtual void CopyTo(KeyValuePair<object, V>[] array, int arrayIndex)
+        /// <summary>
+        /// <c>true</c> if the entire <see cref="KeySet"/> is the same as the 
+        /// <paramref name="text"/> <see cref="char[]"/> being passed in; 
+        /// otherwise <c>false</c>.
+        /// </summary>
+        public virtual bool ContainsKey(char[] text)
         {
-            throw new NotImplementedException();
+            return keys[GetSlot(text, 0, text.Length)] != null;
         }
 
         /// <summary>
-        /// true if the <code>len</code> chars of <code>text</code> starting at <code>off</code>
-        /// are in the <seealso cref="#keySet()"/> 
+        /// <c>true</c> if the <paramref name="text"/> <see cref="string"/> is in the <see cref="KeySet"/>; 
+        /// otherwise <c>false</c>
         /// </summary>
-        public virtual bool ContainsKey(char[] text, int off, int len)
+        public virtual bool ContainsKey(string text)
         {
-            return keys[GetSlot(text, off, len)] != null;
+            return keys[GetSlot(text)] != null;
         }
 
         /// <summary>
-        /// true if the <code>CharSequence</code> is in the <seealso cref="#keySet()"/> </summary>
-        public virtual bool ContainsKey(string cs)
+        /// <c>true</c> if the <paramref name="text"/> <see cref="ICharSequence"/> is in the <see cref="KeySet"/>; 
+        /// otherwise <c>false</c>
+        /// </summary>
+        public virtual bool ContainsKey(ICharSequence cs)
         {
             return keys[GetSlot(cs)] != null;
         }
 
+
+        /// <summary>
+        /// <c>true</c> if the <paramref name="o"/> <see cref="object.ToString()"/> is in the <see cref="KeySet"/>; 
+        /// otherwise <c>false</c>
+        /// </summary>
         public virtual bool ContainsKey(object o)
         {
             if (o == null)
@@ -211,37 +290,85 @@ namespace Lucene.Net.Analysis.Util
         }
 
         /// <summary>
-        /// returns the value of the mapping of <code>len</code> chars of <code>text</code>
-        /// starting at <code>off</code> 
+        /// returns the value of the mapping of <paramref name="length"/> chars of <paramref name="text"/>
+        /// starting at <paramref name="offset"/>
+        /// </summary>
+        public virtual TValue Get(char[] text, int offset, int length)
+        {
+            var value = values[GetSlot(text, offset, length)];
+            return (value != null) ? value.value : default(TValue);
+        }
+
+        /// <summary>
+        /// returns the value of the mapping of the chars inside this <paramref name="text"/>
         /// </summary>
-        public virtual V Get(char[] text, int off, int len)
+        public virtual TValue Get(char[] text)
         {
-            return values[GetSlot(text, off, len)];
+            var value = values[GetSlot(text, 0, text.Length)];
+            return (value != null) ? value.value : default(TValue);
         }
 
         /// <summary>
-        /// returns the value of the mapping of the chars inside this {@code CharSequence} </summary>
-        public virtual V Get(string cs)
+        /// returns the value of the mapping of the chars inside this <see cref="ICharSequence"/>
+        /// </summary>
+        public virtual TValue Get(ICharSequence cs)
+        {
+            var value = values[GetSlot(cs)];
+            return (value != null) ? value.value : default(TValue);
+        }
+
+        /// <summary>
+        /// returns the value of the mapping of the chars inside this <see cref="string"/>
+        /// </summary>
+        public virtual TValue Get(string cs)
         {
-            return values[GetSlot(cs)];
+            var value = values[GetSlot(cs)];
+            return (value != null) ? value.value : default(TValue);
         }
 
-        public virtual V Get(object o)
+        /// <summary>
+        /// returns the value of the mapping of the chars inside this <see cref="object.ToString()"/>
+        /// </summary>
+        public virtual TValue Get(object o)
         {
-            var text = o as char[];
-            if (text != null)
+            // LUCENENET NOTE: Testing for *is* is at least 10x faster
+            // than casting using *as* and then checking for null.
+            // http://stackoverflow.com/q/1583050/181087
+            if (o is char[])
             {
+                var text = o as char[];
                 return Get(text, 0, text.Length);
             }
             return Get(o.ToString());
         }
 
-        private int GetSlot(char[] text, int off, int len)
+        private int GetSlot(char[] text, int offset, int length)
+        {
+            int code = GetHashCode(text, offset, length);
+            int pos = code & (keys.Length - 1);
+            char[] text2 = keys[pos];
+            if (text2 != null && !Equals(text, offset, length, text2))
+            {
+                int inc = ((code >> 8) + code) | 1;
+                do
+                {
+                    code += inc;
+                    pos = code & (keys.Length - 1);
+                    text2 = keys[pos];
+                } while (text2 != null && !Equals(text, offset, length, text2));
+            }
+            return pos;
+        }
+
+        /// <summary>
+        /// Returns true if the <see cref="ICharSequence"/> is in the set
+        /// </summary>
+        private int GetSlot(ICharSequence text)
         {
-            int code = GetHashCode(text, off, len);
+            int code = GetHashCode(text);
             int pos = code & (keys.Length - 1);
             char[] text2 = keys[pos];
-            if (text2 != null && !Equals(text, off, len, text2))
+            if (text2 != null && !Equals(text, text2))
             {
                 int inc = ((code >> 8) + code) | 1;
                 do
@@ -249,13 +376,14 @@ namespace Lucene.Net.Analysis.Util
                     code += inc;
                     pos = code & (keys.Length - 1);
                     text2 = keys[pos];
-                } while (text2 != null && !Equals(text, off, len, text2));
+                } while (text2 != null && !Equals(text, text2));
             }
             return pos;
         }
 
         /// <summary>
-        /// Returns true if the String is in the set </summary>
+        /// Returns true if the <see cref="string"/> is in the set
+        /// </summary>
         private int GetSlot(string text)
         {
             int code = GetHashCode(text);
@@ -275,27 +403,31 @@ namespace Lucene.Net.Analysis.Util
         }
 
         /// <summary>
-        /// Add the given mapping. </summary>
-        public virtual V Put(ICharSequence text, V value)
+        /// Add the given mapping.
+        /// </summary>
+        public virtual TValue Put(ICharSequence text, TValue value)
         {
-            return Put(text.ToString(), value); // could be more efficient
+            MapValue oldValue = PutImpl(text, new MapValue(value)); // could be more efficient
+            return (oldValue != null) ? oldValue.value : default(TValue);
         }
 
-        public virtual V Put(object o, V value)
+        /// <summary>
+        /// Add the given mapping using the <see cref="object.ToString()"/> representation
+        /// of <paramref name="o"/> in the <see cref="CultureInfo.InvariantCulture"/>.
+        /// </summary>
+        public virtual TValue Put(object o, TValue value)
         {
-            var c = o as char[];
-            if (c != null)
-            {
-                return Put(c, value);
-            }
-            return Put(o.ToString(), value);
+            MapValue oldValue = PutImpl(o, new MapValue(value));
+            return (oldValue != null) ? oldValue.value : default(TValue);
         }
 
         /// <summary>
-        /// Add the given mapping. </summary>
-        public virtual V Put(string text, V value)
+        /// Add the given mapping.
+        /// </summary>
+        public virtual TValue Put(string text, TValue value)
         {
-            return Put(text.ToCharArray(), value);
+            MapValue oldValue = PutImpl(text, new MapValue(value));
+            return (oldValue != null) ? oldValue.value : default(TValue);
         }
 
         /// <summary>
@@ -303,7 +435,60 @@ namespace Lucene.Net.Analysis.Util
         /// If ignoreCase is true for this Set, the text array will be directly modified.
         /// The user should never modify this text array after calling this method.
         /// </summary>
-        public virtual V Put(char[] text, V value)
+        public virtual TValue Put(char[] text, TValue value)
+        {
+            MapValue oldValue = PutImpl(text, new MapValue(value));
+            return (oldValue != null) ? oldValue.value : default(TValue);
+        }
+
+        /// <summary>
+        /// Add the given mapping.
+        /// </summary>
+        private MapValue PutImpl(ICharSequence text, MapValue value)
+        {
+            return PutImpl(text.ToString(), value); // could be more efficient
+        }
+
+        private MapValue PutImpl(object o, MapValue value)
+        {
+            // LUCENENET NOTE: Testing for *is* is at least 10x faster
+            // than casting using *as* and then checking for null.
+            // http://stackoverflow.com/q/1583050/181087 
+            if (o is char[])
+            {
+                var c = o as char[];
+                return PutImpl(c, value);
+            }
+            // LUCENENET: We need value types to be represented using the invariant
+            // culture, so it is consistent regardless of the current culture. 
+            // It's easy to work out if this is a value type, but difficult
+            // to get to the ToString(IFormatProvider) overload of the type without
+            // a lot of special cases. It's easier just to change the culture of the 
+            // thread before calling ToString(), but we don't want that behavior to
+            // bleed into PutImpl.
+            string s;
+            using (var context = new CultureContext(CultureInfo.InvariantCulture))
+            {
+                s = o.ToString();
+            }
+            return PutImpl(s, value);
+        }
+
+        /// <summary>
+        /// Add the given mapping.
+        /// </summary>
+        private MapValue PutImpl(string text, MapValue value)
+        {
+            return PutImpl(text.ToCharArray(), value);
+        }
+
+        /// <summary>
+        /// LUCENENET specific. Centralizes the logic between <see cref="Put"/>
+        /// implementations that accept a value and those that don't. This value is
+        /// so we know whether or not the value was set, since we can't reliably do
+        /// a check for <c>null</c> on the <see cref="V"/> type.
+        /// </summary>
+        private MapValue PutImpl(char[] text, MapValue value)
         {
             if (ignoreCase)
             {
@@ -312,7 +497,7 @@ namespace Lucene.Net.Analysis.Util
             int slot = GetSlot(text, 0, text.Length);
             if (keys[slot] != null)
             {
-                V oldValue = values[slot];
+                MapValue oldValue = values[slot];
                 values[slot] = value;
                 return oldValue;
             }
@@ -325,22 +510,125 @@ namespace Lucene.Net.Analysis.Util
                 Rehash();
             }
 
-            innerDictionary[text] = value;
+            return null;
+        }
+
+        #region PutAll
+
+        /// <summary>
+        /// This implementation enumerates over the specified <see cref="IDictionary{char[],TValue}"/>'s
+        /// entries, and calls this map's <see cref="Put(char[], TValue)"/> operation once for each entry.
+        /// </summary>
+        /// <param name="collection">A dictionary of values to add/update in the current map.</param>
+        public virtual void PutAll(IDictionary<char[], TValue> collection)
+        {
+            foreach (var kvp in collection)
+            {
+                Put(kvp.Key, kvp.Value);
+            }
+        }
+
+        /// <summary>
+        /// This implementation enumerates over the specified <see cref="IDictionary{string,TValue}"/>'s
+        /// entries, and calls this map's <see cref="Put(string, TValue)"/> operation once for each entry.
+        /// </summary>
+        /// <param name="collection">A dictionary of values to add/update in the current map.</param>
+        public virtual void PutAll(IDictionary<string, TValue> collection)
+        {
+            foreach (var kvp in collection)
+            {
+                Put(kvp.Key, kvp.Value);
+            }
+        }
 
-            return default(V);
+        /// <summary>
+        /// This implementation enumerates over the specified <see cref="IDictionary{ICharSequence,TValue}"/>'s
+        /// entries, and calls this map's <see cref="Put(ICharSequence, TValue)"/> operation once for each entry.
+        /// </summary>
+        /// <param name="collection">A dictionary of values to add/update in the current map.</param>
+        public virtual void PutAll(IDictionary<ICharSequence, TValue> collection)
+        {
+            foreach (var kvp in collection)
+            {
+                Put(kvp.Key, kvp.Value);
+            }
         }
 
-        private void Rehash()
+        /// <summary>
+        /// This implementation enumerates over the specified <see cref="IDictionary{object,TValue}"/>'s
+        /// entries, and calls this map's <see cref="Put(object, TValue)"/> operation once for each entry.
+        /// </summary>
+        /// <param name="collection">A dictionary of values to add/update in the current map.</param>
+        public virtual void PutAll(IDictionary<object, TValue> collection)
         {
-            Debug.Assert(keys.Length == values.Length);
+            foreach (var kvp in collection)
+            {
+                Put(kvp.Key, kvp.Value);
+            }
+        }
+
+        /// <summary>
+        /// This implementation enumerates over the specified <see cref="IEnumerable{KeyValuePair{char[],TValue}}"/>'s
+        /// entries, and calls this map's <see cref="Put(char[], TValue)"/> operation once for each entry.
+        /// </summary>
+        /// <param name="collection">The values to add/update in the current map.</param>
+        public virtual void PutAll(IEnumerable<KeyValuePair<char[], TValue>> collection)
+        {
+            foreach (var kvp in collection)
+            {
+                Put(kvp.Key, kvp.Value);
+            }
+        }
+
+        /// <summary>
+        /// This implementation enumerates over the specified <see cref="IEnumerable{KeyValuePair{string,TValue}}"/>'s
+        /// entries, and calls this map's <see cref="Put(string, TValue)"/> operation once for each entry.
+        /// </summary>
+        /// <param name="collection">The values to add/update in the current map.</param>
+        public virtual void PutAll(IEnumerable<KeyValuePair<string, TValue>> collection)
+        {
+            foreach (var kvp in collection)
+            {
+                Put(kvp.Key, kvp.Value);
+            }
+        }
+
+        /// <summary>
+        /// This implementation enumerates over the specified <see cref="IEnumerable{KeyValuePair{ICharSequence,TValue}}"/>'s
+        /// entries, and calls this map's <see cref="Put(ICharSequence, TValue)"/> operation once for each entry.
+        /// </summary>
+        /// <param name="collection">The values to add/update in the current map.</param>
+        public virtual void PutAll(IEnumerable<KeyValuePair<ICharSequence, TValue>> collection)
+        {
+            foreach (var kvp in collection)
+            {
+                Put(kvp.Key, kvp.Value);
+            }
+        }
+
+        /// <summary>
+        /// This implementation enumerates over the specified <see cref="IEnumerable{KeyValuePair{object,TValue}}"/>'s
+        /// entries, and calls this map's <see cref="Put(object, TValue)"/> operation once for each entry.
+        /// </summary>
+        /// <param name="collection">The values to add/update in the current map.</param>
+        public virtual void PutAll(IEnumerable<KeyValuePair<object, TValue>> collection)
+        {
+            foreach (var kvp in collection)
+            {
+                Put(kvp.Key, kvp.Value);
+            }
+        }
 
-            innerDictionary.Clear();
+        #endregion
 
+        private void Rehash()
+        {
+            Debug.Assert(keys.Length == values.Length);
             int newSize = 2 * keys.Length;
             char[][] oldkeys = keys;
-            V[] oldvalues = values;
+            MapValue[] oldvalues = values;
             keys = new char[newSize][];
-            values = new V[newSize];
+            values = new MapValue[newSize];
 
             for (int i = 0; i < oldkeys.Length; i++)
             {
@@ -351,24 +639,54 @@ namespace Lucene.Net.Analysis.Util
                     int slot = GetSlot(text, 0, text.Length);
                     keys[slot] = text;
                     values[slot] = oldvalues[i];
+                }
+            }
+        }
 
-                    innerDictionary.Add(text, oldvalues[i]);
+        private bool Equals(char[] text1, int offset, int length, char[] text2)
+        {
+            if (length != text2.Length)
+            {
+                return false;
+            }
+            int limit = offset + length;
+            if (ignoreCase)
+            {
+                for (int i = 0; i < length;)
+                {
+                    var codePointAt = charUtils.CodePointAt(text1, offset + i, limit);
+                    if (Character.ToLowerCase(codePointAt) != charUtils.CodePointAt(text2, i, text2.Length))
+                    {
+                        return false;
+                    }
+                    i += Character.CharCount(codePointAt);
+                }
+            }
+            else
+            {
+                for (int i = 0; i < length; i++)
+                {
+                    if (text1[offset + i] != text2[i])
+                    {
+                        return false;
+                    }
                 }
             }
+            return true;
         }
 
-        private bool Equals(char[] text1, int off, int len, char[] text2)
+        private bool Equals(ICharSequence text1, char[] text2)
         {
-            if (len != text2.Length)
+            int length = text1.Length;
+            if (length != text2.Length)
             {
                 return false;
             }
-            int limit = off + len;
             if (ignoreCase)
             {
-                for (int i = 0; i < len;)
+                for (int i = 0; i < length;)
                 {
-                    var codePointAt = charUtils.CodePointAt(text1, off + i, limit);
+                    int codePointAt = charUtils.CodePointAt(text1, i);
                     if (Character.ToLowerCase(codePointAt) != charUtils.CodePointAt(text2, i, text2.Length))
                     {
                         return false;
@@ -378,9 +696,9 @@ namespace Lucene.Net.Analysis.Util
             }
             else
             {
-                for (int i = 0; i < len; i++)
+                for (int i = 0; i < length; i++)
                 {
-                    if (text1[off + i] != text2[i])
+                    if (text1.CharAt(i) != text2[i])
                     {
                         return false;
                     }
@@ -391,14 +709,14 @@ namespace Lucene.Net.Analysis.Util
 
         private bool Equals(string text1, char[] text2)
         {
-            int len = text1.Length;
-            if (len != text2.Length)
+            int length = text1.Length;
+            if (length != text2.Length)
             {
                 return false;
             }
             if (ignoreCase)
             {
-                for (int i = 0; i < len;)
+                for (int i = 0; i < length;)
                 {
                     int codePointAt = charUtils.CodePointAt(text1, i);
                     if (Character.ToLowerCase(codePointAt) != charUtils.CodePointAt(text2, i, text2.Length))
@@ -410,7 +728,7 @@ namespace Lucene.Net.Analysis.Util
             }
             else
             {
-                for (int i = 0; i < len; i++)
+                for (int i = 0; i < length; i++)
                 {
                     if (text1[i] != text2[i])
                     {
@@ -421,14 +739,60 @@ namespace Lucene.Net.Analysis.Util
             return true;
         }
 
-        private int GetHashCode(char[] text, int offset, int len)
+        /// <summary>
+        /// LUCENENET Specific - test for value equality similar to how it is done in Java
+        /// </summary>
+        /// <param name="obj">Another dictionary to test the values of</param>
+        /// <returns><c>true</c> if the given object is an <see cref="IDictionary{object, V}"/> that contains
+        /// the same key value pairs as the current map</returns>
+        public override bool Equals(object obj)
+        {
+            var other = obj as IDictionary<string, TValue>;
+            if (other == null)
+                return false;
+
+            if (this.Count != other.Count)
+                return false;
+
+            var iter = other.GetEnumerator();
+            while (iter.MoveNext())
+            {
+                if (!this.ContainsKey(iter.Current.Key))
+                    return false;
+
+                if (!EqualityComparer<TValue>.Default.Equals(this[iter.Current.Key], iter.Current.Value))
+                    return false;
+            }
+
+            return true;
+        }
+
+        /// <summary>
+        /// LUCENENET Specific - override required by .NET because we override Equals
+        /// to simulate Java's value equality checking.
+        /// </summary>
+        /// <returns></returns>
+        public override int GetHashCode()
+        {
+            const int PRIME = 31; // arbitrary prime
+            int hash = PRIME; 
+            var iter = (EntryIterator)EntrySet().GetEnumerator();
+            while (iter.MoveNext())
+            {
+                hash = (hash * PRIME) ^ iter.Current.Key.GetHashCode();
+                hash = (hash * PRIME) ^ iter.Current.Value.GetHashCode();
+            }
+            return hash;
+        }
+
+        private int GetHashCode(char[] text, int offset, int length)
         {
             if (text == null)
             {
                 throw new ArgumentException("text can't be null", "text");
             }
             int code = 0;
-            int stop = offset + len;
+            int stop = offset + length;
             if (ignoreCase)
             {
                 for (int i = offset; i < stop;)
@@ -448,6 +812,34 @@ namespace Lucene.Net.Analysis.Util
             return code;
         }
 
+        private int GetHashCode(ICharSequence text)
+        {
+            if (text == null)
+            {
+                throw new ArgumentException("text can't be null", "text");
+            }
+
+            int code = 0;
+            int length = text.Length;
+            if (ignoreCase)
+            {
+                for (int i = 0; i < length;)
+                {
+                    int codePointAt = charUtils.CodePointAt(text, i);
+                    code = code * 31 + Character.ToLowerCase(codePointAt);
+                    i += Character.CharCount(codePointAt);
+                }
+            }
+            else
+            {
+                for (int i = 0; i < length; i++)
+                {
+                    code = code * 31 + text.CharAt(i);
+                }
+            }
+            return code;
+        }
+
         private int GetHashCode(string text)
         {
             if (text == null)
@@ -456,10 +848,10 @@ namespace Lucene.Net.Analysis.Util
             }
 
             int code = 0;
-            int len = text.Length;
+            int length = text.Length;
             if (ignoreCase)
             {
-                for (int i = 0; i < len;)
+                for (int i = 0; i < length;)
                 {
                     int codePointAt = charUtils.CodePointAt(text, i);
                     code = code * 31 + Character.ToLowerCase(codePointAt);
@@ -468,7 +860,7 @@ namespace Lucene.Net.Analysis.Util
             }
             else
             {
-                for (int i = 0; i < len; i++)
+                for (int i = 0; i < length; i++)
                 {
                     code = code * 31 + text[i];
                 }
@@ -478,664 +870,2075 @@ namespace Lucene.Net.Analysis.Util
 
         #region For .NET Support LUCENENET
 
-        public virtual bool TryGetValue(object key, out V value)
+        /// <summary>
+        /// The Lucene version corresponding to the compatibility behavior 
+        /// that this instance emulates
+        /// </summary>
+        public virtual LuceneVersion MatchVersion
         {
-            throw new NotImplementedException();
+            get { return matchVersion; }
         }
 
-        public virtual V this[object key]
-        {
-            get { return Get(key); }
-            set { Put(key, value); }
+        /// <summary>
+        /// Adds a placeholder with the given <paramref name="text"/> as the key.
+        /// Primarily for internal use by <see cref="CharArraySet"/>.
+        /// </summary>
+        public virtual bool Put(char[] text)
+        {
+            return PutImpl(text, PLACEHOLDER) == null;
         }
 
-        public virtual ICollection<object> Keys { get { return KeySet(); } }
-
-        public ICollection<V> Values { get { return values; } }
-
-        #endregion
-
-        public virtual bool IsReadOnly { get; private set; }
-
-        public virtual IEnumerator<KeyValuePair<object, V>> GetEnumerator()
+        /// <summary>
+        /// Adds a placeholder with the given <paramref name="text"/> as the key.
+        /// Primarily for internal use by <see cref="CharArraySet"/>.
+        /// </summary>
+        public virtual bool Put(ICharSequence text)
         {
-            return new EntryIterator(this, false);
+            return PutImpl(text, PLACEHOLDER) == null;
         }
 
-        IEnumerator IEnumerable.GetEnumerator()
+        /// <summary>
+        /// Adds a placeholder with the given <paramref name="text"/> as the key.
+        /// Primarily for internal use by <see cref="CharArraySet"/>.
+        /// </summary>
+        public virtual bool Put(string text)
         {
-            return GetEnumerator();
+            return PutImpl(text, PLACEHOLDER) == null;
         }
 
-
-        public virtual bool Remove(object key)
+        /// <summary>
+        /// Adds a placeholder with the given <paramref name="o"/> as the key.
+        /// Primarily for internal use by <see cref="CharArraySet"/>.
+        /// </summary>
+        public virtual bool Put(object o)
         {
-            throw new NotSupportedException();
+            return PutImpl(o, PLACEHOLDER) == null;
         }
 
-        public virtual bool Remove(KeyValuePair<object, V> item)
+        /// <summary>
+        /// Gets the value associated with the specified key.
+        /// </summary>
+        /// <param name="key">The key of the value to get.</param>
+        /// <param name="offset">The position of the <paramref name="key"/> where the target key begins.</param>
+        /// <param name="length">The total length of the <paramref name="key"/>.</param>
+        /// <param name="value">When this method returns, contains the value associated with the specified key, 
+        /// if the key is found; otherwise, the default value for the type of the value parameter. 
+        /// This parameter is passed uninitialized.</param>
+        /// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
+        public virtual bool TryGetValue(char[] key, int offset, int length, out TValue value)
         {
-            throw new NotSupportedException();
+            var val = values[GetSlot(key, offset, length)];
+            if (val != null)
+            {
+                value = val.value;
+                return true;
+            }
+            value = default(TValue);
+            return false;
         }
 
-        public virtual int Count
+        /// <summary>
+        /// Gets the value associated with the specified key.
+        /// </summary>
+        /// <param name="key">The key of the value to get.</param>
+        /// <param name="value">When this method returns, contains the value associated with the specified key, 
+        /// if the key is found; otherwise, the default value for the type of the value parameter. 
+        /// This parameter is passed uninitialized.</param>
+        /// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
+        public virtual bool TryGetValue(char[] key, out TValue value)
         {
-            get { return count; }
+            var val = values[GetSlot(key, 0, key.Length)];
+            if (val != null)
+            {
+                value = val.value;
+                return true;
+            }
+            value = default(TValue);
+            return false;
         }
 
-        public override string ToString()
+        /// <summary>
+        /// Gets the value associated with the specified key.
+        /// </summary>
+        /// <param name="key">The key of the value to get.</param>
+        /// <param name="value">When this method returns, contains the value associated with the specified key, 
+        /// if the key is found; otherwise, the default value for the type of the value parameter. 
+        /// This parameter is passed uninitialized.</param>
+        /// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
+        public virtual bool TryGetValue(ICharSequence key, out TValue value)
         {
-            var sb = new StringBuilder("{");
-
-            IEnumerator<KeyValuePair<object, V>> iter1 = IDictionaryExtensions.EntrySet(this).GetEnumerator();
-            while (iter1.MoveNext())
+            var val = values[GetSlot(key)];
+            if (val != null)
             {
-                KeyValuePair<object, V> entry = iter1.Current;
-                if (sb.Length > 1)
-                {
-                    sb.Append(", ");
-                }
-                if (entry.Key.GetType().Equals(typeof(char[])))
-                {
-                    sb.Append(new string((char[])entry.Key));
-                }
-                else
-                {
-                    sb.Append(entry.Key);
-                }
-                sb.Append("=");
-                sb.Append(entry.Value);
+                value = val.value;
+                return true;
             }
-
-            return sb.Append('}').ToString();
+            value = default(TValue);
+            return false;
         }
 
-        private EntrySet_ entrySet = null;
-        private CharArraySet keySet = null;
-
-        internal virtual EntrySet_ CreateEntrySet()
+        /// <summary>
+        /// Gets the value associated with the specified key.
+        /// </summary>
+        /// <param name="key">The key of the value to get.</param>
+        /// <param name="value">When this method returns, contains the value associated with the specified key, 
+        /// if the key is found; otherwise, the default value for the type of the value parameter. 
+        /// This parameter is passed uninitialized.</param>
+        /// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
+        public virtual bool TryGetValue(string key, out TValue value)
         {
-            return new EntrySet_(this, true);
+            var val = values[GetSlot(key)];
+            if (val != null)
+            {
+                value = val.value;
+                return true;
+            }
+            value = default(TValue);
+            return false;
         }
 
-        public EntrySet_ EntrySet()
+        /// <summary>
+        /// Gets the value associated with the specified key.
+        /// </summary>
+        /// <param name="key">The key of the value to get.</param>
+        /// <param name="value">When this method returns, contains the value associated with the specified key, 
+        /// if the key is found; otherwise, the default value for the type of the value parameter. 
+        /// This parameter is passed uninitialized.</param>
+        /// <returns><c>true</c> if the <see cref="CharArrayMap{TValue}"/> contains an element with the specified key; otherwise, <c>false</c>.</returns>
+        public virtual bool TryGetValue(object key, out TValue value)
         {
-            if (entrySet == null)
+            // LUCENENET NOTE: Testing for *is* is at least 10x faster
+            // than casting using *as* and then checking for null.
+            // http://stackoverflow.com/q/1583050/181087
+            if (key is char[])
             {
-                entrySet = CreateEntrySet();
+                var text = key as char[];
+                return TryGetValue(text, 0, text.Length, out value);
             }
-            return entrySet;
+            return TryGetValue(key.ToString(), out value);
         }
 
-        // helper for CharArraySet to not produce endless recursion
-        internal IEnumerable<object> OriginalKeySet()
+        /// <summary>
+        /// Gets or sets the value associated with the specified key.
+        /// </summary>
+        /// <param name="key">The key of the value to get or set.</param>
+        /// <param name="offset">The position of the <paramref name="key"/> where the target key begins.</param>
+        /// <param name="length">The total length of the <paramref name="key"/>.</param>
+        public virtual TValue this[char[] key, int offset, int length]
         {
-            return innerDictionary.Keys;
+            get { return Get(key, offset, length); }
+            set { Put(key, value); }
         }
 
         /// <summary>
-        /// Returns an <seealso cref="CharArraySet"/> view on the map's keys.
-        /// The set will use the same {@code matchVersion} as this map. 
+        /// Gets or sets the value associated with the specified key.
         /// </summary>
-        private CharArraySet KeySet()
+        /// <param name="key">The key of the value to get or set.</param>
+        public virtual TValue this[char[] key]
         {
-            if (keySet == null)
-            {
-                // prevent adding of entries
+            get { return Get(key); }
+            set { Put(key, value); }
+        }
 
-                // LUCENENET TODO: Should find a way to pass a direct reference to this object to
-                // CharArraySet's constructor. The only known side effect this causes is to make
-                // the ToString test fail when calling it on the Keys property after changing
-                // the main set, so perhaps this is not a huge issue.
+        /// <summary>
+        /// Gets or sets the value associated with the specified key.
+        /// </summary>
+        /// <param name="key">The key of the value to get or set.</param>
+        public virtual TValue this[ICharSequence key]
+        {
+            get { return Get(key); }
+            set { Put(key, value); }
+        }
 
-                // Then again, any derived classes of this one could have serious issues without
-                // the direct reference.
+        /// <summary>
+        /// Gets or sets the value associated with the specified key.
+        /// </summary>
+        /// <param name="key">The key of the value to get or set.</param>
+        public virtual TValue this[string key]
+        {
+            get { return Get(key); }
+            set { Put(key, value); }
+        }
 
-                // A possible solution is to create an interface with no generics that this class 
-                // implements to be passed into the CharArraySet's constructor. Another possible 
-                // solution is to make the CharArraySet class generic so the inner type can be 
-                // inferred at compile time.
-                var innerDictionaryObj = new Dictionary<object, object>();
-                foreach (var kvp in this.innerDictionary)
-                {
-                    innerDictionaryObj.Add(kvp.Key, kvp.Value);
-                }
-                var copy = new CharArrayMap<object>(this.keys, this.values.Cast<object>().ToArray(), 
-                    this.ignoreCase, this.count, this.charUtils, this.matchVersion, innerDictionaryObj);
-                keySet = new UnmodifiableCharArraySet(copy);
-            }
-            return keySet;
+        /// <summary>
+        /// Gets or sets the value associated with the specified key.
+        /// </summary>
+        /// <param name="key">The key of the value to get or set.</param>
+        public virtual TValue this[object key]
+        {
+            get { return Get(key); }
+            set { Put(key, value); }
         }
 
-        private sealed class UnmodifiableCharArraySet : CharArraySet
+        /// <summary>
+        /// Gets a collection containing the keys in the <see cref="CharArrayMap{TValue}"/>.
+        /// </summary>
+        public virtual ICollection<string> Keys
         {
-            internal UnmodifiableCharArraySet(CharArrayMap<object> map) : base(map)
-            {
-            }
+            get { return KeySet; }
+        }
 
-            public override bool Add(object o)
-            {
-                throw new NotSupportedException();
-            }
-            public bool Add(ICharSequence text)
-            {
-                throw new NotSupportedException();
-            }
-            public override bool Add(string text)
-            {
-                throw new NotSupportedException();
-            }
-            public override bool Add(char[] text)
-            {
-                throw new NotSupportedException();
-            }
-            public override void Clear()
-            {
-                throw new NotSupportedException();
-            }
-            public override bool Remove(object item)
+
+        private volatile ICollection<TValue> valueSet;
+
+        /// <summary>
+        /// Gets a collection containing the values in the <see cref="CharArrayMap{TValue}"/>.
+        /// This specialized collection can be enumerated in order to read its values and 
+        /// overrides <see cref="object.ToString()"/> in order to display a string 
+        /// representation of the values.
+        /// </summary>
+        public ICollection<TValue> Values
+        {
+            get
             {
-                throw new NotSupportedException();
+                if (valueSet == null)
+                {
+                    valueSet = new ValueCollection(this);
+                }
+                return valueSet;
             }
         }
 
         /// <summary>
-        /// public iterator class so efficient methods are exposed to users
+        /// LUCENENET specific class used to break the infinite recursion when the
+        /// CharArraySet iterates the keys of this dictionary via <see cref="OriginalKeySet"/>. 
+        /// In Java, the keyset of the abstract base class was used to break the infinite recursion, 
+        /// however this class doesn't have an abstract base class so that is not an option. 
+        /// This class is just a facade around the keys (not another collection of keys), so it 
+        /// doesn't consume any additional RAM while providing another "virtual" collection to iterate over.
         /// </summary>
-        public class EntryIterator : IEnumerator<KeyValuePair<object, V>>
+        internal class KeyCollection : ICollection<string>
         {
-            private readonly CharArrayMap<V> outerInstance;
-
-            internal int pos = -1;
-            internal int lastPos;
-            internal readonly bool allowModify;
+            private readonly CharArrayMap<TValue> outerInstance;
 
-            internal EntryIterator(CharArrayMap<V> outerInstance, bool allowModify)
+            public KeyCollection(CharArrayMap<TValue> outerInstance)
             {
                 this.outerInstance = outerInstance;
-                this.allowModify = allowModify;
-                GoNext();
             }
 
-            internal void GoNext()
+            public int Count
             {
-                lastPos = pos;
-                pos++;
-                while (pos < outerInstance.keys.Length && outerInstance.keys[pos] == null)
+                get
                 {
-                    pos++;
+                    return outerInstance.Count;
                 }
             }
 
-            public virtual bool HasNext()
+            public bool IsReadOnly
             {
-                return pos < outerInstance.keys.Length;
+                get
+                {
+                    return outerInstance.IsReadOnly;
+                }
             }
 
-            /// <summary>
-            /// gets the next key... do not modify the returned char[] </summary>
-            public virtual char[] NextKey()
+            public void Add(string item)
             {
-                GoNext();
-                return outerInstance.keys[lastPos];
+                throw new NotSupportedException();
             }
 
-            /// <summary>
-            /// gets the next key as a newly created String object </summary>
-            public virtual string NextKeyString()
+            public void Clear()
             {
-                return new string(NextKey());
+                outerInstance.Clear();
             }
 
-            /// <summary>
-            /// returns the value associated with the last key returned </summary>
-            public virtual V CurrentValue()
+            public bool Contains(string item)
             {
-                return outerInstance.values[lastPos];
+                return outerInstance.ContainsKey(item);
             }
 
-            /// <summary>
-            /// sets the value associated with the last key returned </summary>
-            public virtual V SetValue(V value)
+            public void CopyTo(string[] array, int arrayIndex)
             {
-                if (!allowModify)
+                var iter = GetEnumerator();
+                for (int i = arrayIndex; iter.MoveNext(); i++)
                 {
-                    throw new System.NotSupportedException();
+                    array[i] = iter.Current;
                 }
-                V old = outerInstance.values[lastPos];
-                outerInstance.values[lastPos] = value;
-                return old;
             }
 
-            /// <summary>
-            /// use nextCharArray() + currentValue() for better efficiency.
-            /// </summary>
-            //public virtual KeyValuePair<object, V> Next()
-            //{
-            //    GoNext();
-            //    //return new KeyValuePair<object, V>();
-            //    return new MapEntry(outerInstance, lastPos, allowModify);
-            //}
-
-            public virtual void Remove()
+            public IEnumerator<string> GetEnumerator()
             {
-                throw new NotSupportedException();
+                return new KeyEnumerator(outerInstance);
             }
 
-            #region Added for better .NET support LUCENENET
-            public virtual void Dispose()
+            public bool Remove(string item)
             {
+                throw new NotSupportedException();
             }
 
-            public virtual bool MoveNext()
+            IEnumerator IEnumerable.GetEnumerator()
             {
-                if (!HasNext()) return false;
-                GoNext();
-                return true;
+                return GetEnumerator();
             }
 
-            public virtual void Reset()
+            /// <summary>
+            /// LUCENENET specific class to iterate the values in the <see cref="KeyCollection"/>.
+            /// </summary>
+            private class KeyEnumerator : IEnumerator<string>
             {
-                pos = -1;
-                GoNext();
-            }
+                private readonly CharArrayMap<TValue> outerInstance;
+                private readonly EntryIterator entryIterator;
 
-            public virtual KeyValuePair<object, V> Current { get { return new KeyValuePair<object, V>(outerInstance.keys[lastPos], outerInstance.values[lastPos]); } private set { } }
+                public KeyEnumerator(CharArrayMap<TValue> outerInstance)
+                {
+                    this.outerInstance = outerInstance;
+                    this.entryIterator = new EntryIterator(outerInstance, !outerInstance.IsReadOnly);
+                }
 
-            object IEnumerator.Current
-            {
-                get { return CurrentValue(); }
-            }
+                public string Current
+                {
+                    get
+                    {
+                        return entryIterator.Current.Key;
+                    }
+                }
 
-            #endregion
-        }
+                object IEnumerator.Current
+                {
+                    get
+                    {
+                        return Current;
+                    }
+                }
 
-        // LUCENENET NOTE: The Java Lucene type MapEntry was removed here because it is not possible 
-        // to inherit the value type KeyValuePair in .NET.
+                public void Dispose()
+                {
+                    // nothing to do
+                }
+
+                public bool MoveNext()
+                {
+                    return entryIterator.MoveNext();
+                }
+
+                public void Reset()
+                {
+                    entryIterator.Reset();
+                }
+            }
+        }
 
         /// <summary>
-        /// public EntrySet_ class so efficient methods are exposed to users
-        /// 
-        /// NOTE: In .NET this was renamed to EntrySet_ because it conflicted with the
-        /// method EntrySet(). Since there is also an extension method named IDictionary<K,V>.EntrySet()
-        /// that this class needs to override, changing the name of the method was not
-        /// possible because the extension method would produce incorrect results if it were
-        /// inadvertently called.
+        /// LUCENENET specific class that represents the values in the <see cref="CharArrayMap{TValue}"/>.
         /// </summary>
-        public sealed class EntrySet_ : ISet<KeyValuePair<object, V>>
+        internal class ValueCollection : ICollection<TValue>
         {
-            private readonly CharArrayMap<V> outerInstance;
-
-            internal readonly bool allowModify;
+            private readonly CharArrayMap<TValue> outerInstance;
 
-            internal EntrySet_(CharArrayMap<V> outerInstance, bool allowModify)
+            public ValueCollection(CharArrayMap<TValue> outerInstance)
             {
                 this.outerInstance = outerInstance;
-                this.allowModify = allowModify;
-            }
-
-            public IEnumerator GetEnumerator()
-            {
-                return new EntryIterator(outerInstance, allowModify);
             }
 
-            IEnumerator<KeyValuePair<object, V>> IEnumerable<KeyValuePair<object, V>>.GetEnumerator()
+            public int Count
             {
-                return (IEnumerator<KeyValuePair<object, V>>)GetEnumerator();
+                get
+                {
+                    return outerInstance.Count;
+                }
             }
 
-            public bool Contains(object o)
+            public bool IsReadOnly
             {
-                if (!(o is DictionaryEntry))
+                get
                 {
-                    return false;
+                    return outerInstance.IsReadOnly;
                 }
-                var e = (KeyValuePair<object, V>)o;
-                object key = e.Key;
-                object val = e.Value;
-                object v = outerInstance.Get(key);
-                return v == null ? val == null : v.Equals(val);
             }
 
-            public bool Remove(KeyValuePair<object, V> item)
+            public void Add(TValue item)
             {
                 throw new NotSupportedException();
             }
 
-            public int Count { get { return outerInstance.count; } private set { throw new NotSupportedException(); } }
-            
             public void Clear()
             {
-                if (!allowModify)
-                {
-                    throw new NotSupportedException();
-                }
                 outerInstance.Clear();
             }
 
-            #region Added for better .NET support LUCENENET
-
-            #region Not implemented members
-
-            // TODO: Either implement these (and write tests for them) or throw
-            // NotSupportedException.
-            public void CopyTo(KeyValuePair<object, V>[] array, int arrayIndex)
-            {
-                throw new NotImplementedException();
-            }
-
-            public bool Contains(KeyValuePair<object, V> item)
+            public bool Contains(TValue item)
             {
-                throw new NotImplementedException();
+                return outerInstance.values.Select(x => (x != null) ? x.value : default(TValue)).Contains(item);
             }
 
-            public void Add(KeyValuePair<object, V> item)
+            public void CopyTo(TValue[] array, int arrayIndex)
             {
-                throw new NotImplementedException();
+                throw new NotSupportedException();
             }
 
-            public void UnionWith(IEnumerable<KeyValuePair<object, V>> other)
+            public IEnumerator<TValue> GetEnumerator()
             {
-                throw new NotImplementedException();
+                return new ValueEnumerator(outerInstance);
             }
 
-            public void IntersectWith(IEnumerable<KeyValuePair<object, V>> other)
+            public bool Remove(TValue item)
             {
-                throw new NotImplementedException();
+                throw new NotSupportedException();
             }
 
-            public void ExceptWith(IEnumerable<KeyValuePair<object, V>> other)
+            IEnumerator IEnumerable.GetEnumerator()
             {
-                throw new NotImplementedException();
+                return GetEnumerator();
             }
 
-            public void SymmetricExceptWith(IEnumerable<KeyValuePair<object, V>> other)
+            public override string ToString()
             {
-                throw new NotImplementedException();
-            }
+                var i = (ValueEnumerator)GetEnumerator();
+                if (!i.HasNext)
+                    return "[]";
 
-            public bool IsSubsetOf(IEnumerable<KeyValuePair<object, V>> other)
-            {
-                throw new NotImplementedException();
-            }
+                StringBuilder sb = new StringBuilder();
+                sb.Append('[');
+                while(i.MoveNext())
+                {
+                    TValue value = i.Current;
+                    if (sb.Length > 1)
+                    {
+                        sb.Append(',').Append(' ');
+                    }
+                    sb.Append(value.ToString());
+                }
 
-            public bool IsSupersetOf(IEnumerable<KeyValuePair<object, V>> other)
-            {
-                throw new NotImplementedException();
+                return sb.Append(']').ToString();
             }
 
-            public bool IsProperSupersetOf(IEnumerable<KeyValuePair<object, V>> other)
+            /// <summary>
+            /// LUCENENET specific class to enumerate the values in the <see cref="ValueCollection"/>.
+            /// </summary>
+            private class ValueEnumerator : IEnumerator<TValue>
             {
-                throw new NotImplementedException();
-            }
+                private readonly CharArrayMap<TValue> outerInstance;
+                private readonly EntryIterator entryIterator;
 
-            public bool IsProperSubsetOf(IEnumerable<KeyValuePair<object, V>> other)
-            {
-                throw new NotImplementedException();
-            }
+                public ValueEnumerator(CharArrayMap<TValue> outerInstance)
+                {
+                    this.outerInstance = outerInstance;
+                    this.entryIterator = new EntryIterator(outerInstance, !outerInstance.IsReadOnly);
+                }
 
-            public bool Overlaps(IEnumerable<KeyValuePair<object, V>> other)
-            {
-                throw new NotImplementedException();
-            }
+                public TValue Current
+                {
+                    get
+                    {
+                        return entryIterator.CurrentValue;
+                    }
+                }
 
-            public bool SetEquals(IEnumerable<KeyValuePair<object, V>> other)
-            {
-                throw new NotImplementedException();
-            }
-
-            bool ISet<KeyValuePair<object, V>>.Add(KeyValuePair<object, V> item)
-            {
-                throw new NotImplementedException();
-            }
-            #endregion
+                object IEnumerator.Current
+                {
+                    get
+                    {
+                        return Current;
+                    }
+                }
 
-            public bool IsReadOnly { get { return !allowModify; } }
+                public void Dispose()
+                {
+                    // nothing to do
+                }
 
-            public override string ToString()
-            {
-                var sb = new StringBuilder("[");
+                public bool MoveNext()
+                {
+                    return entryIterator.MoveNext();
+                }
 
-                IEnumerator<KeyValuePair<object, V>> iter1 = new EntryIterator(this.outerInstance, false);
-                while (iter1.MoveNext())
+                public void Reset()
                 {
-                    KeyValuePair<object, V> entry = iter1.Current;
-                    if (sb.Length > 1)
-                    {
-                        sb.Append(", ");
-                    }
-                    if (entry.Key.GetType().Equals(typeof(char[])))
-                    {
-                        sb.Append(new string((char[])entry.Key));
-                    }
-                    else
-                    {
-                        sb.Append(entry.Key);
-                    }
-                    sb.Append("=");
-                    sb.Append(entry.Value);
+                    entryIterator.Reset();
                 }
 
-                return sb.Append(']').ToString();
+                public bool HasNext
+                {
+                    get { return entryIterator.HasNext; }
+                }
             }
-            #endregion
         }
 
+        #endregion
+
         /// <summary>
-        /// Returns an unmodifiable <seealso cref="CharArrayMap"/>. This allows to provide
-        /// unmodifiable views of internal map for "read-only" use.
+        /// <c>true</c> if the <see cref="CharArrayMap{TValue}"/> is read-only; otherwise <c>false</c>.
         /// </summary>
-        /// <param name="map">
-        ///          a map for which the unmodifiable map is returned. </param>
-        /// <returns> an new unmodifiable <seealso cref="CharArrayMap"/>. </returns>
-        /// <exception cref="NullPointerException">
-        ///           if the given map is <code>null</code>. </exception>
-        public static CharArrayMap<V> UnmodifiableMap(CharArrayMap<V> map)
+        public virtual bool IsReadOnly { get; private set; }
+
+        /// <summary>
+        /// Returns an enumerator that iterates through the <see cref="CharArrayMap{TValue}"/>.
+        /// </summary>
+        public virtual IEnumerator<KeyValuePair<string, TValue>> GetEnumerator()
         {
-            if (map == null)
-            {
-                throw new ArgumentException("Given map is null", "map");
-            }
-            if (map == EmptyMap() || map.Count == 0)
+            return new EntryIterator(this, false);
+        }
+
+        /// <summary>
+        /// Returns an enumerator that iterates through the <see cref="CharArrayMap{TValue}"/>.
+        /// </summary>
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return GetEnumerator();
+        }
+
+        [Obsolete("Not applicable in this class.")]
+        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
+        public virtual bool Remove(string key)
+        {
+            throw new NotSupportedException();
+        }
+
+        [Obsolete("Not applicable in this class.")]
+        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
+        public virtual bool Remove(KeyValuePair<string, TValue> item)
+        {
+            throw new NotSupportedException();
+        }
+
+        /// <summary>
+        /// Gets the number of key/value pairs contained in the <see cref="CharArrayMap{TValue}"/>.
+        /// </summary>
+        public virtual int Count
+        {
+            get { return count; }
+        }
+
+        /// <summary>
+        /// Returns a string that represents the current object. (Inherited from <see cref="object"/>.)
+        /// </summary>
+        public override string ToString()
+        {
+            var sb = new StringBuilder("{");
+
+            IEnumerator<KeyValuePair<string, TValue>> iter1 = IDictionaryExtensions.EntrySet(this).GetEnumerator();
+            while (iter1.MoveNext())
             {
-                return EmptyMap();
+                KeyValuePair<string, TValue> entry = iter1.Current;
+                if (sb.Length > 1)
+                {
+                    sb.Append(", ");
+                }
+                sb.Append(entry.Key);
+                sb.Append("=");
+                sb.Append(entry.Value);
             }
-            if (map is UnmodifiableCharArrayMap<V>)
+
+            return sb.Append('}').ToString();
+        }
+
+        private EntrySet_ entrySet = null;
+        private CharArraySet keySet = null;
+        private KeyCollection originalKeySet = null;
+
+        internal virtual EntrySet_ CreateEntrySet()
+        {
+            return new EntrySet_(this, true);
+        }
+
+        // LUCENENET NOTE: This MUST be a method, since there is an
+        // extension method that this class needs to override the behavior of.
+        public EntrySet_ EntrySet()
+        {
+            if (entrySet == null)
             {
-                return map;
+                entrySet = CreateEntrySet();
             }
-            return new UnmodifiableCharArrayMap<V>(map);
+            return entrySet;
         }
 
         /// <summary>
-        /// Returns a copy of the given map as a <seealso cref="CharArrayMap"/>. If the given map
-        /// is a <seealso cref="CharArrayMap"/> the ignoreCase property will be preserved.
-        /// <para>
-        /// <b>Note:</b> If you intend to create a copy of another <seealso cref="CharArrayMap"/> where
-        /// the <seealso cref="Version"/> of the source map differs from its copy
-        /// <seealso cref="#CharArrayMap(Version, Map, boolean)"/> should be used instead.
-        /// The <seealso cref="#copy(Version, Map)"/> will preserve the <seealso cref="Version"/> of the
-        /// source map it is an instance of <seealso cref="CharArrayMap"/>.
-        /// </para>
+        /// helper for CharArraySet to not produce endless recursion
         /// </summary>
-        /// <param name="matchVersion">
-        ///          compatibility match version see <a href="#version">Version
-        ///          note</a> above for details. This argument will be ignored if the
-        ///          given map is a <seealso cref="CharArrayMap"/>. </param>
-        /// <param name="map">
-        ///          a map to copy </param>
-        /// <returns> a copy of the given map as a <seealso cref="CharArrayMap"/>. If the given map
-        ///         is a <seealso cref="CharArrayMap"/> the ignoreCase property as well as the
-        ///         matchVersion will be of the given map will be preserved. </returns>
-        public static CharArrayMap<V> Copy(LuceneVersion matchVersion, IDictionary<object, V> map)
+        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+        [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
+        public ICollection<string> OriginalKeySet
         {
-            if (map == EMPTY_MAP)
+            get
             {
-                return EmptyMap();
+                if (originalKeySet == null)
+                {
+                    // prevent adding of entries
+                    originalKeySet = new KeyCollection(this);
+                }
+                return originalKeySet;
             }
+        }
 
-            var vs = map as CharArrayMap<V>;
-            if (vs != null)
+        /// <summary>
+        /// Returns an <see cref="CharArraySet"/> view on the map's keys.
+        /// The set will use the same <see cref="matchVersion"/> as this map. 
+        /// </summary>
+        private CharArraySet KeySet
+        {
+            get
             {
-                var m = vs;
-                // use fast path instead of iterating all values
-                // this is even on very small sets ~10 times faster than iterating
-                var keys = new char[m.keys.Length][];
-                Array.Copy(m.keys, 0, keys, 0, keys.Length);
-                var values = new V[m.values.Length];
-                Array.Copy(m.values, 0, values, 0, values.Length);
-
-                IDictionary<object, V> innerDictionaryCopy = new Dictionary<object, V>();
-                for (int i = 0; i < keys.Length; i++)
+                if (keySet == null)
                 {
-                    if (keys[i] != null)
-                        innerDictionaryCopy.Add(keys[i], values[i]);
+                    // prevent adding of entries
+                    keySet = new UnmodifiableCharArraySet(this);
                 }
-                m = new CharArrayMap<V>(m) { keys = keys, values = values, innerDictionary = innerDictionaryCopy };
-                return m;
+                return keySet;
             }
-            return new CharArrayMap<V>(matchVersion, map, false);
         }
 
-        /// <summary>
-        /// Returns an empty, unmodifiable map. </summary>
-        public static CharArrayMap<V> EmptyMap()
+        private sealed class UnmodifiableCharArraySet : CharArraySet
         {
-            return EMPTY_MAP;
+            internal UnmodifiableCharArraySet(ICharArrayMap map) 
+                : base(map)
+            {
+            }
+
+            public override bool Add(object o)
+            {
+                throw new NotSupportedException();
+            }
+            public override bool Add(ICharSequence text)
+            {
+                throw new NotSupportedException();
+            }
+            public override bool Add(string text)
+            {
+                throw new NotSupportedException();
+            }
+            public override bool Add(char[] text)
+            {
+                throw new NotSupportedException();
+            }
         }
 
-        // package private CharArraySet instanceof check in CharArraySet
-        internal class UnmodifiableCharArrayMap<V> : CharArrayMap<V>
+        /// <summary>
+        /// public iterator class so efficient methods are exposed to users
+        /// </summary>
+        public class EntryIterator : IEnumerator<KeyValuePair<string, TValue>>
         {
+            private readonly CharArrayMap<TValue> outerInstance;
 
-            public UnmodifiableCharArrayMap(CharArrayMap<V> map) : base(map)
-            {
+            internal int pos = -1;
+            internal int lastPos;
+            internal readonly bool allowModify;
 
-            }
-            
-            public override void Clear()
+            internal EntryIterator(CharArrayMap<TValue> outerInstance, bool allowModify)
             {
-                throw new System.NotSupportedException();
+                this.outerInstance = outerInstance;
+                this.allowModify = allowModify;
+                GoNext();
             }
 
-            public override V Put(object o, V val)
+            internal void GoNext()
             {
-                throw new System.NotSupportedException();
+                lastPos = pos;
+                pos++;
+                while (pos < outerInstance.keys.Length && outerInstance.keys[pos] == null)
+                {
+                    pos++;
+                }
             }
 
-            public override V Put(char[] text, V val)
+            public virtual bool HasNext
             {
-                throw new System.NotSupportedException();
+                get { return pos < outerInstance.keys.Length; }
             }
 
-            public override V Put(ICharSequence text, V val)
+            /// <summary>
+            /// gets the next key... do not modify the returned char[]
+            /// </summary>
+            public virtual char[] NextKey()
             {
-                throw new System.NotSupportedException();
+                GoNext();
+                return outerInstance.keys[lastPos];
             }
 
-            public override V Put(string text, V val)
+            /// <summary>
+            /// gets the next key as a newly created <see cref="string"/> object
+            /// </summary>
+            public virtual string NextKeyString()
             {
-                throw new System.NotSupportedException();
+                return new string(NextKey());
             }
 
-            public override bool Remove(object key)
+            /// <summary>
+            /// returns the value associated with the current key
+            /// </summary>
+            public virtual TValue CurrentValue
             {
-                throw new System.NotSupportedException();
+                get
+                {
+                    var val = outerInstance.values[lastPos];
+                    return val != null ? val.value : default(TValue);
+                }
             }
 
-            internal override EntrySet_ CreateEntrySet()
+            /// <summary>
+            /// sets the value associated with the last key returned
+            /// </summary>
+            public virtual TValue SetValue(TValue value)
             {
-                return new EntrySet_(this, false);
+                if (!allowModify)
+                {
+                    throw new NotSupportedException();
+                }
+                TValue old = outerInstance.values[lastPos].value;
+                outerInstance.values[lastPos].value = value;
+                return old;
             }
 
+            // LUCENENET: Next() and Remove() methods eliminated here
+
             #region Added for better .NET support LUCENENET
-            public override void Add(object key, V value)
+            public virtual void Dispose()
+            {
+                // nothing to do
+            }
+
+            public virtual bool MoveNext()
             {
-                throw new System.NotSupportedException();
+                if (!HasNext) return false;
+                GoNext();
+                return true;
             }
-            public override void Add(KeyValuePair<object, V> item)
+
+            public virtual void Reset()
             {
-                throw new System.NotSupportedException();
+                pos = -1;
+                GoNext();
             }
-            public override V this[object key]
+
+            public virtual KeyValuePair<string, TValue> Current
             {
-                get { return base[key]; }
-                set { throw new System.NotSupportedException(); }
+                get
+                {
+                    var val = outerInstance.values[lastPos];
+                    return new KeyValuePair<string, TValue>(
+                        new string(outerInstance.keys[lastPos]), 
+                        val != null ? val.value : default(TValue));
+                }
             }
-            public override bool Remove(KeyValuePair<object, V> item)
+
+            object IEnumerator.Current
             {
-                throw new System.NotSupportedException();
+                get { return Current; }
             }
+
             #endregion
         }
 
+        // LUCENENET NOTE: The Java Lucene type MapEntry was removed here because it is not possible 
+        // to inherit the value type KeyValuePair{TKey, TValue} in .NET.
+
         /// <summary>
-        /// Empty <seealso cref="CharArrayMap{V}.UnmodifiableCharArrayMap"/> optimized for speed.
-        /// Contains checks will always return <code>false</code> or throw
-        /// NPE if necessary.
+        /// public EntrySet_ class so efficient methods are exposed to users
+        /// 
+        /// NOTE: In .NET this was renamed to EntrySet_ because it conflicted with the
+        /// method EntrySet(). Since there is also an extension method named <see cref="IDictionary{K,V}.EntrySet()"/> 
+        /// that this class needs to override, changing the name of the method was not
+        /// possible because the extension method would produce incorrect results if it were
+        /// inadvertently called, leading to hard-to-diagnose bugs.
+        /// 
+        /// Another difference between this set and the Java counterpart is that it implements
+        /// <see cref="ICollection{T}"/> rather than <see cref="ISet{T}"/> so we don't have to implement
+        /// a bunch of methods that we aren't really interested in. The <see cref="Keys"/> and <see cref="Values"/>
+        /// properties both return <see cref="ICollection{T}"/>, and while there is no <see cref="EntrySet()"/> method
+        /// or property in .NET, if there were it would certainly return <see cref="ICollection{T}"/>.
         /// </summary>
-        private class EmptyCharArrayMap<V> : UnmodifiableCharArrayMap<V>
+        public sealed class EntrySet_ : ICollection<KeyValuePair<string, TValue>>
         {
-            public EmptyCharArrayMap()
-#pragma warning disable 612, 618
-                : base(new CharArrayMap<V>(LuceneVersion.LUCENE_CURRENT, 0, false))
-#pragma warning restore 612, 618
+            private readonly CharArrayMap<TValue> outerInstance;
+
+            internal readonly bool allowModify;
+
+            internal EntrySet_(CharArrayMap<TValue> outerInstance, bool allowModify)
             {
+                this.outerInstance = outerInstance;
+                this.allowModify = allowModify;
             }
 
-            public override bool ContainsKey(char[] text, int off, int len)
+            public IEnumerator GetEnumerator()
             {
-                if (text == null)
-                {
-                    throw new NullReferenceException();
-                }
-                return false;
+                return new EntryIterator(outerInstance, allowModify);
             }
 
-            public bool ContainsKey(ICharSequence cs)
+            IEnumerator<KeyValuePair<string, TValue>> IEnumerable<KeyValuePair<string, TValue>>.GetEnumerator()
             {
-                if (cs == null)
-                {
-                    throw new NullReferenceException();
-                }
-                return false;
+                return (IEnumerator<KeyValuePair<string, TValue>>)GetEnumerator();
             }
 
-            public override bool ContainsKey(object o)
+            public bool Contains(object o)
             {
-                if (o == null)
+                if (!(o is KeyValuePair<string, TValue>))
                 {
-                    throw new NullReferenceException();
+                    return false;
                 }
-                return false;
+                var e = (KeyValuePair<string, TValue>)o;
+                string key = e.Key;
+                TValue val = e.Value;
+                TValue v = outerInstance.Get(key);
+                return v == null ? val == null : v.Equals(val);
             }
 
-            public override V Get(char[] text, int off, int len)
+            [Obsolete("Not applicable in this class.")]
+            [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+            [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
+            public bool Remove(KeyValuePair<string, TValue> item)
             {
-                if (text == null)
-                {
-                    throw new NullReferenceException();
-                }
-                return default(V);
+                throw new NotSupportedException();
             }
 
-            public V Get(ICharSequence cs)
+            public int Count
             {
-                if (cs == null)
+                get { return outerInstance.count; }
+            }
+            
+            public void Clear()
+            {
+                if (!allowModify)
                 {
-                    throw new NullReferenceException();
+                    throw new NotSupportedException();
                 }
-                return default(V);
+                outerInstance.Clear();
+            }
+
+            #region LUCENENET Added for better .NET support
+
+            public void CopyTo(KeyValuePair<string, TValue>[] array, int arrayIndex)
+            {
+                outerInstance.CopyTo(array, arrayIndex);
+            }
+
+            [Obsolete("Not applicable in this class.")]
+            [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
+            [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
+            public bool Contains(KeyValuePair<string, TValue> item)
+            {
+                return outerInstance.Contains(item);
+            }
+
+            public void Add(KeyValuePair<string, TValue> item)
+            {
+                outerInstance.Add(item);
+            }
+
+            public bool IsReadOnly { get { return !allowModify; } }
+
+            public override string ToString()
+            {
+                var sb = new StringBuilder("[");
+
+                IEnumerator<KeyValuePair<string, TValue>> iter1 = new EntryIterator(this.outerInstance, false);
+                while (iter1.MoveNext())
+                {
+                    KeyValuePair<string, TValue> entry = iter1.Current;
+                    if (sb.Length > 1)
+                    {
+                        sb.Append(", ");
+                    }
+                    sb.Append(entry.Key);
+                    sb.Append("=");
+                    sb.Append(entry.Value);
+                }
+
+                return sb.Append(']').ToString();
+            }
+            #endregion
+        }
+
+        // LUCENENET: Moved UnmodifiableMap static methods to CharArrayMap class
+
+        // LUCENENET: Moved Copy static methods to CharArrayMap class
+
+        /// <summary>
+        /// Returns an empty, unmodifiable map. </summary>
+        public static CharArrayMap<TValue> EmptyMap()
+        {
+            return EMPTY_MAP;
+        }
+
+        // LUCENENET: Moved UnmodifyableCharArraymap to CharArrayMap class
+
+        // LUCENENET: Moved EmptyCharArrayMap to CharArrayMap class
+    }


<TRUNCATED>