feat[treemap]: fast read only tree map

This commit is contained in:
godotg
2023-04-28 22:09:16 +08:00
parent 0041a7ad31
commit 3af5ffbd46
2 changed files with 179 additions and 0 deletions
@@ -0,0 +1,127 @@
/*
* Copyright (C) 2020 The zfoo Authors
* 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 com.zfoo.net.util;
import com.zfoo.protocol.collection.ArrayUtils;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.TreeMap;
/**
* @author godotg
*/
public class ReadOnlyTreeMapIntLong {
private int[] keys;
private long[] values;
public ReadOnlyTreeMapIntLong(TreeMap<Integer, Long> treeMap) {
var size = treeMap.size();
keys = new int[size];
values = new long[size];
// TreeMap遍历是有序的,直接放入数组中就能直接得到从小到大的有序数组
var index = 0;
for (var entry : treeMap.entrySet()) {
var key = entry.getKey();
var value = entry.getValue();
keys[index] = key;
values[index] = value;
index++;
}
}
public boolean contains(int key) {
return indexOf(key) >= 0;
}
public long get(int key) {
var index = indexOf(key);
if (index < 0) {
throw new NoSuchElementException();
}
return values[index];
}
public long getByIndex(int index) {
Objects.checkIndex(index, values.length);
return values[index];
}
public int indexOf(int key) {
var low = 0;
var high = keys.length - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (keys[mid] == key) {
return mid;
} else if (keys[mid] < key) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return -1;
}
public int indexOfCeilingKey(int key) {
if (ArrayUtils.isEmpty(keys)) {
return -1;
}
var size = keys.length;
// 目标数小于或等于数组的第一个元素
if (key <= keys[0]) {
return 0;
}
// 目标数大于数组的第一个元素
if (key > keys[size - 1]) {
return 0;
}
// 二分查找目标数
var low = 0;
var high = size - 1;
var nearestIndex = -1;
while (low <= high) {
var mid = low + (high - low) / 2;
if (keys[mid] == key) {
nearestIndex = mid;
break;
} else if (keys[mid] < key) {
low = mid + 1;
} else {
high = mid - 1;
nearestIndex = mid;
}
}
// 如果找到了目标数,那么它的邻近最大值就是目标数本身
if (nearestIndex == -1) {
return 0;
}
return nearestIndex;
}
public long getValueByCeilingKey(int key) {
var index = indexOfCeilingKey(key);
if (index < 0) {
throw new NoSuchElementException();
}
return values[index];
}
}
@@ -0,0 +1,52 @@
/*
* Copyright (C) 2020 The zfoo Authors
* 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 com.zfoo.net.util;
import org.junit.Assert;
import org.junit.Test;
import java.util.TreeMap;
/**
* @author godotg
*/
public class ReadOnlyTreeMapIntLongTest {
@Test
public void test() {
var treeMap = new TreeMap<Integer, Long>();
for (var i = 0; i < 100; i = i + 2) {
treeMap.put(i, (long) i);
}
var fastTreeMap = new ReadOnlyTreeMapIntLong(treeMap);
Assert.assertEquals(fastTreeMap.get(0), 0);
Assert.assertEquals(fastTreeMap.get(8), 8);
Assert.assertEquals(fastTreeMap.get(98), 98);
Assert.assertFalse(fastTreeMap.contains(-1));
Assert.assertFalse(fastTreeMap.contains(1));
Assert.assertFalse(fastTreeMap.contains(100));
Assert.assertTrue(fastTreeMap.contains(44));
Assert.assertEquals(fastTreeMap.getValueByCeilingKey(-1), 0);
Assert.assertEquals(fastTreeMap.getValueByCeilingKey(-100), 0);
Assert.assertEquals(fastTreeMap.getValueByCeilingKey(0), 0);
Assert.assertEquals(fastTreeMap.getValueByCeilingKey(1), 2);
Assert.assertEquals(fastTreeMap.getValueByCeilingKey(45), 46);
Assert.assertEquals(fastTreeMap.getValueByCeilingKey(97), 98);
Assert.assertEquals(fastTreeMap.getValueByCeilingKey(100), 0);
Assert.assertEquals(fastTreeMap.indexOfCeilingKey(-1), 0);
Assert.assertEquals(fastTreeMap.indexOfCeilingKey(0), 0);
Assert.assertEquals(fastTreeMap.indexOfCeilingKey(1), 1);
}
}