UFO ET IT

어떻게 Java.util.Map을 현명한 방법으로 소포에 작성합니까?

ufoet 2020. 11. 26. 20:26
반응형

어떻게 Java.util.Map을 현명한 방법으로 소포에 작성합니까?


나는 일반 문자열 맵 (키, 값)을 가지고 있고이 필드는 내가 parcelable해야하는 Bean의 일부입니다. 그래서 Parcel # writeMap 메서드를 사용할 수 있습니다. API 문서는 다음과 같이 말합니다.

대신 writeBundle (Bundle)을 사용하십시오. 지도를 현재 dataPosition ()의 구획으로 평평하게하고 필요한 경우 dataCapacity ()를 늘립니다. 맵 키는 문자열 객체 여야합니다. Map 값은 writeValue (Object)를 사용하여 작성되며 해당 사양을 따라야합니다. 이 메서드 대신 writeBundle (Bundle)을 사용하는 것이 좋습니다. Bundle 클래스는 마샬링 지점에서 신비한 유형 오류를 방지 할 수있는 유형 안전 API를 제공하기 때문입니다.

그래서 맵의 각 항목을 반복하여 번들에 넣을 수 있지만 여전히 더 현명한 방법을 찾고 있습니다. 내가 놓친 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);

나는 그것을 조금 다르게하게되었다. 처리 할 때 예상하는 패턴을 따르 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순서를 유지하기 위해 a 사용하고 있었고 이렇게하면 키가 .NET에서 추출 된 후 동일한 순서로 나타날 것을 보장합니다 Parcel.


당신은 시도 할 수 있습니다:

bundle.putSerializable(yourSerializableMap);

선택한 맵이 HashMap과 같은 직렬화 가능을 구현하면 writeBundle을 쉽게 사용할 수 있습니다.


지도 keyvalue지도가 모두 확장 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);

좋은 질문. API에는 putSerializable 및 writeMap 외에 다른 메소드가 없습니다. 직렬화는 성능상의 이유로 권장되지 않으며 writeMap ()은 이미 지적했듯이 다소 신비한 이유로 권장되지 않습니다.

오늘 HashMap을 파셀해야했기 때문에 권장되는 방식으로 번들과 맵을 파 셀링하기위한 몇 가지 유틸리티 메서드를 작성해 보았습니다.

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

편집 : 내 원래 답변과 동일한 데이터 형식을 유지하면서 entrySet을 사용하도록 writeMap을 수정했습니다 (토글 주석의 다른쪽에 표시됨). 읽기 호환성이 필요하지 않거나 보존하고 싶다면 @bcorso 및 @Anthony Naddeo의 답변에서와 같이 각 반복마다 키-값 쌍을 저장하는 것이 더 간단 할 수 있습니다.


If your map's key is String, you can just use Bundle, as it mentioned in 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);
}

So I wrote the following code:

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

Accordingly, you can use Parcelable instead of Serializable


Here's mine somewhat simple but working so far for me implementation in Kotlin. It can be modified easily if it doesn't satisfy one needs

But don't forget that K,V must be Parcelable if different than the usual String, Int,... etc

Write

parcel.writeMap(map)

Read

parcel.readMap(map)

The read overlaod

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
}

All the solutions mentioned here are valid but no one is universal enough. Often you have maps containing Strings, Integers, Floats etc. values and/or keys. In such a case you can't use <... extends Parcelable> and I don't want to write custom methods for any other key/value combinations. For that case you can use this code:

@FunctionalInterface
public interface ParcelWriter<T> {
    void writeToParcel(@NonNull final T value,
                       @NonNull final Parcel parcel, final int flags);
}

@FunctionalInterface
public interface ParcelReader<T> {
    T readFromParcel(@NonNull final Parcel parcel);
}

public static <K, V> void writeParcelableMap(
        @NonNull final Map<K, V> map,
        @NonNull final Parcel parcel,
        final int flags,
        @NonNull final ParcelWriter<Map.Entry<K, V>> parcelWriter) {
    parcel.writeInt(map.size());

    for (final Map.Entry<K, V> e : map.entrySet()) {
        parcelWriter.writeToParcel(e, parcel, flags);
    }
}

public static <K, V> Map<K, V> readParcelableMap(
        @NonNull final Parcel parcel,
        @NonNull final ParcelReader<Map.Entry<K, V>> parcelReader) {
    int size = parcel.readInt();
    final Map<K, V> map = new HashMap<>(size);

    for (int i = 0; i < size; i++) {
        final Map.Entry<K, V> value = parcelReader.readFromParcel(parcel);
        map.put(value.getKey(), value.getValue());
    }
    return map;
}

It's more verbose but universal. Here is the write usage:

writeParcelableMap(map, dest, flags, (mapEntry, parcel, __) -> {
        parcel.write...; //key from mapEntry
        parcel.write...; //value from mapEntry
    });

and read:

map = readParcelableMap(in, parcel ->
    new AbstractMap.SimpleEntry<>(parcel.read... /*key*/, parcel.read... /*value*/)
);

참고URL : https://stackoverflow.com/questions/8254654/how-write-java-util-map-into-parcel-in-a-smart-way

반응형