diff --git a/net/src/main/java/com/zfoo/net/util/ReadOnlyTreeMapIntLong.java b/net/src/main/java/com/zfoo/net/util/ReadOnlyTreeMapIntLong.java new file mode 100644 index 00000000..d2657dc4 --- /dev/null +++ b/net/src/main/java/com/zfoo/net/util/ReadOnlyTreeMapIntLong.java @@ -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 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]; + } + +} diff --git a/net/src/test/java/com/zfoo/net/util/ReadOnlyTreeMapIntLongTest.java b/net/src/test/java/com/zfoo/net/util/ReadOnlyTreeMapIntLongTest.java new file mode 100644 index 00000000..bbdbd4e2 --- /dev/null +++ b/net/src/test/java/com/zfoo/net/util/ReadOnlyTreeMapIntLongTest.java @@ -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(); + 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); + } + +}