如何以一种聪明的方式将Java.util.Map写入parcel?

Kit*_*fer 62 android bundle parcelable

我有一个字符串的通用映射(键,值),这个字段是我需要可以分配的Bean的一部分.所以,我可以使用Parcel#writeMap方法.API Doc说:

请改用writeBundle(Bundle).在当前dataPosition()中将Map展平到parcel中,如果需要,增加dataCapacity().Map键必须是String对象.Map值使用writeValue(Object)编写,并且必须遵循其中的规范.强烈建议使用writeBundle(Bundle)而不是此方法,因为Bundle类提供了一个类型安全的API,允许您在编组时避免神秘的类型错误.

因此,我可以迭代我的Map中的每个条目并将其放入Bundle中,但我仍然在寻找一种更聪明的方法.我缺少Android SDK中的任何方法吗?

目前我这样做:

final Bundle bundle = new Bundle();
final Iterator<Entry<String, String>> iter = links.entrySet().iterator();
while(iter.hasNext())
{
    final Entry<String, String>  entry =iter.next();
    bundle.putString(entry.getKey(), entry.getValue());
}
parcel.writeBundle(bundle);
Run Code Online (Sandbox Code Playgroud)

Ant*_*deo 81

我最后做的有点不同了.它遵循您期望处理的模式Parcelables,因此它应该是熟悉的.

public void writeToParcel(Parcel out, int flags){
  out.writeInt(map.size());
  for(Map.Entry<String,String> entry : map.entrySet()){
    out.writeString(entry.getKey());
    out.writeString(entry.getValue());
  }
}

private MyParcelable(Parcel in){
  //initialize your map before
  int size = in.readInt();
  for(int i = 0; i < size; i++){
    String key = in.readString();
    String value = in.readString();
    map.put(key,value);
  }
}
Run Code Online (Sandbox Code Playgroud)

在我的应用程序中,地图中键的顺序很重要.我使用a LinkedHashMap来保存顺序,这样做可以保证密钥在从中提取后以相同的顺序出现Parcel.

  • 使用entrySet而不是keySet应该更有效:您可以避免在映射中为每个条目查找值. (6认同)

STT*_*LCU 26

你可以试试:

bundle.putSerializable(yourSerializableMap);
Run Code Online (Sandbox Code Playgroud)

如果您选择的地图实现了可序列化(如HashMap),那么您可以轻松使用您的writeBundle

  • 它可能不是最好的,效率明智的,但确定它写得很短:) (4认同)

bco*_*rso 23

如果地图keyvalue地图都延伸Parcelable,你可以有一个非常漂亮的泛型解决方案:

// For writing to a Parcel
public <K extends Parcelable,V extends Parcelable> void writeParcelableMap(
        Parcel parcel, int flags, Map<K, V > map)
{
    parcel.writeInt(map.size());
    for(Map.Entry<K, V> e : map.entrySet()){
        parcel.writeParcelable(e.getKey(), flags);
        parcel.writeParcelable(e.getValue(), flags);
    }
}

// For reading from a Parcel
public <K extends Parcelable,V extends Parcelable> Map<K,V> readParcelableMap(
        Parcel parcel, Class<K> kClass, Class<V> vClass)
{
    int size = parcel.readInt();
    Map<K, V> map = new HashMap<K, V>(size);
    for(int i = 0; i < size; i++){
        map.put(kClass.cast(parcel.readParcelable(kClass.getClassLoader())),
                vClass.cast(parcel.readParcelable(vClass.getClassLoader())));
    }
    return map;
}
Run Code Online (Sandbox Code Playgroud)

用法

// MyClass1 and MyClass2 must extend Parcelable
Map<MyClass1, MyClass2> map;

// Writing to a parcel
writeParcelableMap(parcel, flags, map);

// Reading from a parcel
map = readParcelableMap(parcel, MyClass1.class, MyClass2.class);
Run Code Online (Sandbox Code Playgroud)


Lor*_*rte 12

好问题.除了putSerializable和writeMap之外,我所知道的API中没有任何方法.出于性能原因不建议使用序列化,并且出于某些神秘的原因,也不推荐使用writeMap(),正如您已经指出的那样.

我今天需要打包一个HashMap,所以我尝试编写一些实用方法,以推荐的方式将Map分配到Bundle和从Bundle中分配Map:

// Usage:

// read map into a HashMap<String,Foo>
links = readMap(parcel, Foo.class);

// another way that lets you use a different Map implementation
links = new SuperDooperMap<String, Foo>;
readMap(links, parcel, Foo.class);

// write map out
writeMap(links, parcel);

////////////////////////////////////////////////////////////////////
// Parcel methods

/**
 * Reads a Map from a Parcel that was stored using a String array and a Bundle.
 *
 * @param in   the Parcel to retrieve the map from
 * @param type the class used for the value objects in the map, equivalent to V.class before type erasure
 * @return     a map containing the items retrieved from the parcel
 */
public static <V extends Parcelable> Map<String,V> readMap(Parcel in, Class<? extends V> type) {

    Map<String,V> map = new HashMap<String,V>();
    if(in != null) {
        String[] keys = in.createStringArray();
        Bundle bundle = in.readBundle(type.getClassLoader());
        for(String key : keys)
            map.put(key, type.cast(bundle.getParcelable(key)));
    }
    return map;
}


/**
 * Reads into an existing Map from a Parcel that was stored using a String array and a Bundle.
 *
 * @param map  the Map<String,V> that will receive the items from the parcel
 * @param in   the Parcel to retrieve the map from
 * @param type the class used for the value objects in the map, equivalent to V.class before type erasure
 */
public static <V extends Parcelable> void readMap(Map<String,V> map, Parcel in, Class<V> type) {

    if(map != null) {
        map.clear();
        if(in != null) {
            String[] keys = in.createStringArray();
            Bundle bundle = in.readBundle(type.getClassLoader());
            for(String key : keys)
                map.put(key, type.cast(bundle.getParcelable(key)));
        }
    }
}


/**
 * Writes a Map to a Parcel using a String array and a Bundle.
 *
 * @param map the Map<String,V> to store in the parcel
 * @param out the Parcel to store the map in
 */
public static void writeMap(Map<String,? extends Parcelable> map, Parcel out) {

    if(map != null && map.size() > 0) {
        /*
        Set<String> keySet = map.keySet();
        Bundle b = new Bundle();
        for(String key : keySet)
            b.putParcelable(key, map.get(key));
        String[] array = keySet.toArray(new String[keySet.size()]);
        out.writeStringArray(array);
        out.writeBundle(b);
        /*/
        // alternative using an entrySet, keeping output data format the same
        // (if you don't need to preserve the data format, you might prefer to just write the key-value pairs directly to the parcel)
        Bundle bundle = new Bundle();
        for(Map.Entry<String, ? extends Parcelable> entry : map.entrySet()) {
            bundle.putParcelable(entry.getKey(), entry.getValue());
        }

        final Set<String> keySet = map.keySet();
        final String[] array = keySet.toArray(new String[keySet.size()]);
        out.writeStringArray(array);
        out.writeBundle(bundle);
        /**/
    }
    else {
        //String[] array = Collections.<String>emptySet().toArray(new String[0]);
        // you can use a static instance of String[0] here instead
        out.writeStringArray(new String[0]);
        out.writeBundle(Bundle.EMPTY);
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:修改了writeMap以使用entrySet,同时保留与原始答案相同的数据格式(显示在切换注释的另一侧).如果您不需要或不想保持读兼容性,那么在每次迭代中存储键值对可能更简单,如@bcorso和@Anthony Naddeo的答案.