Подтвердить что ты не робот

Как написать Java.util.Map в пакет по-разумному?

У меня есть общая карта строк (ключ, значение), и это поле является частью Bean, которое мне нужно быть простым. Таким образом, я мог бы использовать метод почтовой рассылки # WriteMap. API Doc говорит:

Вместо этого используйте writeBundle (Bundle). Сглаживает карту в участок в текущем dataPosition(), при необходимости увеличивая емкость данных(). Ключами карты должны быть объекты String. Значения карты записываются с использованием writeValue (Object) и должен следовать спецификации. это настоятельно рекомендуется использовать writeBundle (Bundle) вместо этого метод, так как класс Bundle предоставляет безопасный тип API, который позволяет вы избегаете таинственных ошибок типа в точке сортировки.

Итак, я мог перебирать каждую запись в моей карте, а затем вносить ее в 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);
4b9b3361

Ответ 1

Я закончил делать это по-другому. Это следует за шаблоном, который вы ожидаете для работы с 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);
  }
}

В моем приложении порядок ключей на карте имел значение. Я использовал LinkedHashMap чтобы сохранить заказ, и таким образом гарантировал, что ключи будут отображаться в том же порядке после того, как будут извлечены из Parcel.

Ответ 2

вы можете попробовать:

bundle.putSerializable(yourSerializableMap);

если ваша выбранная карта реализует сериализуемую (например, HashMap), а затем вы можете легко использовать свой writeBundle

Ответ 3

Если и key и value на карте расширяются Parcelable, вы можете получить довольно красивое решение Generics:

код

// 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;
}

Использование

// 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);

Ответ 4

Хороший вопрос. В API нет каких-либо методов, о которых я знаю, кроме putSerializable и writeMap. Сериализация не рекомендуется по соображениям производительности, а writeMap() также не рекомендуется по несколько таинственным причинам, как вы уже указали.

Мне нужно было посылить HashMap сегодня, поэтому я попробовал свои силы при написании некоторых полезных методов для посылки карты в и из Bundle рекомендуемым образом:

// 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);
    }
}

Изменить: измененную функцию writeMap, чтобы использовать элемент ввода, сохраняя тот же формат данных, что и в моем исходном ответе (показан на другой стороне комментария переключения). Если вам не нужно или хотите сохранить совместимость с чтением, может быть проще просто хранить пары ключ-значение на каждой итерации, как в ответах @bcorso и @Anthony Naddeo.

Ответ 5

Если ваш ключ карты - String, вы можете просто использовать Bundle, как упоминалось в javadocs:

/**
 * Please use {@link #writeBundle} instead.  Flattens a Map into the parcel
 * at the current dataPosition(),
 * growing dataCapacity() if needed.  The Map keys must be String objects.
 * The Map values are written using {@link #writeValue} and must follow
 * the specification there.
 *
 * <p>It is strongly recommended to use {@link #writeBundle} instead of
 * this method, since the Bundle class provides a type-safe API that
 * allows you to avoid mysterious type errors at the point of marshalling.
 */
public final void writeMap(Map val) {
    writeMapInternal((Map<String, Object>) val);
}

Поэтому я написал следующий код:

private void writeMapAsBundle(Parcel dest, Map<String, Serializable> map) {
    Bundle bundle = new Bundle();
    for (Map.Entry<String, Serializable> entry : map.entrySet()) {
        bundle.putSerializable(entry.getKey(), entry.getValue());
    }
    dest.writeBundle(bundle);
}

private void readMapFromBundle(Parcel in, Map<String, Serializable> map, ClassLoader keyClassLoader) {
    Bundle bundle = in.readBundle(keyClassLoader);
    for (String key : bundle.keySet()) {
        map.put(key, bundle.getSerializable(key));
    }
}

Соответственно, вы можете использовать Parcelable вместо Serializable

Ответ 6

Здесь моя несколько простая, но работающая до сих пор для меня реализация в Котлине. Его можно легко модифицировать, если он не удовлетворяет

Но не забывайте, что K,V должен быть Parcelable если он отличается от обычного String, Int,... т.д.

Написать

parcel.writeMap(map)

Читать

parcel.readMap(map)

Читайте дальше

fun<K,V> Parcel.readMap(map: MutableMap<K,V>) : MutableMap<K,V>{

    val tempMap = LinkedHashMap<Any?,Any?>()
    readMap(tempMap, map.javaClass.classLoader)

    tempMap.forEach {
        map[it.key as K] = it.value as V
    }
    /* It populates and returns the map as well
       (useful for constructor parameters inits)*/
    return map
}