mirror of
https://github.com/tiennm99/zfoo.git
synced 2026-10-13 04:15:22 +00:00
feat[map]: add primitive int-long tree map
This commit is contained in:
1 parent
3e26ce4a77
commit
626e96f0f3
4 files changed
+1275
No files matched your search
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.packet.common;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import com.zfoo.protocol.IPacket;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* @author godotg
|
||||
* @version 3.0
|
||||
*/
|
||||
public class PairIntLong implements IPacket {
|
||||
|
||||
public static final short PROTOCOL_ID = 110;
|
||||
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private int key;
|
||||
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private long value;
|
||||
|
||||
public static PairIntLong valueOf(int key, long value) {
|
||||
var pair = new PairIntLong();
|
||||
pair.key = key;
|
||||
pair.value = value;
|
||||
return pair;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short protocolId() {
|
||||
return PROTOCOL_ID;
|
||||
}
|
||||
|
||||
public int getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(int key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public long getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(long value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,788 @@
|
||||
/*
|
||||
* 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.net.packet.common.PairIntLong;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author godotg
|
||||
* @version 3.0
|
||||
*/
|
||||
public class TreeMapIntLong {
|
||||
private static final int DEFAULT_CAPACITY = 10;
|
||||
private static final double ENLARGE_SCALE = 2.0;
|
||||
|
||||
private static final boolean BLACK = false;
|
||||
private static final boolean RED = true;
|
||||
private static final int NULL = 0;
|
||||
private static final int DEFAULT_NULL_KEY = (new int[1])[0];
|
||||
private static final long DEFAULT_NULL_VALUE = (new long[1])[0];
|
||||
|
||||
// Arrays are 1-indexed. Index 0 is a null node.
|
||||
private int[] keys;
|
||||
private long[] values;
|
||||
private int[] left;
|
||||
private int[] right;
|
||||
private int[] p;
|
||||
private boolean[] color;
|
||||
|
||||
private int size;
|
||||
private int root;
|
||||
private boolean returnedNull;
|
||||
|
||||
public TreeMapIntLong() {
|
||||
this(DEFAULT_CAPACITY);
|
||||
}
|
||||
|
||||
public TreeMapIntLong(int capacity) {
|
||||
if (capacity < 0) {
|
||||
throw new IllegalArgumentException("Capacity must be non-negative");
|
||||
}
|
||||
capacity++;
|
||||
keys = new int[capacity];
|
||||
values = new long[capacity];
|
||||
left = new int[capacity];
|
||||
right = new int[capacity];
|
||||
p = new int[capacity];
|
||||
color = new boolean[capacity];
|
||||
color[NULL] = BLACK;
|
||||
size = 0;
|
||||
root = NULL;
|
||||
returnedNull = false;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
public boolean isEmpty() {
|
||||
return size == 0;
|
||||
}
|
||||
|
||||
|
||||
public boolean containsKey(int key) {
|
||||
int x = root;
|
||||
while (x != NULL) {
|
||||
if (key < keys[x]) {
|
||||
x = left[x];
|
||||
} else if (key > keys[x]) {
|
||||
x = right[x];
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public long get(int key) {
|
||||
int x = root;
|
||||
while (x != NULL) {
|
||||
if (key < keys[x]) {
|
||||
x = left[x];
|
||||
} else if (key > keys[x]) {
|
||||
x = right[x];
|
||||
} else {
|
||||
returnedNull = false;
|
||||
return values[x];
|
||||
}
|
||||
}
|
||||
returnedNull = true;
|
||||
return DEFAULT_NULL_VALUE;
|
||||
}
|
||||
|
||||
|
||||
public long put(int key, long value) {
|
||||
int y = NULL;
|
||||
int x = root;
|
||||
while (x != NULL) {
|
||||
//noinspection SuspiciousNameCombination
|
||||
y = x;
|
||||
if (key < keys[x]) {
|
||||
x = left[x];
|
||||
} else if (key > keys[x]) {
|
||||
x = right[x];
|
||||
} else {
|
||||
final long oldValue = values[x];
|
||||
values[x] = value;
|
||||
returnedNull = false;
|
||||
return oldValue;
|
||||
}
|
||||
}
|
||||
if (size == color.length - 1) {
|
||||
enlarge();
|
||||
}
|
||||
int z = ++size;
|
||||
keys[z] = key;
|
||||
values[z] = value;
|
||||
p[z] = y;
|
||||
if (y == NULL) {
|
||||
root = z;
|
||||
} else {
|
||||
if (key < keys[y]) {
|
||||
left[y] = z;
|
||||
} else {
|
||||
right[y] = z;
|
||||
}
|
||||
}
|
||||
left[z] = NULL;
|
||||
right[z] = NULL;
|
||||
color[z] = RED;
|
||||
fixAfterAdd(z);
|
||||
returnedNull = true;
|
||||
return DEFAULT_NULL_VALUE;
|
||||
}
|
||||
|
||||
|
||||
public long remove(int key) {
|
||||
int z = root;
|
||||
while (z != NULL) {
|
||||
if (key < keys[z]) {
|
||||
z = left[z];
|
||||
} else if (key > keys[z]) {
|
||||
z = right[z];
|
||||
} else {
|
||||
final long removedValue = values[z];
|
||||
removeNode(z);
|
||||
returnedNull = false;
|
||||
return removedValue;
|
||||
}
|
||||
}
|
||||
returnedNull = true;
|
||||
return DEFAULT_NULL_VALUE;
|
||||
}
|
||||
|
||||
private void removeNode(int z) {
|
||||
int y = (left[z] == NULL || right[z] == NULL) ? z : successorNode(z);
|
||||
int x = (left[y] != NULL) ? left[y] : right[y];
|
||||
p[x] = p[y];
|
||||
if (p[y] == NULL) {
|
||||
root = x;
|
||||
} else {
|
||||
if (y == left[p[y]]) {
|
||||
left[p[y]] = x;
|
||||
} else {
|
||||
right[p[y]] = x;
|
||||
}
|
||||
}
|
||||
if (y != z) {
|
||||
keys[z] = keys[y];
|
||||
values[z] = values[y];
|
||||
}
|
||||
//noinspection PointlessBooleanExpression
|
||||
if (color[y] == BLACK) {
|
||||
fixAfterRemove(x);
|
||||
}
|
||||
// Swap with last
|
||||
if (y != size) {
|
||||
// copy fields
|
||||
keys[y] = keys[size];
|
||||
values[y] = values[size];
|
||||
left[y] = left[size];
|
||||
right[y] = right[size];
|
||||
p[y] = p[size];
|
||||
color[y] = color[size];
|
||||
// fix the children's parents
|
||||
p[left[size]] = y;
|
||||
p[right[size]] = y;
|
||||
// fix one of the parent's children
|
||||
if (left[p[size]] == size) {
|
||||
left[p[size]] = y;
|
||||
} else {
|
||||
right[p[size]] = y;
|
||||
}
|
||||
// fix root
|
||||
if (root == size) {
|
||||
root = y;
|
||||
}
|
||||
}
|
||||
size--;
|
||||
}
|
||||
|
||||
private int successorNode(int x) {
|
||||
if (right[x] != NULL) {
|
||||
x = right[x];
|
||||
while (left[x] != NULL) {
|
||||
x = left[x];
|
||||
}
|
||||
return x;
|
||||
} else {
|
||||
int y = p[x];
|
||||
while (y != NULL && x == right[y]) {
|
||||
//noinspection SuspiciousNameCombination
|
||||
x = y;
|
||||
y = p[y];
|
||||
}
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
private void fixAfterAdd(int z) {
|
||||
while (color[p[z]] == RED) {
|
||||
if (p[z] == left[p[p[z]]]) {
|
||||
int y = right[p[p[z]]];
|
||||
if (color[y] == RED) {
|
||||
color[p[z]] = BLACK;
|
||||
color[y] = BLACK;
|
||||
color[p[p[z]]] = RED;
|
||||
z = p[p[z]];
|
||||
} else {
|
||||
if (z == right[p[z]]) {
|
||||
z = p[z];
|
||||
rotateLeft(z);
|
||||
}
|
||||
color[p[z]] = BLACK;
|
||||
color[p[p[z]]] = RED;
|
||||
rotateRight(p[p[z]]);
|
||||
}
|
||||
} else {
|
||||
int y = left[p[p[z]]];
|
||||
if (color[y] == RED) {
|
||||
color[p[z]] = BLACK;
|
||||
color[y] = BLACK;
|
||||
color[p[p[z]]] = RED;
|
||||
z = p[p[z]];
|
||||
} else {
|
||||
if (z == left[p[z]]) {
|
||||
z = p[z];
|
||||
rotateRight(z);
|
||||
}
|
||||
color[p[z]] = BLACK;
|
||||
color[p[p[z]]] = RED;
|
||||
rotateLeft(p[p[z]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
color[root] = BLACK;
|
||||
}
|
||||
|
||||
private void fixAfterRemove(int x) {
|
||||
while (x != root && color[x] == BLACK) {
|
||||
if (x == left[p[x]]) {
|
||||
int w = right[p[x]];
|
||||
if (color[w] == RED) {
|
||||
color[w] = BLACK;
|
||||
color[p[x]] = RED;
|
||||
rotateLeft(p[x]);
|
||||
w = right[p[x]];
|
||||
}
|
||||
if (color[left[w]] == BLACK && color[right[w]] == BLACK) {
|
||||
color[w] = RED;
|
||||
x = p[x];
|
||||
} else {
|
||||
if (color[right[w]] == BLACK) {
|
||||
color[left[w]] = BLACK;
|
||||
color[w] = RED;
|
||||
rotateRight(w);
|
||||
w = right[p[x]];
|
||||
}
|
||||
color[w] = color[p[x]];
|
||||
color[p[x]] = BLACK;
|
||||
color[right[w]] = BLACK;
|
||||
rotateLeft(p[x]);
|
||||
x = root;
|
||||
}
|
||||
} else {
|
||||
int w = left[p[x]];
|
||||
if (color[w] == RED) {
|
||||
color[w] = BLACK;
|
||||
color[p[x]] = RED;
|
||||
rotateRight(p[x]);
|
||||
w = left[p[x]];
|
||||
}
|
||||
if (color[left[w]] == BLACK && color[right[w]] == BLACK) {
|
||||
color[w] = RED;
|
||||
x = p[x];
|
||||
} else {
|
||||
if (color[left[w]] == BLACK) {
|
||||
color[right[w]] = BLACK;
|
||||
color[w] = RED;
|
||||
rotateLeft(w);
|
||||
w = left[p[x]];
|
||||
}
|
||||
color[w] = color[p[x]];
|
||||
color[p[x]] = BLACK;
|
||||
color[left[w]] = BLACK;
|
||||
rotateRight(p[x]);
|
||||
x = root;
|
||||
}
|
||||
}
|
||||
}
|
||||
color[x] = BLACK;
|
||||
}
|
||||
|
||||
private void rotateLeft(int x) {
|
||||
int y = right[x];
|
||||
right[x] = left[y];
|
||||
if (left[y] != NULL) {
|
||||
p[left[y]] = x;
|
||||
}
|
||||
p[y] = p[x];
|
||||
if (p[x] == NULL) {
|
||||
root = y;
|
||||
} else {
|
||||
if (x == left[p[x]]) {
|
||||
left[p[x]] = y;
|
||||
} else {
|
||||
right[p[x]] = y;
|
||||
}
|
||||
}
|
||||
left[y] = x;
|
||||
p[x] = y;
|
||||
}
|
||||
|
||||
private void rotateRight(int x) {
|
||||
int y = left[x];
|
||||
left[x] = right[y];
|
||||
if (right[y] != NULL) {
|
||||
p[right[y]] = x;
|
||||
}
|
||||
p[y] = p[x];
|
||||
if (p[x] == NULL) {
|
||||
root = y;
|
||||
} else {
|
||||
if (x == right[p[x]]) {
|
||||
right[p[x]] = y;
|
||||
} else {
|
||||
left[p[x]] = y;
|
||||
}
|
||||
}
|
||||
right[y] = x;
|
||||
p[x] = y;
|
||||
}
|
||||
|
||||
|
||||
public boolean returnedNull() {
|
||||
return returnedNull;
|
||||
}
|
||||
|
||||
|
||||
public void clear() {
|
||||
color[NULL] = BLACK;
|
||||
size = 0;
|
||||
root = NULL;
|
||||
}
|
||||
|
||||
|
||||
public int[] keys() {
|
||||
int[] result = new int[size];
|
||||
for (int i = 0, x = firstNode(); x != NULL; x = successorNode(x), i++) {
|
||||
result[i] = keys[x];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public long[] values() {
|
||||
long[] result = new long[size];
|
||||
for (int i = 0, x = firstNode(); x != NULL; x = successorNode(x), i++) {
|
||||
result[i] = values[x];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void enlarge() {
|
||||
int newLength = Math.max(color.length + 1, (int) (color.length * ENLARGE_SCALE));
|
||||
keys = Arrays.copyOf(keys, newLength);
|
||||
values = Arrays.copyOf(values, newLength);
|
||||
left = Arrays.copyOf(left, newLength);
|
||||
right = Arrays.copyOf(right, newLength);
|
||||
p = Arrays.copyOf(p, newLength);
|
||||
color = Arrays.copyOf(color, newLength);
|
||||
}
|
||||
|
||||
private int firstNode() {
|
||||
int x = root;
|
||||
while (left[x] != NULL) {
|
||||
x = left[x];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
private int lastNode() {
|
||||
int x = root;
|
||||
while (right[x] != NULL) {
|
||||
x = right[x];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
public int getFirstKey() {
|
||||
if (root == NULL) {
|
||||
returnedNull = true;
|
||||
return DEFAULT_NULL_KEY;
|
||||
}
|
||||
final int x = firstNode();
|
||||
returnedNull = false;
|
||||
return keys[x];
|
||||
}
|
||||
|
||||
|
||||
public int getLastKey() {
|
||||
if (root == NULL) {
|
||||
returnedNull = true;
|
||||
return DEFAULT_NULL_KEY;
|
||||
}
|
||||
final int x = lastNode();
|
||||
returnedNull = false;
|
||||
return keys[x];
|
||||
}
|
||||
|
||||
|
||||
public PairIntLong getFirstEntry() {
|
||||
if (root == NULL) {
|
||||
returnedNull = true;
|
||||
return null;
|
||||
}
|
||||
final int x = firstNode();
|
||||
returnedNull = false;
|
||||
return PairIntLong.valueOf(keys[x], values[x]);
|
||||
}
|
||||
|
||||
|
||||
public PairIntLong getLastEntry() {
|
||||
if (root == NULL) {
|
||||
returnedNull = true;
|
||||
return null;
|
||||
}
|
||||
final int x = lastNode();
|
||||
returnedNull = false;
|
||||
return PairIntLong.valueOf(keys[x], values[x]);
|
||||
}
|
||||
|
||||
|
||||
public int floorKey(int key) {
|
||||
int x = root;
|
||||
while (x != NULL) {
|
||||
if (key > keys[x]) {
|
||||
if (right[x] != NULL) {
|
||||
x = right[x];
|
||||
} else {
|
||||
returnedNull = false;
|
||||
return keys[x];
|
||||
}
|
||||
} else if (key < keys[x]) {
|
||||
if (left[x] != NULL) {
|
||||
x = left[x];
|
||||
} else {
|
||||
int y = p[x];
|
||||
while (y != NULL && x == left[y]) {
|
||||
//noinspection SuspiciousNameCombination
|
||||
x = y;
|
||||
y = p[y];
|
||||
}
|
||||
if (y == NULL) {
|
||||
returnedNull = true;
|
||||
return DEFAULT_NULL_KEY;
|
||||
} else {
|
||||
returnedNull = false;
|
||||
return keys[y];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
returnedNull = false;
|
||||
return keys[x];
|
||||
}
|
||||
}
|
||||
returnedNull = true;
|
||||
return DEFAULT_NULL_KEY;
|
||||
}
|
||||
|
||||
|
||||
public int ceilingKey(int key) {
|
||||
int x = root;
|
||||
while (x != NULL) {
|
||||
if (key < keys[x]) {
|
||||
if (left[x] != NULL) {
|
||||
x = left[x];
|
||||
} else {
|
||||
returnedNull = false;
|
||||
return keys[x];
|
||||
}
|
||||
} else if (key > keys[x]) {
|
||||
if (right[x] != NULL) {
|
||||
x = right[x];
|
||||
} else {
|
||||
int y = p[x];
|
||||
while (y != NULL && x == right[y]) {
|
||||
//noinspection SuspiciousNameCombination
|
||||
x = y;
|
||||
y = p[y];
|
||||
}
|
||||
if (y == NULL) {
|
||||
returnedNull = true;
|
||||
return DEFAULT_NULL_KEY;
|
||||
} else {
|
||||
returnedNull = false;
|
||||
return keys[y];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
returnedNull = false;
|
||||
return keys[x];
|
||||
}
|
||||
}
|
||||
returnedNull = true;
|
||||
return DEFAULT_NULL_KEY;
|
||||
}
|
||||
|
||||
|
||||
public int lowerKey(int key) {
|
||||
int x = root;
|
||||
while (x != NULL) {
|
||||
if (key > keys[x]) {
|
||||
if (right[x] != NULL) {
|
||||
x = right[x];
|
||||
} else {
|
||||
returnedNull = false;
|
||||
return keys[x];
|
||||
}
|
||||
} else {
|
||||
if (left[x] != NULL) {
|
||||
x = left[x];
|
||||
} else {
|
||||
int y = p[x];
|
||||
while (y != NULL && x == left[y]) {
|
||||
//noinspection SuspiciousNameCombination
|
||||
x = y;
|
||||
y = p[y];
|
||||
}
|
||||
if (y == NULL) {
|
||||
returnedNull = true;
|
||||
return DEFAULT_NULL_KEY;
|
||||
} else {
|
||||
returnedNull = false;
|
||||
return keys[y];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
returnedNull = true;
|
||||
return DEFAULT_NULL_KEY;
|
||||
}
|
||||
|
||||
|
||||
public int higherKey(int key) {
|
||||
int x = root;
|
||||
while (x != NULL) {
|
||||
if (key < keys[x]) {
|
||||
if (left[x] != NULL) {
|
||||
x = left[x];
|
||||
} else {
|
||||
returnedNull = false;
|
||||
return keys[x];
|
||||
}
|
||||
} else {
|
||||
if (right[x] != NULL) {
|
||||
x = right[x];
|
||||
} else {
|
||||
int y = p[x];
|
||||
while (y != NULL && x == right[y]) {
|
||||
//noinspection SuspiciousNameCombination
|
||||
x = y;
|
||||
y = p[y];
|
||||
}
|
||||
if (y == NULL) {
|
||||
returnedNull = true;
|
||||
return DEFAULT_NULL_KEY;
|
||||
} else {
|
||||
returnedNull = false;
|
||||
return keys[y];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
returnedNull = true;
|
||||
return DEFAULT_NULL_KEY;
|
||||
}
|
||||
|
||||
|
||||
public PairIntLong floorEntry(int key) {
|
||||
int x = root;
|
||||
while (x != NULL) {
|
||||
if (key > keys[x]) {
|
||||
if (right[x] != NULL) {
|
||||
x = right[x];
|
||||
} else {
|
||||
returnedNull = false;
|
||||
return PairIntLong.valueOf(keys[x], values[x]);
|
||||
}
|
||||
} else if (key < keys[x]) {
|
||||
if (left[x] != NULL) {
|
||||
x = left[x];
|
||||
} else {
|
||||
int y = p[x];
|
||||
while (y != NULL && x == left[y]) {
|
||||
//noinspection SuspiciousNameCombination
|
||||
x = y;
|
||||
y = p[y];
|
||||
}
|
||||
if (y == NULL) {
|
||||
returnedNull = true;
|
||||
return null;
|
||||
} else {
|
||||
returnedNull = false;
|
||||
return PairIntLong.valueOf(keys[y], values[y]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
returnedNull = false;
|
||||
return PairIntLong.valueOf(keys[x], values[x]);
|
||||
}
|
||||
}
|
||||
returnedNull = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public PairIntLong ceilingEntry(int key) {
|
||||
int x = root;
|
||||
while (x != NULL) {
|
||||
if (key < keys[x]) {
|
||||
if (left[x] != NULL) {
|
||||
x = left[x];
|
||||
} else {
|
||||
returnedNull = false;
|
||||
return PairIntLong.valueOf(keys[x], values[x]);
|
||||
}
|
||||
} else if (key > keys[x]) {
|
||||
if (right[x] != NULL) {
|
||||
x = right[x];
|
||||
} else {
|
||||
int y = p[x];
|
||||
while (y != NULL && x == right[y]) {
|
||||
//noinspection SuspiciousNameCombination
|
||||
x = y;
|
||||
y = p[y];
|
||||
}
|
||||
if (y == NULL) {
|
||||
returnedNull = true;
|
||||
return null;
|
||||
} else {
|
||||
returnedNull = false;
|
||||
return PairIntLong.valueOf(keys[y], values[y]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
returnedNull = false;
|
||||
return PairIntLong.valueOf(keys[x], values[x]);
|
||||
}
|
||||
}
|
||||
returnedNull = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public PairIntLong lowerEntry(int key) {
|
||||
int x = root;
|
||||
while (x != NULL) {
|
||||
if (key > keys[x]) {
|
||||
if (right[x] != NULL) {
|
||||
x = right[x];
|
||||
} else {
|
||||
returnedNull = false;
|
||||
return PairIntLong.valueOf(keys[x], values[x]);
|
||||
}
|
||||
} else {
|
||||
if (left[x] != NULL) {
|
||||
x = left[x];
|
||||
} else {
|
||||
int y = p[x];
|
||||
while (y != NULL && x == left[y]) {
|
||||
//noinspection SuspiciousNameCombination
|
||||
x = y;
|
||||
y = p[y];
|
||||
}
|
||||
if (y == NULL) {
|
||||
returnedNull = true;
|
||||
return null;
|
||||
} else {
|
||||
returnedNull = false;
|
||||
return PairIntLong.valueOf(keys[y], values[y]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
returnedNull = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public PairIntLong higherEntry(int key) {
|
||||
int x = root;
|
||||
while (x != NULL) {
|
||||
if (key < keys[x]) {
|
||||
if (left[x] != NULL) {
|
||||
x = left[x];
|
||||
} else {
|
||||
returnedNull = false;
|
||||
return PairIntLong.valueOf(keys[x], values[x]);
|
||||
}
|
||||
} else {
|
||||
if (right[x] != NULL) {
|
||||
x = right[x];
|
||||
} else {
|
||||
int y = p[x];
|
||||
while (y != NULL && x == right[y]) {
|
||||
//noinspection SuspiciousNameCombination
|
||||
x = y;
|
||||
y = p[y];
|
||||
}
|
||||
if (y == NULL) {
|
||||
returnedNull = true;
|
||||
return null;
|
||||
} else {
|
||||
returnedNull = false;
|
||||
return PairIntLong.valueOf(keys[y], values[y]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
returnedNull = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
TreeMapIntLong that = (TreeMapIntLong) o;
|
||||
|
||||
if (size != that.size) {
|
||||
return false;
|
||||
}
|
||||
for (int x = firstNode(), y = that.firstNode(); x != NULL;
|
||||
//noinspection SuspiciousNameCombination
|
||||
x = successorNode(x), y = that.successorNode(y)) {
|
||||
if (keys[x] != that.keys[y] || values[x] != that.values[y]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 0;
|
||||
for (int x = firstNode(); x != NULL; x = successorNode(x)) {
|
||||
hash = hash ^ keys[x] ^ (int) values[x];
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,423 @@
|
||||
/*
|
||||
* 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.net.packet.common.PairIntLong;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* @author godotg
|
||||
* @version 3.0
|
||||
*/
|
||||
public class TreeMapTest {
|
||||
|
||||
private void testPermutation(int[] array) {
|
||||
int n = array.length;
|
||||
TreeMapIntLong map = new TreeMapIntLong(0);
|
||||
for (int it = 0; it < 3; it++) {
|
||||
Assert.assertEquals(map.size(), 0);
|
||||
Assert.assertTrue(map.isEmpty());
|
||||
for (int i = 0; i < n; i++) {
|
||||
map.put(array[i], 2 * array[i]);
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
Assert.assertEquals(map.size(), i + 1);
|
||||
Assert.assertFalse(map.isEmpty());
|
||||
}
|
||||
//noinspection ForLoopReplaceableByForEach
|
||||
for (int i = 0; i < n; i++) {
|
||||
Assert.assertEquals(map.put(array[i], 3 * array[i]), 2 * array[i]);
|
||||
Assert.assertFalse(map.returnedNull());
|
||||
Assert.assertEquals(map.size(), n);
|
||||
Assert.assertFalse(map.isEmpty());
|
||||
}
|
||||
for (int i = -1; i <= n; i++) {
|
||||
if (0 <= i && i < n) {
|
||||
Assert.assertTrue(map.containsKey(array[i]));
|
||||
Assert.assertEquals(map.get(array[i]), 3 * array[i]);
|
||||
Assert.assertFalse(map.returnedNull());
|
||||
} else {
|
||||
Assert.assertFalse(map.containsKey(i));
|
||||
map.get(i);
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
}
|
||||
}
|
||||
Assert.assertEquals(map.getFirstKey(), 0);
|
||||
Assert.assertFalse(map.returnedNull());
|
||||
Assert.assertEquals(map.getFirstEntry(), PairIntLong.valueOf(0, 0L));
|
||||
Assert.assertFalse(map.returnedNull());
|
||||
Assert.assertEquals(map.getLastKey(), n - 1);
|
||||
Assert.assertFalse(map.returnedNull());
|
||||
Assert.assertEquals(map.getLastEntry(), PairIntLong.valueOf(n - 1, (long) (3 * (n - 1))));
|
||||
Assert.assertFalse(map.returnedNull());
|
||||
for (int i = -2; i <= n + 1; i++) {
|
||||
int lowerKey = map.lowerKey(i);
|
||||
if (i > 0) {
|
||||
Assert.assertEquals(lowerKey, Math.min(i - 1, n - 1));
|
||||
Assert.assertFalse(map.returnedNull());
|
||||
} else {
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
}
|
||||
int higherKey = map.higherKey(i);
|
||||
if (i < n - 1) {
|
||||
Assert.assertEquals(higherKey, Math.max(i + 1, 0));
|
||||
Assert.assertFalse(map.returnedNull());
|
||||
} else {
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
}
|
||||
int floorKey = map.floorKey(i);
|
||||
if (i >= 0) {
|
||||
Assert.assertEquals(floorKey, Math.min(i, n - 1));
|
||||
Assert.assertFalse(map.returnedNull());
|
||||
} else {
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
}
|
||||
int ceilingKey = map.ceilingKey(i);
|
||||
if (i < n) {
|
||||
Assert.assertEquals(ceilingKey, Math.max(i, 0));
|
||||
Assert.assertFalse(map.returnedNull());
|
||||
} else {
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
}
|
||||
PairIntLong lowerEntry = map.lowerEntry(i);
|
||||
if (i > 0) {
|
||||
Assert.assertEquals(lowerEntry, PairIntLong.valueOf(lowerKey, (long) (3 * lowerKey)));
|
||||
Assert.assertFalse(map.returnedNull());
|
||||
} else {
|
||||
Assert.assertNull(lowerEntry);
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
}
|
||||
PairIntLong higherEntry = map.higherEntry(i);
|
||||
if (i < n - 1) {
|
||||
Assert.assertEquals(higherEntry, PairIntLong.valueOf(higherKey, (long) (3 * higherKey)));
|
||||
Assert.assertFalse(map.returnedNull());
|
||||
} else {
|
||||
Assert.assertNull(higherEntry);
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
}
|
||||
PairIntLong floorEntry = map.floorEntry(i);
|
||||
if (i >= 0) {
|
||||
Assert.assertEquals(floorEntry, PairIntLong.valueOf(floorKey, (long) (3 * floorKey)));
|
||||
Assert.assertFalse(map.returnedNull());
|
||||
} else {
|
||||
Assert.assertNull(floorEntry);
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
}
|
||||
PairIntLong ceilingEntry = map.ceilingEntry(i);
|
||||
if (i < n) {
|
||||
Assert.assertEquals(ceilingEntry, PairIntLong.valueOf(ceilingKey, (long) (3 * ceilingKey)));
|
||||
Assert.assertFalse(map.returnedNull());
|
||||
} else {
|
||||
Assert.assertNull(ceilingEntry);
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
}
|
||||
}
|
||||
int removed = 0;
|
||||
for (int i = -1; i <= n; i++) {
|
||||
if (0 <= i && i < n) {
|
||||
Assert.assertEquals(map.remove(array[i]), 3 * array[i]);
|
||||
Assert.assertFalse(map.returnedNull());
|
||||
removed++;
|
||||
} else {
|
||||
map.remove(i);
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
}
|
||||
Assert.assertEquals(map.size(), n - removed);
|
||||
}
|
||||
map.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void genPermutations(int[] permutation, int left, int length) {
|
||||
if (left >= length - 1) {
|
||||
testPermutation(permutation);
|
||||
return;
|
||||
}
|
||||
for (int i = left; i < length; i++) {
|
||||
swap(permutation, i, left);
|
||||
genPermutations(permutation, left + 1, length);
|
||||
swap(permutation, i, left);
|
||||
}
|
||||
}
|
||||
|
||||
private void swap(int[] a, int i, int j) {
|
||||
int tmp = a[i];
|
||||
a[i] = a[j];
|
||||
a[j] = tmp;
|
||||
}
|
||||
|
||||
private void shuffle(int[] a, Random rnd) {
|
||||
int n = a.length;
|
||||
for (int i = 0; i < n; i++) {
|
||||
int j = i + rnd.nextInt(n - i);
|
||||
swap(a, i, j);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllSmallPermutations() {
|
||||
for (int length = 1; length <= 8; length++) {
|
||||
int[] a = new int[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
a[i] = i;
|
||||
}
|
||||
genPermutations(a, 0, length);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testManyPermutationsLength100() {
|
||||
Random rnd = new Random(-228);
|
||||
for (int it = 0; it < 7000; it++) {
|
||||
int length = 9 + rnd.nextInt(92);
|
||||
int[] a = new int[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
a[i] = i;
|
||||
}
|
||||
shuffle(a, rnd);
|
||||
testPermutation(a);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFewPermutationsLength10000() {
|
||||
Random rnd = new Random(-322);
|
||||
for (int it = 0; it < 35; it++) {
|
||||
int length = 9 + rnd.nextInt(9992);
|
||||
int[] a = new int[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
a[i] = i;
|
||||
}
|
||||
shuffle(a, rnd);
|
||||
testPermutation(a);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOperationsOnEmptyMap() {
|
||||
TreeMapIntLong map = new TreeMapIntLong(0);
|
||||
Assert.assertTrue(map.isEmpty());
|
||||
Assert.assertEquals(map.size(), 0);
|
||||
for (int key = -200; key <= 200; key++) {
|
||||
Assert.assertFalse(map.containsKey(key));
|
||||
map.get(key);
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
map.remove(key);
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
map.getFirstKey();
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
map.getLastKey();
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
map.higherKey(key);
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
map.lowerKey(key);
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
map.ceilingKey(key);
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
map.floorKey(key);
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
Assert.assertEquals(map.higherEntry(key), null);
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
Assert.assertEquals(map.lowerEntry(key), null);
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
Assert.assertEquals(map.ceilingEntry(key), null);
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
Assert.assertEquals(map.floorEntry(key), null);
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
Assert.assertEquals(map.getFirstEntry(), null);
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
Assert.assertEquals(map.getLastEntry(), null);
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
}
|
||||
Assert.assertArrayEquals(map.keys(), new int[0]);
|
||||
Assert.assertArrayEquals(map.values(), new long[0]);
|
||||
}
|
||||
|
||||
private void assertSortedMapMethods(TreeMapIntLong map, TreeMap<Integer, Long> javaMap, int key) {
|
||||
{
|
||||
Map.Entry<Integer, Long> javaEntry = javaMap.floorEntry(key);
|
||||
int ezResultKey = map.floorKey(key);
|
||||
if (javaEntry == null) {
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
} else {
|
||||
Assert.assertEquals(ezResultKey, (int) javaEntry.getKey());
|
||||
}
|
||||
PairIntLong ezResultEntry = map.floorEntry(key);
|
||||
if (javaEntry == null) {
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
Assert.assertNull(ezResultEntry);
|
||||
} else {
|
||||
Assert.assertEquals(ezResultEntry, PairIntLong.valueOf(javaEntry.getKey(), (long) javaEntry.getValue()));
|
||||
}
|
||||
}
|
||||
{
|
||||
Map.Entry<Integer, Long> javaEntry = javaMap.ceilingEntry(key);
|
||||
int ezResultKey = map.ceilingKey(key);
|
||||
if (javaEntry == null) {
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
} else {
|
||||
Assert.assertEquals(ezResultKey, (int) javaEntry.getKey());
|
||||
}
|
||||
PairIntLong ezResultEntry = map.ceilingEntry(key);
|
||||
if (javaEntry == null) {
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
Assert.assertNull(ezResultEntry);
|
||||
} else {
|
||||
Assert.assertEquals(ezResultEntry, PairIntLong.valueOf(javaEntry.getKey(), (long) javaEntry.getValue()));
|
||||
}
|
||||
}
|
||||
{
|
||||
Map.Entry<Integer, Long> javaEntry = javaMap.lowerEntry(key);
|
||||
int ezResultKey = map.lowerKey(key);
|
||||
if (javaEntry == null) {
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
} else {
|
||||
Assert.assertEquals(ezResultKey, (int) javaEntry.getKey());
|
||||
}
|
||||
PairIntLong ezResultEntry = map.lowerEntry(key);
|
||||
if (javaEntry == null) {
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
Assert.assertNull(ezResultEntry);
|
||||
} else {
|
||||
Assert.assertEquals(ezResultEntry, PairIntLong.valueOf(javaEntry.getKey(), (long) javaEntry.getValue()));
|
||||
}
|
||||
}
|
||||
{
|
||||
Map.Entry<Integer, Long> javaEntry = javaMap.higherEntry(key);
|
||||
int ezResultKey = map.higherKey(key);
|
||||
if (javaEntry == null) {
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
} else {
|
||||
Assert.assertEquals(ezResultKey, (int) javaEntry.getKey());
|
||||
}
|
||||
PairIntLong ezResultEntry = map.higherEntry(key);
|
||||
if (javaEntry == null) {
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
Assert.assertNull(ezResultEntry);
|
||||
} else {
|
||||
Assert.assertEquals(ezResultEntry, PairIntLong.valueOf(javaEntry.getKey(), (long) javaEntry.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignificantMethodsOnRandomData() {
|
||||
TreeMapIntLong map = new TreeMapIntLong();
|
||||
TreeMap<Integer, Long> javaMap = new TreeMap<Integer, Long>();
|
||||
Random rnd = new Random(~42);
|
||||
for (int i = 0; i < 1000000; i++) {
|
||||
Assert.assertEquals(map.size(), javaMap.size());
|
||||
double prob = rnd.nextDouble();
|
||||
if (prob < 0.301) {
|
||||
int key = rnd.nextInt(50);
|
||||
long newValue = rnd.nextInt();
|
||||
Long oldValue = javaMap.put(key, newValue);
|
||||
if (oldValue == null) {
|
||||
map.put(key, newValue);
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
} else {
|
||||
long actualOldValue = map.put(key, newValue);
|
||||
Assert.assertFalse(map.returnedNull());
|
||||
Assert.assertEquals(actualOldValue, (long) oldValue);
|
||||
}
|
||||
} else if (prob < 0.6) {
|
||||
int key = rnd.nextInt(50);
|
||||
Long oldValue = javaMap.remove(key);
|
||||
if (oldValue == null) {
|
||||
map.remove(key);
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
} else {
|
||||
long actualOldValue = map.remove(key);
|
||||
Assert.assertFalse(map.returnedNull());
|
||||
Assert.assertEquals(actualOldValue, (long) oldValue);
|
||||
}
|
||||
} else {
|
||||
int key = rnd.nextInt(20);
|
||||
assertSortedMapMethods(map, javaMap, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testKeysValuesArrays() {
|
||||
int[] keys = new int[100];
|
||||
long[] values = new long[100];
|
||||
for (int i = 0; i < 100; i++) {
|
||||
keys[i] = i * 322 + 228;
|
||||
values[i] = i * 228 + 322;
|
||||
}
|
||||
for (int len = 0; len <= 100; len++) {
|
||||
TreeMapIntLong map = new TreeMapIntLong();
|
||||
for (int i = 0; i < len; i++) {
|
||||
map.put(keys[i], values[i]);
|
||||
Assert.assertTrue(map.returnedNull());
|
||||
}
|
||||
Assert.assertArrayEquals(map.keys(), Arrays.copyOfRange(keys, 0, len));
|
||||
Assert.assertArrayEquals(map.values(), Arrays.copyOfRange(values, 0, len));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEqualsHashcode() {
|
||||
for (int mask1 = 0; mask1 < 32; mask1++) {
|
||||
for (int mask2 = 0; mask2 < 32; mask2++) {
|
||||
TreeMapIntLong map1 = new TreeMapIntLong(1);
|
||||
TreeMapIntLong map2 = new TreeMapIntLong(1);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if ((mask1 & (1 << i)) != 0) {
|
||||
map1.put(1000 * i, i);
|
||||
}
|
||||
if ((mask2 & (1 << i)) != 0) {
|
||||
map2.put(1000 * i, i);
|
||||
}
|
||||
}
|
||||
int hashCode1 = map1.hashCode();
|
||||
int hashCode2 = map2.hashCode();
|
||||
if (mask1 == mask2) {
|
||||
Assert.assertTrue(map1.equals(map2));
|
||||
Assert.assertTrue(map2.equals(map1));
|
||||
Assert.assertTrue(hashCode1 == hashCode2);
|
||||
} else {
|
||||
Assert.assertFalse(map1.equals(map2));
|
||||
Assert.assertFalse(map2.equals(map1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCelling() {
|
||||
var max = 10000;
|
||||
TreeMapIntLong treeMap = new TreeMapIntLong(200);
|
||||
for (int i = 0; i < max; i++) {
|
||||
if (i % 2 == 0) {
|
||||
treeMap.put(i, i);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < max; i++) {
|
||||
if (i % 2 != 0 && i != (max - 1)) {
|
||||
Assert.assertEquals(treeMap.ceilingEntry(i).getValue(), i + 1);
|
||||
}
|
||||
if (i % 2 != 0) {
|
||||
Assert.assertEquals(treeMap.floorEntry(i).getValue(), i - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,6 +26,7 @@
|
||||
<protocol id="103" location="com.zfoo.net.packet.common.Ping"/>
|
||||
<protocol id="104" location="com.zfoo.net.packet.common.Pong"/>
|
||||
|
||||
<protocol id="110" location="com.zfoo.net.packet.common.PairIntLong"/>
|
||||
<protocol id="111" location="com.zfoo.net.packet.common.PairLong"/>
|
||||
<protocol id="112" location="com.zfoo.net.packet.common.PairString"/>
|
||||
<protocol id="113" location="com.zfoo.net.packet.common.PairLS"/>
|
||||
|
||||
Reference in new issue
Block a user