diff --git a/README.markdown b/README.markdown index 2558a762..68b36a85 100644 --- a/README.markdown +++ b/README.markdown @@ -22,7 +22,7 @@ A project that provides [kryo](https://github.com/EsotericSoftware/kryo) (v2 and * UUIDSerializer - serializer for java.util.UUID * EnumMapSerializer - serializer for EnumMap * EnumSetSerializer - serializer for EnumSet -* FieldAnnotationAwareSerializer - field serializer that either ignores fields with user specified annotations or exclusively considers such fields (e.g. useful to ignore all fields annotated with Springs `@Autowired` annotation). +* FieldAnnotationAwareSerializer - field serializer that either ignores fields with user-specified annotations or exclusively considers such fields (e.g. useful to ignore all fields annotated with Springs `@Autowired` annotation). * GregorianCalendarSerializer - optimized serializer for (Gregorian)Calendar (24 bytes vs. 1323 bytes with FieldSerializer) * JdkProxySerializer - for jdk proxies (proxies created via Proxy.newProxyInstance) * KryoReflectionFactorySupport - kryo specialization that uses sun's ReflectionFactory to create new instances for classes without a default constructor @@ -31,11 +31,16 @@ A project that provides [kryo](https://github.com/EsotericSoftware/kryo) (v2 and * UnmodifiableCollectionsSerializer - for unmodifiable Collections and Maps created via Collections.unmodifiable*. * cglib/CGLibProxySerializer - serializer for CGLib proxies +* guava/ArrayListMultimapSerializer - serializer for guava-libraries' ArrayListMultimap +* guava/HashMultimapSerializer -- serializer for guava-libraries' HashMultimap * guava/ImmutableListSerializer - serializer for guava-libraries' ImmutableList * guava/ImmutableSetSerializer - serializer for guava-libraries' ImmutableSet * guava/ImmutableMapSerializer - serializer for guava-libraries' ImmutableMap * guava/ImmutableMultimapSerializer - serializer for guava-libraries' ImmutableMultimap * guava/ImmutableSortedSetSerializer - serializer for guava-libraries' ImmutableSortedSet +* guava/LinkedHashMultimapSerializer - serializer for guava-libraries' LinkedHashMultimap +* guava/LinkedListMultimapSerializer - serializer for guava-libraries' LinkedListMultimap +* guava/TreeMultimapSerializer - serializer for guava-libraries' TreeMultimap * guava/UnmodifiableNavigableSetSerializer - serializer for guava-libraries' UnmodifiableNavigableSet * jodatime/JodaDateTimeSerializer - serializer for joda's DateTime * jodatime/JodaIntervalSerializer - serializer for joda's Interval @@ -89,8 +94,14 @@ After that's done you can register the custom serializers at the kryo instance. ImmutableMapSerializer.registerSerializers( kryo ); ImmutableMultimapSerializer.registerSerializers( kryo ); UnmodifiableNavigableSetSerializer.registerSerializers( kryo ); - -The following code snippet shows how to use the `KryoReflectionFactorySupport` (can only be used with sun/oracly jdk!) and how other serializers are registered via the `getDefaultSerializer` lookup. If you don't want to use the `KryoReflectionFactorySupport` you can override the `getDefaultSerializer` method for your `new Kryo()` instance. + // guava ArrayListMultimap, HashMultimap, LinkedHashMultimap, LinkedListMultimap, TreeMultimap + ArrayListMultimapSerializer.registerSerializers( kryo ); + HashMultimapSerializer.registerSerializers( kryo ); + LinkedHashMultimapSerializer.registerSerializers( kryo ); + LinkedListMultimapSerializer.registerSerializers( kryo ); + TreeMultimapSerializer.registerSerializers( kryo ); + +The following code snippet shows how to use the `KryoReflectionFactorySupport` (can only be used with sun/oracle jdk!) and how other serializers are registered via the `getDefaultSerializer` lookup. If you don't want to use the `KryoReflectionFactorySupport` you can override the `getDefaultSerializer` method for your `new Kryo()` instance. final Kryo kryo = new KryoReflectionFactorySupport() { diff --git a/src/main/java/de/javakaffee/kryoserializers/guava/ArrayListMultimapSerializer.java b/src/main/java/de/javakaffee/kryoserializers/guava/ArrayListMultimapSerializer.java new file mode 100644 index 00000000..90c24e66 --- /dev/null +++ b/src/main/java/de/javakaffee/kryoserializers/guava/ArrayListMultimapSerializer.java @@ -0,0 +1,45 @@ +package de.javakaffee.kryoserializers.guava; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.Serializer; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; + +import com.google.common.collect.ArrayListMultimap; + +/** + * A kryo {@link Serializer} for guava-libraries {@link ArrayListMultimap}. + * This does not yet support {@link Kryo#copy(java.lang.Object)}. + */ +public class ArrayListMultimapSerializer extends MultimapSerializerBase> { + + private static final boolean DOES_NOT_ACCEPT_NULL = false; + + private static final boolean IMMUTABLE = false; + + public ArrayListMultimapSerializer() { + super(DOES_NOT_ACCEPT_NULL, IMMUTABLE); + } + + @Override + public void write(Kryo kryo, Output output, ArrayListMultimap multimap) { + writeMultimap(kryo, output, multimap); + } + + @Override + public ArrayListMultimap read(Kryo kryo, Input input, Class> type) { + final ArrayListMultimap multimap = ArrayListMultimap.create(); + readMultimap(kryo, input, multimap); + return multimap; + } + + /** + * Creates a new {@link ArrayListMultimapSerializer} and registers its serializer. + * + * @param kryo the {@link Kryo} instance to set the serializer on + */ + public static void registerSerializers(final Kryo kryo) { + final ArrayListMultimapSerializer serializer = new ArrayListMultimapSerializer(); + kryo.register(ArrayListMultimap.class, serializer); + } +} diff --git a/src/main/java/de/javakaffee/kryoserializers/guava/HashMultimapSerializer.java b/src/main/java/de/javakaffee/kryoserializers/guava/HashMultimapSerializer.java new file mode 100644 index 00000000..23d9af33 --- /dev/null +++ b/src/main/java/de/javakaffee/kryoserializers/guava/HashMultimapSerializer.java @@ -0,0 +1,45 @@ +package de.javakaffee.kryoserializers.guava; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.Serializer; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; + +import com.google.common.collect.HashMultimap; + +/** + * A kryo {@link Serializer} for guava-libraries {@link HashMultimap}. + * This does not yet support {@link Kryo#copy(java.lang.Object)}. + */ +public class HashMultimapSerializer extends MultimapSerializerBase> { + + private static final boolean DOES_NOT_ACCEPT_NULL = false; + + private static final boolean IMMUTABLE = false; + + public HashMultimapSerializer() { + super(DOES_NOT_ACCEPT_NULL, IMMUTABLE); + } + + @Override + public void write(Kryo kryo, Output output, HashMultimap multimap) { + writeMultimap(kryo, output, multimap); + } + + @Override + public HashMultimap read(Kryo kryo, Input input, Class> type) { + final HashMultimap multimap = HashMultimap.create(); + readMultimap(kryo, input, multimap); + return multimap; + } + + /** + * Creates a new {@link HashMultimapSerializer} and registers its serializer. + * + * @param kryo the {@link Kryo} instance to set the serializer on + */ + public static void registerSerializers(final Kryo kryo) { + final HashMultimapSerializer serializer = new HashMultimapSerializer(); + kryo.register(HashMultimap.class, serializer); + } +} diff --git a/src/main/java/de/javakaffee/kryoserializers/guava/LinkedHashMultimapSerializer.java b/src/main/java/de/javakaffee/kryoserializers/guava/LinkedHashMultimapSerializer.java new file mode 100644 index 00000000..7099e8b7 --- /dev/null +++ b/src/main/java/de/javakaffee/kryoserializers/guava/LinkedHashMultimapSerializer.java @@ -0,0 +1,45 @@ +package de.javakaffee.kryoserializers.guava; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.Serializer; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; + +import com.google.common.collect.LinkedHashMultimap; + +/** + * A kryo {@link Serializer} for guava-libraries {@link LinkedHashMultimap}. + * This does not yet support {@link Kryo#copy(java.lang.Object)}. + */ +public class LinkedHashMultimapSerializer extends MultimapSerializerBase> { + + private static final boolean DOES_NOT_ACCEPT_NULL = false; + + private static final boolean IMMUTABLE = false; + + public LinkedHashMultimapSerializer() { + super(DOES_NOT_ACCEPT_NULL, IMMUTABLE); + } + + @Override + public void write(Kryo kryo, Output output, LinkedHashMultimap multimap) { + writeMultimap(kryo, output, multimap); + } + + @Override + public LinkedHashMultimap read(Kryo kryo, Input input, Class> type) { + final LinkedHashMultimap multimap = LinkedHashMultimap.create(); + readMultimap(kryo, input, multimap); + return multimap; + } + + /** + * Creates a new {@link LinkedHashMultimapSerializer} and registers its serializer. + * + * @param kryo the {@link Kryo} instance to set the serializer on + */ + public static void registerSerializers(final Kryo kryo) { + final LinkedHashMultimapSerializer serializer = new LinkedHashMultimapSerializer(); + kryo.register(LinkedHashMultimap.class, serializer); + } +} diff --git a/src/main/java/de/javakaffee/kryoserializers/guava/LinkedListMultimapSerializer.java b/src/main/java/de/javakaffee/kryoserializers/guava/LinkedListMultimapSerializer.java new file mode 100644 index 00000000..5599b023 --- /dev/null +++ b/src/main/java/de/javakaffee/kryoserializers/guava/LinkedListMultimapSerializer.java @@ -0,0 +1,45 @@ +package de.javakaffee.kryoserializers.guava; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.Serializer; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; + +import com.google.common.collect.LinkedListMultimap; + +/** + * A kryo {@link Serializer} for guava-libraries {@link LinkedListMultimap}. + * This does not yet support {@link Kryo#copy(java.lang.Object)}. + */ +public class LinkedListMultimapSerializer extends MultimapSerializerBase> { + + private static final boolean DOES_NOT_ACCEPT_NULL = false; + + private static final boolean IMMUTABLE = false; + + public LinkedListMultimapSerializer() { + super(DOES_NOT_ACCEPT_NULL, IMMUTABLE); + } + + @Override + public void write(Kryo kryo, Output output, LinkedListMultimap multimap) { + writeMultimap(kryo, output, multimap); + } + + @Override + public LinkedListMultimap read(Kryo kryo, Input input, Class> type) { + final LinkedListMultimap multimap = LinkedListMultimap.create(); + readMultimap(kryo, input, multimap); + return multimap; + } + + /** + * Creates a new {@link LinkedListMultimapSerializer} and registers its serializer. + * + * @param kryo the {@link Kryo} instance to set the serializer on + */ + public static void registerSerializers(final Kryo kryo) { + final LinkedListMultimapSerializer serializer = new LinkedListMultimapSerializer(); + kryo.register(LinkedListMultimap.class, serializer); + } +} diff --git a/src/main/java/de/javakaffee/kryoserializers/guava/MultimapSerializerBase.java b/src/main/java/de/javakaffee/kryoserializers/guava/MultimapSerializerBase.java new file mode 100644 index 00000000..67bfe88c --- /dev/null +++ b/src/main/java/de/javakaffee/kryoserializers/guava/MultimapSerializerBase.java @@ -0,0 +1,34 @@ +package de.javakaffee.kryoserializers.guava; + +import java.util.Map; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.Serializer; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; + +import com.google.common.collect.Multimap; + +public abstract class MultimapSerializerBase> extends Serializer { + + public MultimapSerializerBase(boolean acceptsNull, boolean immutable) { + super(acceptsNull, immutable); + } + + protected void writeMultimap(Kryo kryo, Output output, Multimap multimap) { + output.writeInt(multimap.size(), true); + for (final Map.Entry entry : multimap.entries()) { + kryo.writeClassAndObject(output, entry.getKey()); + kryo.writeClassAndObject(output, entry.getValue()); + } + } + + protected void readMultimap(Kryo kryo, Input input, Multimap multimap) { + final int size = input.readInt(true); + for (int i = 0; i < size; ++i) { + final K key = (K) kryo.readClassAndObject(input); + final V value = (V) kryo.readClassAndObject(input); + multimap.put(key, value); + } + } +} diff --git a/src/main/java/de/javakaffee/kryoserializers/guava/TreeMultimapSerializer.java b/src/main/java/de/javakaffee/kryoserializers/guava/TreeMultimapSerializer.java new file mode 100644 index 00000000..7bae7d12 --- /dev/null +++ b/src/main/java/de/javakaffee/kryoserializers/guava/TreeMultimapSerializer.java @@ -0,0 +1,47 @@ +package de.javakaffee.kryoserializers.guava; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.Serializer; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; + +import com.google.common.collect.TreeMultimap; + +/** + * A kryo {@link Serializer} for guava-libraries {@link TreeMultimap}. + * The default comparator is assumed so the multimaps are not null-safe. + * This does not yet support {@link Kryo#copy(java.lang.Object)}. + */ +public class TreeMultimapSerializer extends MultimapSerializerBase> { + + /* assumes default comparator */ + private static final boolean DOES_NOT_ACCEPT_NULL = true; + + private static final boolean IMMUTABLE = false; + + public TreeMultimapSerializer() { + super(DOES_NOT_ACCEPT_NULL, IMMUTABLE); + } + + @Override + public void write(Kryo kryo, Output output, TreeMultimap multimap) { + writeMultimap(kryo, output, multimap); + } + + @Override + public TreeMultimap read(Kryo kryo, Input input, Class> type) { + final TreeMultimap multimap = TreeMultimap.create(); + readMultimap(kryo, input, multimap); + return multimap; + } + + /** + * Creates a new {@link TreeMultimapSerializer} and registers its serializer. + * + * @param kryo the {@link Kryo} instance to set the serializer on + */ + public static void registerSerializers(final Kryo kryo) { + final TreeMultimapSerializer serializer = new TreeMultimapSerializer(); + kryo.register(TreeMultimap.class, serializer); + } +} diff --git a/src/test/java/de/javakaffee/kryoserializers/guava/ArrayListMultimapSerializerTest.java b/src/test/java/de/javakaffee/kryoserializers/guava/ArrayListMultimapSerializerTest.java new file mode 100644 index 00000000..c8a2a597 --- /dev/null +++ b/src/test/java/de/javakaffee/kryoserializers/guava/ArrayListMultimapSerializerTest.java @@ -0,0 +1,30 @@ +package de.javakaffee.kryoserializers.guava; + +import de.javakaffee.kryoserializers.KryoTest; + +import com.esotericsoftware.kryo.Kryo; + +import com.google.common.collect.ArrayListMultimap; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class ArrayListMultimapSerializerTest extends MultimapSerializerTestBase { + + private Kryo _kryo; + + @BeforeClass + public void initializeKyroWithSerializer() { + _kryo = new Kryo(); + ArrayListMultimapSerializer.registerSerializers(_kryo); + } + + @Test(dataProvider = "Google Guava multimaps") + public void testMultimap(Object[] contents) { + final ArrayListMultimap multimap = ArrayListMultimap.create(); + populateMultimap(multimap, contents); + final byte[] serialized = KryoTest.serialize(_kryo, multimap); + final ArrayListMultimap deserialized = KryoTest.deserialize(_kryo, serialized, ArrayListMultimap.class); + assertEqualMultimaps(false, true, deserialized, multimap); + } +} diff --git a/src/test/java/de/javakaffee/kryoserializers/guava/HashMultimapSerializerTest.java b/src/test/java/de/javakaffee/kryoserializers/guava/HashMultimapSerializerTest.java new file mode 100644 index 00000000..215d3977 --- /dev/null +++ b/src/test/java/de/javakaffee/kryoserializers/guava/HashMultimapSerializerTest.java @@ -0,0 +1,30 @@ +package de.javakaffee.kryoserializers.guava; + +import de.javakaffee.kryoserializers.KryoTest; + +import com.esotericsoftware.kryo.Kryo; + +import com.google.common.collect.HashMultimap; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class HashMultimapSerializerTest extends MultimapSerializerTestBase { + + private Kryo _kryo; + + @BeforeClass + public void initializeKyroWithSerializer() { + _kryo = new Kryo(); + HashMultimapSerializer.registerSerializers(_kryo); + } + + @Test(dataProvider = "Google Guava multimaps") + public void testMultimap(Object[] contents) { + final HashMultimap multimap = HashMultimap.create(); + populateMultimap(multimap, contents); + final byte[] serialized = KryoTest.serialize(_kryo, multimap); + final HashMultimap deserialized = KryoTest.deserialize(_kryo, serialized, HashMultimap.class); + assertEqualMultimaps(false, false, deserialized, multimap); + } +} diff --git a/src/test/java/de/javakaffee/kryoserializers/guava/LinkedHashMultimapSerializerTest.java b/src/test/java/de/javakaffee/kryoserializers/guava/LinkedHashMultimapSerializerTest.java new file mode 100644 index 00000000..a8a4ceff --- /dev/null +++ b/src/test/java/de/javakaffee/kryoserializers/guava/LinkedHashMultimapSerializerTest.java @@ -0,0 +1,30 @@ +package de.javakaffee.kryoserializers.guava; + +import de.javakaffee.kryoserializers.KryoTest; + +import com.esotericsoftware.kryo.Kryo; + +import com.google.common.collect.LinkedHashMultimap; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class LinkedHashMultimapSerializerTest extends MultimapSerializerTestBase { + + private Kryo _kryo; + + @BeforeClass + public void initializeKyroWithSerializer() { + _kryo = new Kryo(); + LinkedHashMultimapSerializer.registerSerializers(_kryo); + } + + @Test(dataProvider = "Google Guava multimaps") + public void testMultimap(Object[] contents) { + final LinkedHashMultimap multimap = LinkedHashMultimap.create(); + populateMultimap(multimap, contents); + final byte[] serialized = KryoTest.serialize(_kryo, multimap); + final LinkedHashMultimap deserialized = KryoTest.deserialize(_kryo, serialized, LinkedHashMultimap.class); + assertEqualMultimaps(true, true, deserialized, multimap); + } +} diff --git a/src/test/java/de/javakaffee/kryoserializers/guava/LinkedListMultimapSerializerTest.java b/src/test/java/de/javakaffee/kryoserializers/guava/LinkedListMultimapSerializerTest.java new file mode 100644 index 00000000..129d4a03 --- /dev/null +++ b/src/test/java/de/javakaffee/kryoserializers/guava/LinkedListMultimapSerializerTest.java @@ -0,0 +1,30 @@ +package de.javakaffee.kryoserializers.guava; + +import de.javakaffee.kryoserializers.KryoTest; + +import com.esotericsoftware.kryo.Kryo; + +import com.google.common.collect.LinkedListMultimap; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class LinkedListMultimapSerializerTest extends MultimapSerializerTestBase { + + private Kryo _kryo; + + @BeforeClass + public void initializeKyroWithSerializer() { + _kryo = new Kryo(); + LinkedListMultimapSerializer.registerSerializers(_kryo); + } + + @Test(dataProvider = "Google Guava multimaps") + public void testMultimap(Object[] contents) { + final LinkedListMultimap multimap = LinkedListMultimap.create(); + populateMultimap(multimap, contents); + final byte[] serialized = KryoTest.serialize(_kryo, multimap); + final LinkedListMultimap deserialized = KryoTest.deserialize(_kryo, serialized, LinkedListMultimap.class); + assertEqualMultimaps(true, true, deserialized, multimap); + } +} diff --git a/src/test/java/de/javakaffee/kryoserializers/guava/MultimapSerializerTestBase.java b/src/test/java/de/javakaffee/kryoserializers/guava/MultimapSerializerTestBase.java new file mode 100644 index 00000000..1371427d --- /dev/null +++ b/src/test/java/de/javakaffee/kryoserializers/guava/MultimapSerializerTestBase.java @@ -0,0 +1,70 @@ +package de.javakaffee.kryoserializers.guava; + +import com.google.common.collect.Multimap; +import java.util.ArrayList; +import java.util.List; + +import org.testng.Assert; +import org.testng.annotations.DataProvider; + +public abstract class MultimapSerializerTestBase { + + protected void populateMultimap(Multimap multimap, Object[] contents) { + for (int index = 0; index < contents.length;) { + multimap.put((K) contents[index++], (V) contents[index++]); + } + } + + protected void assertEqualMultimaps(boolean orderedKeys, boolean orderedValues, + Multimap actual, Multimap expected) { + if (orderedKeys) { + Assert.assertEquals(actual.keySet(), expected.keySet()); + } else { + Assert.assertEqualsNoOrder(actual.keySet().toArray(), expected.keySet().toArray()); + } + for (final K key : expected.keySet()) { + if (orderedValues) { + Assert.assertEquals(actual.get(key), expected.get(key)); + } else { + Assert.assertEqualsNoOrder(actual.get(key).toArray(), expected.get(key).toArray()); + } + } + } + + @DataProvider(name = "Google Guava multimaps") + public Object[][][] getMultimaps() { + final Object[][] multimaps = new Object[][]{new Object[]{}, + new Object[]{"foo", "bar"}, + new Object[]{"foo", null}, + new Object[]{null, "bar"}, + new Object[]{null, null}, + new Object[]{"new", Thread.State.NEW, "run", Thread.State.RUNNABLE}, + new Object[]{1.0, "foo", null, "bar", 1.0, null, null, "baz", 1.0, "wibble"}, + new Object[]{'a', 1, 'b', 2, 'c', 3, 'a', 4, 'b', 5}, + new Object[]{'a', 1, 'b', 2, 'c', 3, 'a', 1, 'b', 2}}; + final Object[][][] toProvide = new Object[multimaps.length][][]; + int index = 0; + for (final Object[] multimap : multimaps) { + toProvide[index++] = new Object[][]{multimap}; + } + return toProvide; + } + + @DataProvider(name = "Google Guava multimaps (no nulls)") + public Object[][][] getMultimapsNoNulls() { + final List multimaps = new ArrayList(); + for (final Object[][] multimap : getMultimaps()) { + boolean isNull = false; + for (final Object element : multimap[0]) { + if (element == null) { + isNull = true; + break; + } + } + if (!isNull) { + multimaps.add(multimap); + } + } + return multimaps.toArray(new Object[multimaps.size()][][]); + } +} diff --git a/src/test/java/de/javakaffee/kryoserializers/guava/TreeMultimapSerializerTest.java b/src/test/java/de/javakaffee/kryoserializers/guava/TreeMultimapSerializerTest.java new file mode 100644 index 00000000..b0b81534 --- /dev/null +++ b/src/test/java/de/javakaffee/kryoserializers/guava/TreeMultimapSerializerTest.java @@ -0,0 +1,30 @@ +package de.javakaffee.kryoserializers.guava; + +import de.javakaffee.kryoserializers.KryoTest; + +import com.esotericsoftware.kryo.Kryo; + +import com.google.common.collect.TreeMultimap; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class TreeMultimapSerializerTest extends MultimapSerializerTestBase { + + private Kryo _kryo; + + @BeforeClass + public void initializeKyroWithSerializer() { + _kryo = new Kryo(); + TreeMultimapSerializer.registerSerializers(_kryo); + } + + @Test(dataProvider = "Google Guava multimaps (no nulls)") + public void testMultimap(Object[] contents) { + final TreeMultimap multimap = TreeMultimap.create(); + populateMultimap(multimap, contents); + final byte[] serialized = KryoTest.serialize(_kryo, multimap); + final TreeMultimap deserialized = KryoTest.deserialize(_kryo, serialized, TreeMultimap.class); + assertEqualMultimaps(true, true, deserialized, multimap); + } +}