net.openhft.chronicle.hash.serialization.SetMarshaller Maven / Gradle / Ivy
/*
* Copyright 2012-2018 Chronicle Map Contributors
*
* Licensed 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 net.openhft.chronicle.hash.serialization;
import net.openhft.chronicle.bytes.Bytes;
import net.openhft.chronicle.wire.WireIn;
import net.openhft.chronicle.wire.WireOut;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashSet;
import java.util.Set;
import static net.openhft.chronicle.hash.serialization.StatefulCopyable.copyIfNeeded;
/**
* Marshaller of {@code Set}. Uses {@link HashSet} (hence default element objects' equality and
* {@code hashCode()}) as the set implementation to deserialize into.
*
*
This marshaller supports multimap emulation on top of Chronicle Map, that is possible but
* inefficient. See the
* README section.
*
*
Usage:
{@code
* SetMarshaller valueMarshaller = SetMarshaller.of(
* new StringBytesReader(), CharSequenceBytesWriter.INSTANCE);
* ChronicleMap> map = ChronicleMap
* .of(String.class, (Class>) (Class) Set.class)
* .averageKey("fruits")
* .valueMarshaller(valueMarshaller)
* .averageValue(ImmutableSet.of("apples", "bananas", "grapes"))
* .entries(10_000)
* .create();
* }
*
*
Look for pre-defined element marshallers in {@link
* net.openhft.chronicle.hash.serialization.impl} package. This package is not included into
* Javadocs, but present in Chronicle Map distribution. If there are no existing marshallers for
* your {@code Set} element type, define {@link BytesReader} and {@link BytesWriter} yourself.
*
* @param the element type of serialized Sets
* @see ListMarshaller
* @see MapMarshaller
*/
public final class SetMarshaller
implements BytesReader>, BytesWriter>, StatefulCopyable> {
// Config fields
private BytesReader elementReader;
private BytesWriter super T> elementWriter;
/**
* Cache field
*/
private transient Deque orderedElements;
/**
* Constructs a {@code SetMarshaller} with the given set elements' serializers.
*
*
Use static factory {@link #of(BytesReader, BytesWriter)} instead of this constructor
* directly.
*
* @param elementReader set elements' reader
* @param elementWriter set elements' writer
*/
public SetMarshaller(BytesReader elementReader, BytesWriter super T> elementWriter) {
this.elementReader = elementReader;
this.elementWriter = elementWriter;
initTransients();
}
/**
* Returns a {@code SetMarshaller} which uses the given set elements' serializers.
*
* @param elementReader set elements' reader
* @param elementWriter set elements' writer
* @param type of set elements
* @return a {@code SetMarshaller} which uses the given set elements' serializers
*/
public static SetMarshaller of(
BytesReader elementReader, BytesWriter super T> elementWriter) {
return new SetMarshaller<>(elementReader, elementWriter);
}
/**
* Returns a {@code SetMarshaller} which uses the given marshaller as both reader and writer of
* set elements. Example:
* ChronicleMap
* .of(String.class,{@code (Class>)} ((Class) Set.class))
* .valueMarshaller(SetMarshaller.of(IntegerMarshaller.INSTANCE))
* ...
*
* @param elementMarshaller set elements' marshaller
* @param type of set elements
* @param type of set elements' marshaller
* @return a {@code SetMarshaller} which uses the given set elements' marshaller
*/
public static & BytesWriter super T>> SetMarshaller of(
M elementMarshaller) {
return of(elementMarshaller, elementMarshaller);
}
private void initTransients() {
orderedElements = new ArrayDeque<>();
}
@NotNull
@Override
public Set read(Bytes in, @Nullable Set using) {
int size = in.readInt();
if (using == null) {
using = new HashSet<>((int) (size / 0.75));
for (int i = 0; i < size; i++) {
using.add(elementReader.read(in, null));
}
} else {
orderedElements.addAll(using);
using.clear();
for (int i = 0; i < size; i++) {
using.add(elementReader.read(in, orderedElements.pollFirst()));
}
orderedElements.clear(); // for GC, avoid zombie object links
}
return using;
}
@Override
public void write(Bytes out, @NotNull Set toWrite) {
out.writeInt(toWrite.size());
toWrite.forEach(e -> elementWriter.write(out, e));
}
@Override
public SetMarshaller copy() {
return new SetMarshaller<>(copyIfNeeded(elementReader), copyIfNeeded(elementWriter));
}
@Override
public void readMarshallable(@NotNull WireIn wireIn) {
elementReader = wireIn.read(() -> "elementReader").typedMarshallable();
elementWriter = wireIn.read(() -> "elementWriter").typedMarshallable();
initTransients();
}
@Override
public void writeMarshallable(@NotNull WireOut wireOut) {
wireOut.write(() -> "elementReader").typedMarshallable(elementReader);
wireOut.write(() -> "elementWriter").typedMarshallable(elementWriter);
}
}