一个有序的key-value容器NamedList
在读solr源代码的时候,看到一个有序的key-value容器类NamedList,挺有创意的。
一、特点:
1、key是可以重复的
2、容器中的元素是有序的(插入顺序)
3、可通过下标获取key和value
4、key和value都是可空的
二、适用场景
1、需要用上以上特点的场景。
2、从上面描述的特点,我们可以看出NamedList非常适合作为JSON格式的对象的序列化。
三、实现
实现挺简单的,简单来说就是在一个ArrayList里用第2n、2n+1(n=0、1、2...)个item为一对存放一个keyvalue对,
第2n个item存放name,第2n+1个item存放value(最多只能存放Interge.MAX_VALUE/2 个keyvalue对)。
然后实现了根据下标操作数据的方法,如:
public String getName(int idx) {
return (String)nvPairs.get(idx << 1);
public T getVal(int idx) {
return (T)nvPairs.get((idx << 1) + 1);
也实现了根据key对数据的操作,如
public T get(String name) {
return get(name,0);
但性能会比较差,因为需要从头开始一个一个元素去比较,来定位元素。
附上NamedList的完整源代码:
* 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.solr.common.util;
import java.util.*;
import java.io.Serializable;
* A simple container class for modeling an ordered list of name/value pairs.
* <p>
* Unlike Maps:
* </p>
* <ul>
* <li>Names may be repeated</li>
* <li>Order of elements is maintained</li>
* <li>Elements may be accessed by numeric index</li>
* <li>Names and Values can both be null</li>
* </ul>
* <p>
* A NamedList provides fast access by element number, but not by name.
* </p>
* <p>
* When a NamedList is serialized, order is considered more important than access
* by key, so ResponseWriters that output to a format such as JSON will normally
* choose a data structure that allows order to be easily preserved in various
* clients (i.e. not a straight map).
* If access by key is more important, see {@link SimpleOrderedMap},
* or simply use a regular {@link Map}
* </p>
* @version $Id: NamedList.java 779486 2009-05-28 08:48:29Z shalin $
public class NamedList<T> implements Cloneable, Serializable, Iterable<Map.Entry<String,T>> {
protected final List nvPairs;
/** Creates an empty instance */
public NamedList() {
nvPairs = new ArrayList();
* Creates a NamedList instance containing the "name,value" pairs contained in the
* Entry[].
* <p>
* Modifying the contents of the Entry[] after calling this constructor may change
* the NamedList (in future versions of Solr), but this is not garunteed and should
* not be relied upon. To modify the NamedList, refer to {@link #add(String, Object)}
* or {@link #remove(String)}.
* </p>
* @param nameValuePairs the name value pairs
public NamedList(Map.Entry<String, ? extends T>[] nameValuePairs) {
nvPairs = nameValueMapToList(nameValuePairs);
* Creates an instance backed by an explicitly specified list of
* pairwise names/values.
* <p>
* When using this constructor, runtime typesafety is only garunteed if the all
* even numbered elements of the input list are of type "T".
* </p>
* @param nameValuePairs underlying List which should be used to implement a NamedList
* @deprecated Use {@link #NamedList(java.util.Map.Entry[])} for the NamedList instantiation
@Deprecated
public NamedList(List nameValuePairs) {
nvPairs=nameValuePairs;
* Method to serialize Map.Entry<String, ?> to a List in which the even
* indexed elements (0,2,4. ..etc) are Strings and odd elements (1,3,5,) are of
* the type "T".
* @param nameValuePairs
* @return Modified List as per the above description
* @deprecated This a temporary placeholder method until the guts of the class
* are actually replaced by List<String, ?>.
* @see https://issues.apache.org/jira/browse/SOLR-912
@Deprecated
private List nameValueMapToList(Map.Entry<String, ? extends T>[] nameValuePairs) {
List result = new ArrayList();
for (Map.Entry<String, ?> ent : nameValuePairs) {
result.add(ent.getKey());
result.add(ent.getValue());
return result;
/** The total number of name/value pairs */
public int size() {
return nvPairs.size() >> 1;
* The name of the pair at the specified List index
* @return null if no name exists
public String getName(int idx) {
return (String)nvPairs.get(idx << 1);
* The value of the pair at the specified List index
* @return may be null
@SuppressWarnings("unchecked")
public T getVal(int idx) {
return (T)nvPairs.get((idx << 1) + 1);
* Adds a name/value pair to the end of the list.
public void add(String name, T val) {
nvPairs.add(name);
nvPairs.add(val);
* Modifies the name of the pair at the specified index.
public void setName(int idx, String name) {
nvPairs.set(idx<<1, name);
* Modifies the value of the pair at the specified index.
* @return the value that used to be at index
public T setVal(int idx, T val) {
int index = (idx<<1)+1;
T old = (T)nvPairs.get( index );
nvPairs.set(index, val);
return old;
* Removes the name/value pair at the specified index.
* @return the value at the index removed
public T remove(int idx) {
int index = (idx<<1);
nvPairs.remove(index);
return (T)nvPairs.remove(index); // same index, as things shifted in previous remove
* Scans the list sequentially beginning at the specified index and
* returns the index of the first pair with the specified name.
* @param name name to look for, may be null
* @param start index to begin searching from
* @return The index of the first matching pair, -1 if no match
public int indexOf(String name, int start) {
int sz = size();
for (int i=start; i<sz; i++) {
String n = getName(i);
if (name==null) {
if (n==null) return i; // matched null
} else if (name.equals(n)) {
return i;
return -1;
* Gets the value for the first instance of the specified name
* found.
* @return null if not found or if the value stored was null.
* @see #indexOf
* @see #get(String,int)
public T get(String name) {
return get(name,0);
* Gets the value for the first instance of the specified name
* found starting at the specified index.
* @return null if not found or if the value stored was null.
* @see #indexOf
public T get(String name, int start) {
int sz = size();
for (int i=start; i<sz; i++) {
String n = getName(i);
if (name==null) {
if (n==null) return getVal(i);
} else if (name.equals(n)) {
return getVal(i);
return null;
* Gets the values for the the specified name
* @param name Name
* @return List of values
public List<T> getAll(String name) {
List<T> result = new ArrayList<T>();
int sz = size();
for (int i = 0; i < sz; i++) {
String n = getName(i);
if (name==n || (name!=null && name.equals(n))) {
result.add(getVal(i));
return result;
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append('{');
int sz = size();
for (int i=0; i<sz; i++) {
if (i != 0) sb.append(',');
sb.append(getName(i));
sb.append('=');
sb.append(getVal(i));
sb.append('}');
return sb.toString();
* Helper class implementing Map.Entry<String, T> to store the key-value
* relationship in NamedList (the keys of which are String-s)
* @param <T>
public static final class NamedListEntry<T> implements Map.Entry<String, T> {
public NamedListEntry() {
public NamedListEntry(String _key, T _value) {
key = _key;
value = _value;
public String getKey() {
return key;
public T getValue() {
return value;
public T setValue(T _value) {
T oldValue = value;
value = _value;
return oldValue;
private String key;
private T value;
* Iterates over the Map and sequentially adds it's key/value pairs
public boolean addAll(Map<String,T> args) {
for( Map.Entry<String, T> entry : args.entrySet() ) {
add( entry.getKey(), entry.getValue() );
return args.size()>0;
/** Appends the elements of the given NamedList to this one. */
public boolean addAll(NamedList<T> nl) {
nvPairs.addAll(nl.nvPairs);
return nl.size()>0;
* Makes a <i>shallow copy</i> of the named list.
public NamedList<T> clone() {
ArrayList newList = new ArrayList(nvPairs.size());
newList.addAll(nvPairs);
return new NamedList<T>(newList);
//----------------------------------------
// Iterable interface
//----------------------------------------
* Support the Iterable interface
public Iterator<Map.Entry<String,T>> iterator() {
final NamedList list = this;
Iterator<Map.Entry<String,T>> iter = new Iterator<Map.Entry<String,T>>() {
int idx = 0;
public boolean hasNext() {
return idx < list.size();
public Map.Entry<String,T> next() {
final int index = idx++;
Map.Entry<String,T> nv = new Map.Entry<String,T>() {
public String getKey() {
return list.getName( index );
@SuppressWarnings("unchecked")
public T getValue() {
return (T)list.getVal( index );
public String toString()
return getKey()+"="+getValue();
public T setValue(T value) {
return (T) list.setVal(index, value);
return nv;
public void remove() {
throw new UnsupportedOperationException();
return iter;
public T remove(String name) {
int idx = indexOf(name, 0);
if(idx != -1) return remove(idx);
return null;
@Override
public int hashCode() {
return nvPairs.hashCode();
@Override
public boolean equals(Object obj) {
if (!(obj instanceof NamedList)) return false;
NamedList nl = (NamedList) obj;
return this.nvPairs.equals(nl.nvPairs);