SortedMap、TreeMap、SortedSet和TreeSet都是Java集合框架中的重要组成部分,它们各自具有独特的特点和适用场景。
下面将逐一详细介绍这些集合类的特点。
SortedMap
SortedMap是Java中的一个接口,它继承自Map接口,用于存储键值对,并且可以按照键的自然顺序或自定义排序顺序进行排序。在SortedMap中,键是唯一的,而值则可以重复。SortedMap的键值对按照键的排序顺序存储,这使得我们可以方便地根据键来查找和遍历数据。TreeMap就是SortedMap的一个具体实现。
TreeMap
TreeMap是Java中的一种集合类,它实现了SortedMap接口,因此具有按照键的自然顺序或自定义顺序对元素进行排序的特性。TreeMap的底层实现采用了红黑树数据结构,这使得元素在TreeMap中按照键的排序顺序存储,从而可以高效地进行查找、插入和删除操作。同样,TreeMap中的键是唯一的,相同的键只能存储一个元素。
SortedSet
SortedSet
是一个接口,它扩展了 Set
接口并添加了排序的功能。SortedSet
中的元素都是唯一的,并且根据它们的自然顺序(如果元素实现了 Comparable
接口)或根据创建 SortedSet
时提供的 Comparator
进行排序。
SortedSet
接口定义了一些额外的方法,例如 first()
和 last()
,分别用于获取集合中的第一个和最后一个元素;headSet(E toElement)
、tailSet(E fromElement)
和 subSet(E fromElement, E toElement)
方法分别用于获取小于某个元素的集合、大于某个元素的集合以及介于两个元素之间的集合。
TreeSet
TreeSet是Java集合框架中的另一种有序集合,它实现了Set接口,因此具有不允许重复元素的特性。TreeSet使用红黑树数据结构来存储元素,这使得元素在集合中保持有序。TreeSet中的元素按照自然排序(元素的自然顺序)或者指定的排序方式(通过比较器)排列,因此遍历TreeSet得到的元素是按照一定的顺序排列的。同样,TreeSet也保证元素的唯一性,不允许重复元素。
一个TreeSet的简单代码示例:
import java.util.TreeSet;
public class TreeSetExample {
public static void main(String[] args) {
TreeSet<Integer> set = new TreeSet<>();
set.add(3);
set.add(1);
set.add(2);
// 输出:[1, 2, 3]
System.out.println(set);
// 获取并输出第一个元素
System.out.println(set.first()); // 输出:1
// 获取并输出最后一个元素
System.out.println(set.last()); // 输出:3
// 获取并输出小于3的集合
System.out.println(set.headSet(3)); // 输出:[1, 2]
// 获取并输出大于1的集合
System.out.println(set.tailSet(2)); // 输出:[2, 3]
// 获取并输出介于1和3之间的集合(不包括3)
System.out.println(set.subSet(1, 3)); // 输出:[1, 2]
}
}
从以上的简单介绍中可以看出,SortedMap、SortedSet是通过自然序或Comparator来比较排序,TreeMap、TreeSet用到了红黑树。
红黑树:
红黑树(Red Black Tree)是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,其典型的用途是实现关联数组。它是在1972年由Rudolf Bayer发明的,当时被称为平衡二叉B树(symmetric binary B-trees)。后来,在1978年被Leo J. Guibas和Robert Sedgewick修改为如今的“红黑树”。
红黑树本身是一种特殊的二叉树,它的每个节点要么是黑色,要么是红色。它满足以下五个性质:
- 根节点是黑色。
- 每个叶子节点(Nil或空值)都是黑色。
- 每个红色节点的两个子节点都是黑色(但黑色节点的子节点可以是黑色,也可以是红色)。
- 从任一节点到其每个叶子节点的所有简单路径都包含相同数量的黑色节点。
- 每个简单路径都包含相同数目的黑色节点。
红黑树在进行插入和删除操作时,会通过特定的操作保持二叉查找树的平衡,从而确保在最坏情况下也有良好的运行时间。这使得红黑树在查找、插入和删除操作上的时间复杂度都是O(log n),其中n是树中元素的数目。
红黑树的这些特性使得它在计算机科学领域得到了广泛的应用,尤其是在需要高效处理大量有序数据的场景中。
红黑树的java实现:
Java中实际上内置了对红黑树的实现,最典型的是在java.util.TreeMap
和java.util.TreeSet
中。这些类内部使用了红黑树来存储元素,并提供了高效的查找、插入和删除操作。
不过,要完全从头开始用Java编写一个红黑树的实现是相当复杂的。
这里提供一个简化版的红黑树节点定义和插入操作的示例代码:
public class RedBlackTree {
// 定义红黑树节点的颜色
enum Color {
RED, BLACK
}
// 定义红黑树节点
static class Node {
int key;
Color color;
Node left;
Node right;
Node parent;
Node(int key, Color color) {
this.key = key;
this.color = color;
this.left = null;
this.right = null;
this.parent = null;
}
}
// 根节点
private Node root;
// 向红黑树中插入新节点
public void insert(int key) {
Node newNode = new Node(key, Color.RED); // 新节点默认为红色
if (root == null) {
root = newNode;
root.color = Color.BLACK; // 根节点必须为黑色
return;
}
Node current = root;
Node parent = null;
// 找到新节点的插入位置
while (current != null) {
parent = current;
if (newNode.key < current.key) {
current = current.left;
} else {
current = current.right;
}
}
newNode.parent = parent;
if (newNode.key < parent.key) {
parent.left = newNode;
} else {
parent.right = newNode;
}
// 修复红黑树
fixInsert(newNode);
}
// 修复插入新节点后的红黑树
private void fixInsert(Node k) {
// 插入修复算法:向上修复颜色及进行旋转
while (k != root && k.parent.color == Color.RED) {
if (parentOf(k) == leftOf(parentOf(parentOf(k)))) {
Node uncle = rightOf(parentOf(parentOf(k)));
if (uncle != null && uncle.color == Color.RED) {
// 情况1: 叔叔节点也是红色
parentOf(k).color = Color.BLACK;
uncle.color = Color.BLACK;
parentOf(parentOf(k)).color = Color.RED;
k = parentOf(parentOf(k));
} else {
// 情况2: 叔叔节点是黑色且k是右孩子
if (k == rightOf(parentOf(k))) {
k = parentOf(k);
leftRotate(k);
}
// 情况3: 叔叔节点是黑色且k是左孩子
parentOf(k).color = Color.BLACK;
parentOf(parentOf(k)).color = Color.RED;
rightRotate(parentOf(parentOf(k)));
}
} else {
// 镜像情况
Node uncle = leftOf(parentOf(parentOf(k)));
if (uncle != null && uncle.color == Color.RED) {
parentOf(k).color = Color.BLACK;
uncle.color = Color.BLACK;
parentOf(parentOf(k)).color = Color.RED;
k = parentOf(parentOf(k));
} else {
if (k == leftOf(parentOf(k))) {
k = parentOf(k);
rightRotate(k);
}
parentOf(k).color = Color.BLACK;
parentOf(parentOf(k)).color = Color.RED;
leftRotate(parentOf(parentOf(k)));
}
}
}
root.color = Color.BLACK;
}
// 左旋转
private void leftRotate(Node x) {
Node y = x.right;
x.right = y.left;
if (y.left != null) {
y.left.parent = x;
}
y.parent = x.parent;
if (x.parent == null) {
root = y;
} else if (x == x.parent.left) {
x.parent.left = y;
} else {
x.parent.right = y;
}
y.left = x;
x.parent = y;
}
// 右旋转
private void rightRotate(Node y) {
Node x = y.left;
y.left = x.right;
if (x.right != null) {
x.right.parent = y;
}
x.parent = y.parent;
if (y.parent == null) {
root = x;
} else if (y == y.parent.right) {
y.parent.right = x;
} else {
y.parent.left = x;
}
x.right = y;
y.parent = x;
}
// 获取节点的父节点
private Node parentOf(Node node) {
return node != null ? node.parent : null;
}
// 获取节点的左孩子
private Node leftOf(Node node) {
return node != null ? node.left : null;
}
// 获取节点的右孩子
private Node rightOf(Node node) {
return node != null ? node.right : null;
}
// ...(其他需要的方法和功能)
public static void main(String[] args) {
RedBlackTree tree = new RedBlackTree();
// 示例:插入节点
tree.insert(5);
tree.insert(3);
tree.insert(8);
tree.insert(10);
tree.insert(1);
// ...(其他操作)
}
}
请注意,这只是一个非常简化的示例,它不包括完整的红黑树功能(如删除操作)以及完整的错误检查和边界情况处理。在实际应用中,您可能需要使用现有的数据结构库(如Java的TreeMap
或TreeSet
),或者查阅更完整的红黑树实现,以确保正确性和性能。
更详细的信息,请阅读java.util.TreeMap或java.util.TreeSet中的代码实现。