TreeMap的结构是红黑树又称红-黑二叉树,它首先是一颗二叉树,它具体二叉树所有的特性。同时红黑树更是一颗自平衡的排序二叉树。关于红-黑二叉树,可以参考:点击打开链接
在这里,我们主要简单分析TreeMap的源码实现。
public Object put(Object obj, Object obj1)
TreeMap里的数据都是存放在Entry里的,这也是树的节点,下面是Entry的构造方法
<span style="font-size:18px;"> <span> </span>Object key;
Object value;
Entry left;
Entry right;
Entry parent;
boolean color;
Entry(Object obj, Object obj1, Entry entry)
{
left = null;
right = null;
color = true;
key = obj;
value = obj1;
parent = entry;
}</span>
new一个Entry的时候,进行初始化,指定key、value、parent,同时把左右孩子设为null;
下面是创建一个新节点的put方法的源码,并对代码附上相应解释
public Object put(Object obj, Object obj1)
{
Entry entry = root;
if(entry == null) //如果entry==Null,说明该TreeMap为空,则把entry设为根节点
{
compare(obj, obj);
root = new Entry(obj, obj1, null); //创建一个根节点
size = 1;
modCount++;
return null;
}
Comparator comparator1 = comparator;
int i; //变量i,用来记录新创建节点的key值与TreeMap原有节点key值的大小关系
Entry entry1;
if(comparator1 != null)
{
do
{ //这个循环是为了寻找新创建节点的parent,entry1就是新创建节点的parent
entry1 = entry;
i = comparator1.compare(obj, entry.key);
if(i < 0)
entry = entry.left;
else
if(i > 0)
entry = entry.right;
else
return entry.setValue(obj1);
} while(entry != null);
} else
{
if(obj == null)
throw new NullPointerException();
Comparable comparable = (Comparable)obj;
do //这个循环是也是为了寻找新创建节点的parent,entry1就是新创建节点的parent
{
entry1 = entry;
i = comparable.compareTo(entry.key);
if(i < 0)
entry = entry.left;
else
if(i > 0)
entry = entry.right;
else
return entry.setValue(obj1);
} while(entry != null);
}
Entry entry2 = new Entry(obj, obj1, entry1);//这里正式创建一个节点了,并指定entry1为新创建节点的parent
if(i < 0) //判断新创建节点是该作为entry1的左孩子还是右孩子
entry1.left = entry2;
else
entry1.right = entry2;
fixAfterInsertion(entry2);
size++;
modCount++;
return null;
}
private void deleteEntry(Entry entry)
其实更为复杂的还是删除节点,删除节点分为三种情况,一种是删除叶子节点,只要把被删除节点的left,right,parent都设为null就可以;第二种是删除的节点只有一个孩子,也就是只有左孩子或者只有右孩子,假设只有左孩子entryLeft,那就只要把被删除节点的左孩子的parent指向被删除节点的parent,然后再判断被删除节点是自身parent的左孩子还是右孩子,如果是右孩子,则把parent的right指向entryLeft,这样就完成了删除操作;复杂的是第三种情况,就是当被删除节点拥有两个孩子left和right的时候,要判断出哪个是被删除节点的后继节点,由于二叉树的特殊结构决定了某个节点entry的left以及left下所有的节点都小于entry,而entry的right以及right下所有的节点都大于entry,而要满足这一要求,就得从entry的left下找,而且得找最小的节点,这样才满足比entry的left下所有结点都大,同时比entry的right下的所有节点都小。下面是deleteEntry(Entry entry)的源码,并附上解析
static Entry successor(Entry entry)
{
if(entry == null)
return null;
if(entry.right != null)
{
Entry entry1;
for(entry1 = entry.right; entry1.left != null; entry1 = entry1.left);
return entry1;//返回entry节点的右子树中最小的节点(其实就是寻找entry的右子树中最小的左孩子)
}
Entry entry2 = entry.parent;
for(Entry entry3 = entry; entry2 != null && entry3 == entry2.right; entry2 = entry2.parent)
entry3 = entry2;
return entry2;
}
private void deleteEntry(Entry entry)
{
modCount++;
size--;
if(entry.left != null && entry.right != null)//entry有两个孩子的时候的处理
{
Entry entry1 = successor(entry); //寻找entry的后继节点,然后用后继节点替换
entry.key = entry1.key;
entry.value = entry1.value;
entry = entry1;
}
Entry entry2 = entry.left == null ? entry.right : entry.left;//entry2是被删除节点的后继节点(这种情况是针对entry只有一个孩子的时候)
if(entry2 != null) //entry2!=null说明entry有一个左孩子或者右孩子
{
entry2.parent = entry.parent; //把要删除的entry的parent作为后继节点entry2的parent
if(entry.parent == null) //如果entry.parent==null,说明entry是根节点,则把entry2作为根节点
root = entry2;
else
if(entry == entry.parent.left) //如果entry是属于entry.parent的左孩子,则把后继节点entry2也作为entry.parent的左孩子。
entry.parent.left = entry2;
else
entry.parent.right = entry2; //如果entry是属于entry.parent的右孩子,则把后继节点entry2也作为entry.parent的右孩子。
entry.left = entry.right = entry.parent = null; //把entry的left、right、parent都设为null。
if(entry.color)
fixAfterDeletion(entry2);
} else
if(entry.parent == null) //如果entry.parent==null,说明该树只有一个节点,而且是根节点,把根节点设为null
{
root = null;
} else
{
if(entry.color)
fixAfterDeletion(entry);
if(entry.parent != null) //entry为叶子节点的时候的处理,把被删除的left、right、parent都设为Null
{
if(entry == entry.parent.left)
entry.parent.left = null;
else
if(entry == entry.parent.right)
entry.parent.right = null;
entry.parent = null;
}
}
}