Java 在Scala中使用盒装/原子值和编年史地图
我们使用编年史地图在大量不同的存储中支持堆外持久性,但在最简单的用例中遇到了一些问题 首先,以下是我为简化创建而编写的帮助程序:Java 在Scala中使用盒装/原子值和编年史地图,java,scala,chronicle,chronicle-map,Java,Scala,Chronicle,Chronicle Map,我们使用编年史地图在大量不同的存储中支持堆外持久性,但在最简单的用例中遇到了一些问题 首先,以下是我为简化创建而编写的帮助程序: import java.io.File import java.util.concurrent.atomic.AtomicLong import com.madhukaraphatak.sizeof.SizeEstimator import net.openhft.chronicle.map.{ChronicleMap, ChronicleMapBuilder}
import java.io.File
import java.util.concurrent.atomic.AtomicLong
import com.madhukaraphatak.sizeof.SizeEstimator
import net.openhft.chronicle.map.{ChronicleMap, ChronicleMapBuilder}
import scala.reflect.ClassTag
object ChronicleHelper {
def estimateSizes[Key, Value](data: Iterator[(Key, Value)], keyEstimator: AnyRef => Long = defaultEstimator, valueEstimator: AnyRef => Long = defaultEstimator): (Long, Long, Long) = {
println("Estimating sizes...")
val entries = new AtomicLong(1)
val keySum = new AtomicLong(1)
val valueSum = new AtomicLong(1)
var i = 0
val GroupSize = 5000
data.grouped(GroupSize).foreach { chunk =>
chunk.par.foreach { case (key, value) =>
entries.incrementAndGet()
keySum.addAndGet(keyEstimator(key.asInstanceOf[AnyRef]))
valueSum.addAndGet(valueEstimator(value.asInstanceOf[AnyRef]))
}
i += 1
println("Progress:" + i * GroupSize)
}
(entries.get(), keySum.get() / entries.get(), valueSum.get() / entries.get())
}
def defaultEstimator(v: AnyRef): Long = SizeEstimator.estimate(v)
def createMap[Key: ClassTag, Value: ClassTag](data: => Iterator[(Key, Value)], file: File): ChronicleMap[Key, Value] = {
val keyClass = implicitly[ClassTag[Key]].runtimeClass.asInstanceOf[Class[Key]]
val valueClass = implicitly[ClassTag[Value]].runtimeClass.asInstanceOf[Class[Value]]
val (entries, averageKeySize, averageValueSize) = estimateSizes(data)
val builder = ChronicleMapBuilder.of(keyClass, valueClass)
.entries(entries)
.averageKeySize(averageKeySize)
.averageValueSize(averageValueSize)
.asInstanceOf[ChronicleMapBuilder[Key, Value]]
val cmap = builder.createPersistedTo(file)
val GroupSize = 5000
println("Inserting data...")
var i = 0
data.grouped(GroupSize).foreach { chunk =>
chunk.par.foreach { case (key, value) =>
cmap.put(key, value)
}
i += 1
println("Progress:" + i * GroupSize)
}
cmap
}
def empty[Key: ClassTag, Value: ClassTag]: ChronicleMap[Key, Value] = {
val keyClass = implicitly[ClassTag[Key]].runtimeClass.asInstanceOf[Class[Key]]
val valueClass = implicitly[ClassTag[Value]].runtimeClass.asInstanceOf[Class[Value]]
ChronicleMapBuilder.of(keyClass, valueClass).create()
}
def loadMap[Key: ClassTag, Value: ClassTag](file: File): ChronicleMap[Key, Value] = {
val keyClass = implicitly[ClassTag[Key]].runtimeClass.asInstanceOf[Class[Key]]
val valueClass = implicitly[ClassTag[Value]].runtimeClass.asInstanceOf[Class[Value]]
ChronicleMapBuilder.of(keyClass, valueClass).createPersistedTo(file)
}
}
它用于对象大小估计。以下是我们希望支持的使用方式:
object TestChronicle {
def main(args: Array[String]) {
def dataIterator: Iterator[(String, Int)] = (1 to 5000).toIterator.zipWithIndex.map(x => x.copy(_1 = x._1.toString))
ChronicleHelper.createMap[String, Int](dataIterator, new File("/tmp/test.map"))
}
}
但它抛出了一个例外:
[错误]线程“main”java.lang.ClassCastException中出现异常:Key
必须是int,但在处是类java.lang.Integer[error]
net.openhft.chronicle.hash.impl.VanillaChronicleHash.checkKey(VanillaChronicleHash.java:661)
[错误]在
net.openhft.chronicle.map.VanillaChronicleMap.queryContext(VanillaChronicleMap.java:281)
[错误]在
net.openhft.chronicle.map.VanillaChronicleMap.put(VanillaChronicleMap.java:390)
[错误]在
我可以看出,这可能与Scala的Int的原子性有关,而不是与Java的Integer有关,但我如何绕过它呢
Scala 2.11.7
编年史地图3.8.0
Iterator[(String,Int)]
(而不是Iterator[(Int,String)]
),因为键类型是String
,值类型是Int
,而错误消息是关于键的类型(Int/Integer)键必须是%type%
,则表示您在(keyType,valueType)语句的第一个编年史映射生成器中配置了该类型。因此,在您的例子中,这意味着您配置了int.class
(class
对象,表示Java中的原语int
类型),这是不允许的,并且为map的方法提供了Java.lang.Integer
实例(可能您提供了原语int
s,但由于装箱,它们变成了Integer
),这是允许的。您应该确保您提供了java.lang.Integer.class
(或其他一些Scala类)给ChronicleMapBuilder.of(keyType,valueType)
调用
编年史映射生成器
配置的自定义序列化程序。因此,使用有关键/值“大小”的任何信息来配置编年史映射(而不是编年史映射本身)是脆弱的。您可以使用以下过程更可靠地估计大小:
public static <V> double averageValueSize(Class<V> valueClass, Iterable<V> values) {
try (ChronicleMap<Integer, V> testMap = ChronicleMap.of(Integer.class, valueClass)
// doesn't matter, anyway not a single value will be written to a map
.averageValueSize(1)
.entries(1)
.create()) {
LongSummaryStatistics statistics = new LongSummaryStatistics();
for (V value : values) {
try (MapSegmentContext<Integer, V, ?> c = testMap.segmentContext(0)) {
statistics.accept(c.wrapValueAsData(value).size());
}
}
return statistics.getAverage();
}
}
public静态双平均值大小(Class-valueClass,Iterable-values){
try(chronicmap testMap=chronicmap.of(Integer.class,valueClass)
//不管怎样,没有一个值会写入映射
.平均值大小(1)
.参赛作品(1)
.create()){
LongSummaryStatistics=新的LongSummaryStatistics();
对于(V值:值){
try(MapSegmentContext c=testMap.segmentContext(0)){
accept(c.wrapValueAsData(value.size());
}
}
返回统计信息。getAverage();
}
}
您可以在此测试中找到它:
这个程序很粗糙,但现在没有更好的选择- 如果您的键或值属于基本类型(整数、长整数、双精度,但已装箱),或任何其他大小相同的类型,您不应该使用
方法,最好使用averageKey/averageValue/averageKeySize/averageValueSize
方法。特别是对于constantKeySizeBySample/constantValueSizeBySample
,java.lang.Integer
和Long
方法,即使不需要这样做d、 编年史地图已经知道这些类型的大小是不断变化的Double