一个有序的key-value容器NamedList

                                                 一个有序的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&lt;String, ?&gt; 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&lt;String, ?&gt;.
   * @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);
  }
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值