引言:
在数据库管理系统中,索引是一种重要的数据结构,用于加速对数据的访问。B+树是一种常用的索引结构,被广泛应用于数据库系统中,能够有效地支持精确查询,范围查询、排序和高效的插入,删除操作。
实现需求:
![](https://i-blog.csdnimg.cn/direct/da66b8ee074e431cb4919fa597493a1a.png)
- 初始化 B+树,设置一些同学的信息(随机初始化 100 名同学(学号按顺序,其他信息随机),插入 B+树,使 B+树具有一定的高度)
- 修改 B+树中已存在同学的信息,更新树
- 向 B+树上添加一名新的同学信息,更新树的信息
- 删除 B+树上的一名同学的信息,更新树的信息
- 查询学号为 xxx 的学生信息,并输出
- 查询学号范围在为 10-18 之间的学生信息,并输出(输出顺序10-11-12-…
实现效果:
可以实现较大键值的插入。
范围查询:
ps:主要是参考了大佬的B+树实现
大佬原实现链接:【大数据存储】Java实现B+树(增、删、单值查询和范围查询)_java b+数据落盘-CSDN博客
代码:
BPlusNode.java:
package studentTree;
import java.util.*;
@SuppressWarnings("all")
public class BPlusNode<K extends Comparable<K>, V> {
// 是否为叶子节点
protected boolean isLeaf;
//是否是根节点
protected boolean isRoot;
// 父节点
protected BPlusNode<K, V> parent;
// 叶节点的前驱节点
protected BPlusNode<K, V> previous;
// 叶节点的后继节点
protected BPlusNode<K, V> next;
// 节点的关键字列表
protected List<Map.Entry<K, V>> entries;
// 子节点列表
protected List<BPlusNode<K, V>> children;
//构造方法
public BPlusNode(boolean isLeaf) {
this.isLeaf = isLeaf;
entries = new ArrayList();
//如果不是叶子结点,则有孩子列表
if (!isLeaf) {
children = new ArrayList();
}
}
//构造方法
public BPlusNode(boolean isLeaf, boolean isRoot) {
this(isLeaf);
this.isRoot = isRoot;
}
//单值查找,找到key对应的value值
public V query(K key) {
//如果是叶子节点
if (isLeaf) {
//二分查找
int low = 0, high = entries.size() - 1, mid;
int comp;
while (low <= high) {
mid = (low + high) / 2;
comp = entries.get(mid).getKey().compareTo(key);
//等于key值
if (comp == 0) {
//在entries列表中取value值
return entries.get(mid).getValue();
} else if (comp < 0) {
low = mid + 1;
} else {
high = mid - 1;
}
}
//未找到所要查询的对象
return null;
}
//如果不是叶子节点
//如果key小于节点最左边的key,沿第一个子节点继续递归地搜索
if (key.compareTo(entries.get(0).getKey()) < 0) {
return children.get(0).query(key);
//如果key大于等于节点最右边的key,沿最后一个子节点继续递归地搜索
} else if (key.compareTo(entries.get(entries.size() - 1).getKey()) >= 0) {
return children.get(children.size() - 1).query(key);
//否则沿比key大的前一个子节点继续递归地搜索
} else {
//二分查找
int low = 0, high = entries.size() - 1, mid = 0;
int comp;
while (low <= high) {
mid = (low + high) / 2;
comp = entries.get(mid).getKey().compareTo(key);
if (comp == 0) {
return children.get(mid + 1).query(key);
} else if (comp < 0) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return children.get(low).query(key);
}
}
public List<Map.Entry<K, V>> rangeQuery(K startInclude, K endExclude) {
List<Map.Entry<K, V>> result = new ArrayList<>();
rangeSearch(this, startInclude, endExclude, result);
return result;
}
private void rangeSearch(BPlusNode<K, V> node, K startInclude, K endExclude, List<Map.Entry<K, V>> result) {
if (node.isLeaf) {
for (Map.Entry<K, V> entry : node.entries) {
K key = entry.getKey();
if (key.compareTo(startInclude) >= 0 && key.compareTo(endExclude) < 0) {
result.add(entry);
}
}
BPlusNode<K, V> next = node.next;
while (next != null) {
for (Map.Entry<K, V> entry : next.entries) {
K key = entry.getKey();
if (key.compareTo(endExclude) < 0) {
result.add(entry);
} else {
return;
}
}
next = next.next;
}
} else {
boolean foundRange = false;
for (int i = 0; i < node.entries.size(); i++) {
K key = node.entries.get(i).getKey();
if (key.compareTo(startInclude) >= 0) {
rangeSearch(node.children.get(i), startInclude, endExclude, result);
foundRange = true;
}
if (foundRange || key.compareTo(endExclude) >= 0) {
break;
}
}
if (!foundRange) {
rangeSearch(node.children.get(node.children.size() - 1), startInclude, endExclude, result);
}
}
}
//二分查找找到key在entries中的位置
protected int findIndex(K key) {
int l = 0;
int r = entries.size() - 1;
int index = entries.size();
while (l <= r) {
int mid = l + ((r - l) >> 1);
if (entries.get(mid).getKey().compareTo(key) >= 0) {
index = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
return index;
}
//插入 将key value键值对插入tree
public void insertOrUpdate(K key, V value, BPlusTree<K, V> tree) {
//如果是叶子节点
if (isLeaf) {
//小于树的度,不需要分裂,直接插入或更新
if (contains(key) != -1 || entries.size() < tree.getOrder()) {
insertOrUpdate(key, value);
if (tree.getHeight() == 0) {
tree.setHeight(1);
}
return;
}
//需要分裂
//分裂成左右两个节点
BPlusNode<K, V> left = new BPlusNode<K, V>(true);
BPlusNode<K, V> right = new BPlusNode<K, V>(true);
//设置链接
if (previous != null) {
//前驱结点的next指针指向left
previous.next = left;
left.previous = previous;
}
if (next != null) {
//后继结点的pre指针指向right
next.previous = right;
right.next = next;
}
//没有前驱
if (previous == null) {
tree.setHead(left);
}
//left的后继指针指向right
left.next = right;
//right的前驱指针指向left
right.previous = left;
//pre和next置空
previous = null;
next = null;
//把原节点关键字复制到到分裂出来的两个新节点
copy2Nodes(key, value, left, right, tree);
//如果不是根节点
if (parent != null) {
//调整父子节点关系
//在父节点的children序列中的位置
int index = parent.children.indexOf(this);
//移除当前结点
parent.children.remove(this);
//更新左右新结点的parent
left.parent = parent;
right.parent = parent;
//在父节点的children序列中添加两个新结点
parent.children.add(index, left);
parent.children.add(index + 1, right);
//在父节点的关键字列表的index位置中添加右结点的第一个关键字
parent.entries.add(index, right.entries.get(0));
entries = null; //删除当前节点的关键字信息
children = null; //删除当前节点的孩子节点引用
//父节点插入或更新关键字
parent.updateInsert(tree);
parent = null; //删除当前节点的父节点引用
//如果是根节点
} else {
isRoot = false;
//新建根节点
BPlusNode<K, V> parent = new BPlusNode<K, V>(false, true);
tree.setRoot(parent);
//把相应的指针和序列更新
left.parent = parent;
right.parent = parent;
parent.children.add(left);
parent.children.add(right);
parent.entries.add(right.entries.get(0));
entries = null;
children = null;
}
return;
}
//如果不是叶子节点
//如果key小于等于节点最左边的key,沿第一个子节点继续递归地搜索
if (key.compareTo(entries.get(0).getKey()) < 0) {
children.get(0).insertOrUpdate(key, value, tree);
//如果key大于节点最右边的key,沿最后一个子节点继续递归地搜索
} else if (key.compareTo(entries.get(entries.size() - 1).getKey()) >= 0) {
children.get(children.size() - 1).insertOrUpdate(key, value, tree);
//否则沿比key大的前一个子节点继续递归地搜索
} else {
//二分查找
int low = 0, high = entries.size() - 1, mid = 0;
int comp;
while (low <= high) {
mid = (low + high) / 2;
comp = entries.get(mid).getKey().compareTo(key);
if (comp == 0) {
children.get(mid + 1).insertOrUpdate(key, value, tree);
break;
} else if (comp < 0) {
low = mid + 1;
} else {
high = mid - 1;
}
}
if (low > high) {
children.get(low).insertOrUpdate(key, value, tree);
}
}
}
//把原节点关键字复制到到分裂出来的两个新节点
private void copy2Nodes(K key, V value, BPlusNode<K, V> left,
BPlusNode<K, V> right, BPlusTree<K, V> tree) {
//左右两个节点关键字长度,比方说原结点有5个数,则left分2个,right分3个
int leftSize = (tree.getOrder() + 1) / 2 + (tree.getOrder() + 1) % 2;
boolean b = false;//用于记录新元素是否已经被插入
for (int i = 0; i < entries.size(); i++) {
//左结点
if (leftSize != 0) {
leftSize--;
if (!b && entries.get(i).getKey().compareTo(key) > 0) {
//在左节点的entries序列中新建一个存放新元素
left.entries.add(new AbstractMap.SimpleEntry<K, V>(key, value));
b = true;
i--;
} else {
left.entries.add(entries.get(i));
}
//右结点
} else {
if (!b && entries.get(i).getKey().compareTo(key) > 0) {
//在右节点的entries序列中新建一个存放新元素
right.entries.add(new AbstractMap.SimpleEntry<K, V>(key, value));
b = true;
i--;
} else {
right.entries.add(entries.get(i));
}
}
}
//新元素仍没插入,说明新元素最大,把它放在右节点
if (!b) {
right.entries.add(new AbstractMap.SimpleEntry<K, V>(key, value));
}
}
/**
* 插入节点后中间节点的更新
*/
protected void updateInsert(BPlusTree<K, V> tree) {
//如果子节点数超出阶数,则需要分裂该节点
if (children.size() > tree.getOrder()) {
//分裂成左右两个节点
BPlusNode<K, V> left = new BPlusNode<K, V>(false);
BPlusNode<K, V> right = new BPlusNode<K, V>(false);
//左右两个节点子节点的长度
int leftSize = (tree.getOrder() + 1) / 2 + (tree.getOrder() + 1) % 2;
int rightSize = (tree.getOrder() + 1) / 2;
//复制子节点到分裂出来的新节点,并更新关键字
for (int i = 0; i < leftSize; i++) {
left.children.add(children.get(i));
//把每个孩子的parent指针指向left
children.get(i).parent = left;
}
for (int i = 0; i < rightSize; i++) {
right.children.add(children.get(leftSize + i));
//把每个孩子的parent指针指向right
children.get(leftSize + i).parent = right;
}
for (int i = 0; i < leftSize - 1; i++) {
//更新左节点的entries列表
left.entries.add(entries.get(i));
}
for (int i = 0; i < rightSize - 1; i++) {
//更新右节点的entries列表
right.entries.add(entries.get(leftSize + i));
}
//如果不是根节点
if (parent != null) {
//调整父子节点关系
int index = parent.children.indexOf(this);
//在父节点的children列表中移除当前结点
parent.children.remove(this);
//更新指针指向
left.parent = parent;
right.parent = parent;
//更新列表
parent.children.add(index, left);
parent.children.add(index + 1, right);
parent.entries.add(index, entries.get(leftSize - 1));
//原来的置空
entries = null;
children = null;
//递归调用 父节点更新关键字
parent.updateInsert(tree);
parent = null;
//如果是根节点
} else {
isRoot = false;
//新建根节点
BPlusNode<K, V> parent = new BPlusNode<K, V>(false, true);
tree.setRoot(parent);
tree.setHeight(tree.getHeight() + 1);
//更新
left.parent = parent;
right.parent = parent;
parent.children.add(left);
parent.children.add(right);
parent.entries.add(entries.get(leftSize - 1));
//原来的置空
entries = null;
children = null;
}
}
}
/**
* 删除节点后中间节点的更新
*/
protected void updateRemove(BPlusTree<K, V> tree) {
// 如果子节点数小于M / 2或者小于2,则需要合并节点
if (children.size() < tree.getOrder() / 2 || children.size() < 2) {
if (isRoot) {
// 如果是根节点并且子节点数大于等于2,无需合并
if (children.size() >= 2) return;
// 否则与子节点合并
//新建根节点
BPlusNode<K, V> root = children.get(0);
tree.setRoot(root);
tree.setHeight(tree.getHeight() - 1);
root.parent = null;
root.isRoot = true;
//原来的置空
entries = null;
children = null;
return;
}
//计算前后节点
//当前结点在父节点的children序列中的位置
int currIdx = parent.children.indexOf(this);
int prevIdx = currIdx - 1;
int nextIdx = currIdx + 1;
//新建前驱和后继
BPlusNode<K, V> previous = null, next = null;
if (prevIdx >= 0) {
previous = parent.children.get(prevIdx);
}
if (nextIdx < parent.children.size()) {
next = parent.children.get(nextIdx);
}
// 如果前驱节点子节点数大于M / 2并且大于2,则从其处借补
if (previous != null
&& previous.children.size() > tree.getOrder() / 2
&& previous.children.size() > 2) {
//前驱叶子节点末尾节点添加到首位
int idx = previous.children.size() - 1;
//新建借的结点
BPlusNode<K, V> borrow = previous.children.get(idx);
//借走了,从前驱结点的children序列中移除
previous.children.remove(idx);
//借过来了,更新相关
borrow.parent = this;
children.add(0, borrow);
int preIndex = parent.children.indexOf(previous);
entries.add(0, parent.entries.get(preIndex));
parent.entries.set(preIndex, previous.entries.remove(idx - 1));
return;
}
// 同理,如果后继节点子节点数大于M / 2并且大于2,则从其处借补
if (next != null
&& next.children.size() > tree.getOrder() / 2
&& next.children.size() > 2) {
//后叶子节点首位添加到末尾
BPlusNode<K, V> borrow = next.children.get(0);
next.children.remove(0);
borrow.parent = this;
children.add(borrow);
int preIndex = parent.children.indexOf(this);
entries.add(parent.entries.get(preIndex));
parent.entries.set(preIndex, next.entries.remove(0));
return;
}
// 如果前驱结点不够借,就同前驱节点合并
if (previous != null
&& (previous.children.size() <= tree.getOrder() / 2
|| previous.children.size() <= 2)) {
for (int i = 0; i < children.size(); i++) {
previous.children.add(children.get(i));
}
for (int i = 0; i < previous.children.size(); i++) {
previous.children.get(i).parent = this;
}
int indexPre = parent.children.indexOf(previous);
previous.entries.add(parent.entries.get(indexPre));
for (int i = 0; i < entries.size(); i++) {
previous.entries.add(entries.get(i));
}
children = previous.children;
entries = previous.entries;
//更新父节点的关键字列表
parent.children.remove(previous);
previous.parent = null;
previous.children = null;
previous.entries = null;
//合并了,父节点的孩子就少一个
parent.entries.remove(parent.children.indexOf(this));
if ((!parent.isRoot
&& (parent.children.size() >= tree.getOrder() / 2
&& parent.children.size() >= 2))
|| parent.isRoot && parent.children.size() >= 2) {
return;
}
//递归调用
parent.updateRemove(tree);
return;
}
// 同理,后继结点不够借,就同后面节点合并
if (next != null
&& (next.children.size() <= tree.getOrder() / 2
|| next.children.size() <= 2)) {
for (int i = 0; i < next.children.size(); i++) {
BPlusNode<K, V> child = next.children.get(i);
children.add(child);
child.parent = this;
}
int index = parent.children.indexOf(this);
entries.add(parent.entries.get(index));
for (int i = 0; i < next.entries.size(); i++) {
entries.add(next.entries.get(i));
}
parent.children.remove(next);
next.parent = null;
next.children = null;
next.entries = null;
parent.entries.remove(parent.children.indexOf(this));
if ((!parent.isRoot && (parent.children.size() >= tree.getOrder() / 2
&& parent.children.size() >= 2))
|| parent.isRoot && parent.children.size() >= 2) {
return;
}
parent.updateRemove(tree);
return;
}
}
}
//在B+树中删除key
public V remove(K key, BPlusTree<K, V> tree) {
//如果是叶子节点
if (isLeaf) {
//如果不包含该关键字,则直接返回
if (contains(key) == -1) {
return null;
}
//如果既是叶子节点又是根节点,直接删除
if (isRoot) {
if (entries.size() == 1) {
tree.setHeight(0);
}
return remove(key);
}
//如果关键字数大于M / 2,直接删除
if (entries.size() > tree.getOrder() / 2 && entries.size() > 2) {
return remove(key);
}
//如果自身关键字数小于M / 2,并且前驱节点关键字数大于M / 2,则从兄弟结点处借补
if (previous != null &&
previous.parent == parent
&& previous.entries.size() > tree.getOrder() / 2
&& previous.entries.size() > 2) {
//添加到首位
int size = previous.entries.size();
entries.add(0, previous.entries.remove(size - 1));
int index = parent.children.indexOf(previous);
parent.entries.set(index, entries.get(0));
return remove(key);
}
//如果自身关键字数小于M / 2,并且后节点关键字数大于M / 2,则从右兄弟处借补
if (next != null
&& next.parent == parent
&& next.entries.size() > tree.getOrder() / 2
&& next.entries.size() > 2) {
entries.add(next.entries.remove(0));
int index = parent.children.indexOf(this);
parent.entries.set(index, next.entries.get(0));
return remove(key);
}
//前驱不够借,就同前面节点合并
if (previous != null
&& previous.parent == parent
&& (previous.entries.size() <= tree.getOrder() / 2
|| previous.entries.size() <= 2)) {
V returnValue = remove(key);
for (int i = 0; i < entries.size(); i++) {
//将当前节点的关键字添加到前节点的末尾
previous.entries.add(entries.get(i));
}
entries = previous.entries;
parent.children.remove(previous);
previous.parent = null;
previous.entries = null;
//更新链表
if (previous.previous != null) {
BPlusNode<K, V> temp = previous;
temp.previous.next = this;
previous = temp.previous;
temp.previous = null;
temp.next = null;
} else {
tree.setHead(this);
previous.next = null;
previous = null;
}
parent.entries.remove(parent.children.indexOf(this));
if ((!parent.isRoot && (parent.children.size() >= tree.getOrder() / 2
&& parent.children.size() >= 2))
|| parent.isRoot && parent.children.size() >= 2) {
return returnValue;
}
//父节点要调整
parent.updateRemove(tree);
return returnValue;
}
//后继结点不够借,就同后面节点合并
if (next != null
&& next.parent == parent
&& (next.entries.size() <= tree.getOrder() / 2
|| next.entries.size() <= 2)) {
V returnValue = remove(key);
for (int i = 0; i < next.entries.size(); i++) {
//从首位开始添加到末尾
entries.add(next.entries.get(i));
}
next.parent = null;
next.entries = null;
parent.children.remove(next);
//更新链表
if (next.next != null) {
BPlusNode<K, V> temp = next;
temp.next.previous = this;
next = temp.next;
temp.previous = null;
temp.next = null;
} else {
next.previous = null;
next = null;
}
//更新父节点的关键字列表
parent.entries.remove(parent.children.indexOf(this));
if ((!parent.isRoot && (parent.children.size() >= tree.getOrder() / 2
&& parent.children.size() >= 2))
|| parent.isRoot && parent.children.size() >= 2) {
return returnValue;
}
parent.updateRemove(tree);
return returnValue;
}
}
/*如果不是叶子节点*/
//如果key小于等于节点最左边的key,沿第一个子节点继续搜索
if (key.compareTo(entries.get(0).getKey()) < 0) {
return children.get(0).remove(key, tree);
//如果key大于节点最右边的key,沿最后一个子节点继续搜索
} else if (key.compareTo(entries.get(entries.size() - 1).getKey()) >= 0) {
return children.get(children.size() - 1).remove(key, tree);
//否则沿比key大的前一个子节点继续搜索
} else {
//二分查找
int low = 0, high = entries.size() - 1, mid = 0;
int comp;
while (low <= high) {
mid = (low + high) / 2;
comp = entries.get(mid).getKey().compareTo(key);
if (comp == 0) {
return children.get(mid + 1).remove(key, tree);
} else if (comp < 0) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return children.get(low).remove(key, tree);
}
}
// 判断当前节点是否包含该关键字
protected int contains(K key) {
int low = 0, high = entries.size() - 1, mid;
int comp;
while (low <= high) {
mid = (low + high) / 2;
comp = entries.get(mid).getKey().compareTo(key);
if (comp == 0) {
return mid;
} else if (comp < 0) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return -1;
}
// 直接插入:插入到当前节点的关键字中
protected void insertOrUpdate(K key, V value) {
//二叉查找,插入
int low = 0, high = entries.size() - 1, mid;
int comp;
while (low <= high) {
mid = (low + high) / 2;
comp = entries.get(mid).getKey().compareTo(key);
if (comp == 0) {
entries.get(mid).setValue(value);
break;
} else if (comp < 0) {
low = mid + 1;
} else {
high = mid - 1;
}
}
if (low > high) {
entries.add(low, new AbstractMap.SimpleEntry<K, V>(key, value));
}
}
// 直接删除:返回待删除节点的value值
protected V remove(K key) {
int low = 0, high = entries.size() - 1, mid;
int comp;
while (low <= high) {
mid = (low + high) / 2;
comp = entries.get(mid).getKey().compareTo(key);
if (comp == 0) {
return entries.remove(mid).getValue();
} else if (comp < 0) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return null;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("isRoot: ");
sb.append(isRoot);
sb.append(", ");
sb.append("isLeaf: ");
sb.append(isLeaf);
sb.append(", ");
sb.append("keys: ");
for (Map.Entry<K, V> entry : entries) {
sb.append(entry.getKey());
sb.append(", ");
}
sb.append(", ");
return sb.toString();
}
//打印信息
public void printBPlusTree(int index) {
if (this.isLeaf) {
System.out.print("层级:" + index + ",叶子节点,keys为: ");
for (int i = 0; i < entries.size(); ++i)
System.out.print(entries.get(i) + " ");
System.out.println();
} else {
System.out.print("层级:" + index + ",非叶子节点,keys为: ");
for (int i = 0; i < entries.size(); ++i)
System.out.print(entries.get(i) + " ");
System.out.println();
for (int i = 0; i < children.size(); ++i)
children.get(i).printBPlusTree(index + 1);
}
}
}
BPlusTree.java:
package studentTree;
/**
* B+树的定义:
* 1.任意非叶子结点最多有M个子节点;且M>2;M为B+树的阶数
* 2.除根结点以外的非叶子结点至少有 (M+1)/2个子节点;
* 3.根结点至少有2个子节点;
* 4.除根节点外每个结点存放至少(M-1)/2和至多M-1个关键字;(至少1个关键字)
* 5.非叶子结点的子树指针比关键字多1个;
* 6.非叶子节点的所有key按升序存放,假设节点的关键字分别为K[0], K[1] … K[M-2],指向子女的指针分别为P[0], P[1]…P[M-1]。则有:
* P[0] < K[0] <= P[1] < K[1] …..< K[M-2] <= P[M-1]
* 7.所有叶子结点位于同一层;
* 8.为所有叶子结点增加一个链指针;
* 9.所有关键字都在叶子结点出现
*/
@SuppressWarnings("all")
public class BPlusTree<K extends Comparable<K>, V> {
// 根节点
protected BPlusNode<K, V> root;
// 树的阶数
protected int order;
// 叶子节点的链表头
protected BPlusNode<K, V> head;
// 树高
protected int height = 0;
public BPlusNode<K, V> getHead() {
return head;
}
public void setHead(BPlusNode<K, V> head) {
this.head = head;
}
public BPlusNode<K, V> getRoot() {
return root;
}
public void setRoot(BPlusNode<K, V> root) {
this.root = root;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public void setHeight(int height) {
this.height = height;
}
public int getHeight() {
return height;
}
public V query(K key) {return root.query(key);}
public V remove(K key) {
return root.remove(key, this);
}
public void insertOrUpdate(K key, V value) {
root.insertOrUpdate(key, value, this);
}
public BPlusTree(int order) {
if (order < 3) {
System.out.print("度数要大于2!");
System.exit(0);
}
this.order = order;
root = new BPlusNode<K, V>(true, true);
head = root;
}
public void printBPlusTree() {
this.root.printBPlusTree(0);
}
}
Student.java:
package studentTree;
public class Student {
private int id;
private String name;
private String gender;
private int score;
public Student(int id, String name, String gender, int score) {
this.id = id;
this.name = name;
this.gender = gender;
this.score = score;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public String getGender() {
return gender;
}
public int getScore() {
return score;
}
public void setName(String name) {
this.name = name;
}
public void setGender(String gender) {
this.gender = gender;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "ID: " + id + ", Name: " + name + ", Gender: " + gender + ", Score: " + score;
}
}
BPlusTreeTest.java:
package studentTree;
import java.util.*;
public class BPlusTreeTest {
private static final Scanner scanner = new Scanner(System.in);
public static void main(String[] args) {
int size = 100;
int order = 4;
BPlusTree<Integer, Student> tree = new BPlusTree<>(order);
while (true) {
System.out.println("请选择操作:");
System.out.println("1. 初始化100个学生信息,并显示学生信息");
System.out.println("2. 增加学生");
System.out.println("3. 修改学生");
System.out.println("4. 查询学生");
System.out.println("5. 删除学生");
System.out.println("6. 范围查询");
System.out.println("7. 退出");
int choice = scanner.nextInt();
scanner.nextLine(); // consume newline
switch (choice) {
case 1:
initializeTree(tree, size);
//printAllStudents(tree);
break;
case 2:
addStudent(tree);
break;
case 3:
modifyStudent(tree);
break;
case 4:
queryStudent(tree);
break;
case 5:
deleteStudent(tree);
break;
case 6:
rangeQueryStudents(tree);
break;
case 7:
System.out.println("退出程序。");
return;
default:
System.out.println("无效的选择,请重新选择。");
}
}
}
// 初始化 B+ 树,设置一些同学的信息
private static void initializeTree(BPlusTree<Integer, Student> tree, int size) {
Random random = new Random();
for (int i = 1; i <= size; i++) {
String name = "Student" + i;
String gender = random.nextBoolean() ? "男" : "女";
int score = random.nextInt(101);
Student student = new Student(i, name, gender, score);
System.out.println(student.toString());
tree.insertOrUpdate(i, student);
}
System.out.println("初始化完成。");
}
// 打印所有学生信息
private static void printAllStudents(BPlusTree<Integer, Student> tree) {
List<Map.Entry<Integer, Student>> students = tree.getRoot().rangeQuery(1, 101);
for (Map.Entry<Integer, Student> entry : students) {
System.out.println(entry.getValue());
}
}
// 增加学生
private static void addStudent(BPlusTree<Integer, Student> tree) {
System.out.print("请输入学生ID: ");
int id = scanner.nextInt();
scanner.nextLine(); // consume newline
System.out.print("请输入学生姓名: ");
String name = scanner.nextLine();
System.out.print("请输入学生性别: ");
String gender = scanner.nextLine();
System.out.print("请输入学生成绩: ");
int score = scanner.nextInt();
scanner.nextLine(); // consume newline
Student student = new Student(id, name, gender, score);
tree.insertOrUpdate(id, student);
System.out.println("添加学生成功。");
}
// 修改学生
private static void modifyStudent(BPlusTree<Integer, Student> tree) {
System.out.print("请输入要修改的学生ID: ");
int oldId = scanner.nextInt();
scanner.nextLine(); // consume newline
Student student = tree.query(oldId);
if (student != null) {
System.out.print("请输入新的学生姓名: ");
String name = scanner.nextLine();
System.out.print("请输入新的学生性别: ");
String gender = scanner.nextLine();
System.out.print("请输入新的学生成绩: ");
int score = scanner.nextInt();
scanner.nextLine(); // consume newline
System.out.print("是否需要修改学生ID? (y/n): ");
String modifyIdChoice = scanner.nextLine();
int newId = oldId;
if (modifyIdChoice.equalsIgnoreCase("y")) {
System.out.print("请输入新的学生ID: ");
newId = scanner.nextInt();
scanner.nextLine(); // consume newline
}
// 删除旧的学生信息并插入新的学生信息
tree.remove(oldId);
Student updatedStudent = new Student(newId, name, gender, score);
tree.insertOrUpdate(newId, updatedStudent);
System.out.println("修改学生成功: " + updatedStudent);
} else {
System.out.println("学生ID不存在。");
}
}
// 查询学生
private static void queryStudent(BPlusTree<Integer, Student> tree) {
System.out.print("请输入要查询的学生ID: ");
int id = scanner.nextInt();
scanner.nextLine(); // consume newline
Student student = tree.query(id);
if (student != null) {
System.out.println("学生信息: " + student);
} else {
System.out.println("学生ID不存在。");
}
}
// 删除学生
private static void deleteStudent(BPlusTree<Integer, Student> tree) {
System.out.print("请输入要删除的学生ID: ");
int id = scanner.nextInt();
scanner.nextLine(); // consume newline
Student student = tree.remove(id);
if (student != null) {
System.out.println("删除学生成功: " + student);
} else {
System.out.println("学生ID不存在。");
}
}
private static void rangeQueryStudents(BPlusTree<Integer, Student> tree) {
System.out.print("请输入范围查询的起始学号: ");
int start = scanner.nextInt();
System.out.print("请输入范围查询的结束学号: ");
int end = scanner.nextInt();
scanner.nextLine(); // consume newline
List<Map.Entry<Integer, Student>> students = tree.getRoot().rangeQuery(start, end + 1);
System.out.println("Students with ID in range " + start + "-" + end + ":");
for (Map.Entry<Integer, Student> entry : students) {
System.out.println(entry.getValue());
}
}
}