自定义 ComparableBinding 实现

Custom ComparableBinding implementation

是否可以实现提供自己排序的自定义 ComparableBinding/ByteIterable 组合?我将如何在系统中注册它? 另外,仅在用于键时不实现 ByteIterable.subIterable(final int offset, final int length) 方法是否安全?在我的用例中没有有效的子迭代,因为这会破坏排序。

下面的TestStore.test()方法不足以使Cursor按升序移动,因为底部的assert语句失败了。它在使用内置 IntegerBinding.intToEntry(index) 生成密钥时有效,但是:

import jetbrains.exodus.ArrayByteIterable;
import jetbrains.exodus.ByteIterable;
import jetbrains.exodus.ByteIterator;
import org.jetbrains.annotations.NotNull;

import java.nio.charset.Charset;

public class TestKey implements ByteIterable {

    private final int value;
    private final byte[] bytes;

    public TestKey(int value) {
        this.value = value;
        this.bytes = Integer.toString(value).getBytes(Charset.forName("utf-8"));
    }

    @Override
    public int compareTo(@NotNull ByteIterable o) {
        return Integer.compare(value, ((TestKey)o).value);
    }

    @Override
    public ByteIterator iterator() {
        return new ArrayByteIterable(bytes).iterator();
    }

    @Override
    public byte[] getBytesUnsafe() {
        return bytes;
    }

    @Override
    public int getLength() {
        return bytes.length;
    }

    @Override
    public @NotNull ByteIterable subIterable(int offset, int length) {
        throw new UnsupportedOperationException("subIterable");
    }
}



import jetbrains.exodus.ByteIterable;
import jetbrains.exodus.bindings.IntegerBinding;
import jetbrains.exodus.bindings.StringBinding;
import jetbrains.exodus.env.Cursor;
import jetbrains.exodus.env.Environment;
import jetbrains.exodus.env.Environments;
import jetbrains.exodus.env.Store;
import jetbrains.exodus.env.StoreConfig;
import jetbrains.exodus.env.Transaction;
import jetbrains.exodus.env.TransactionalExecutable;
import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.util.Arrays;
import java.util.UUID;

public class TestStore {


    private Store store;
    private Environment environment;

    public TestStore(File folder) {
        environment = Environments.newContextualInstance(folder);
        environment.executeInTransaction(new TransactionalExecutable() {
            @Override
            public void execute(@NotNull Transaction txn) {
                store = environment.openStore(
                        UUID.randomUUID().toString(),
                        StoreConfig.WITHOUT_DUPLICATES,
                        txn,
                        true);
            }
        });
    }

    public void test() {

        int count = 1000;

        int[] orig = new int[count];
        int[] iterated = new int[count];

        for(int i = 0; i < count; i++) {
            final int index = i;
            environment.executeInTransaction(new TransactionalExecutable() {
                @Override
                public void execute(@NotNull Transaction txn) {
                    orig[index] = index;
                    store.put(txn,
                            new TestKey(index),
                       //     IntegerBinding.intToEntry(index),
                            StringBinding.stringToEntry(Integer.toString(index))
                    );
                }
            });
        }


        environment.executeInTransaction(new TransactionalExecutable() {
            @Override
            public void execute(@NotNull Transaction txn) {
                int offset = 0;
                try(Cursor cursor = store.openCursor(txn)) {
                    while(cursor.getNext()) {
                        ByteIterable key = cursor.getKey();
                        ByteIterable value = cursor.getValue();
                        iterated[offset++] = Integer.parseInt(StringBinding.entryToString(value));
                    }
                }
            }
        });

        assert Arrays.equals(orig, iterated);
    }

}

如果使用环境 API,则无需关心 keys/values 的顺序,因为 API 只接受数据,因为 ByteIterables instances, so it's agnostic to how the ByteIterables are generated. Also there is no need to register a binding somehow, it can be defined in the application. The only drawback of custom ordering may be range search 会产生有点奇怪的结果。

至于subIterable()方法,请看FixedLengthByteIterable。如果您仅将自定义 ByteIterables 用作键,则不实现该方法是安全的,尽管在 API.

中对此没有明确保证

至于您的测试,TestKey class 定义了一个不明确的顺序。一方面,它将键的顺序定义为自然整数顺序。另一方面,在二进制表示中,它按自然整数的字符串表示排序。如果您需要存储整数的字符串表示,请用零填充它以达到一定的准确性。在这种情况下,您甚至不必为键声明 class。例如,对于 int key,10 位 ByteIterables (keyEntry) 可以计算如下:

final DecimalFormat format = (DecimalFormat) NumberFormat.getIntegerInstance();
format.applyPattern("0000000000");
final ByteIterable keyEntry = StringBinding.stringToEntry(format.format(key));