下面编写的HashMap与JDK1.8源码的hHashMap来说:
忽略了链表转化为红黑树的情况。
- public class HashMap
implements Map{ - //是所有节点数量总大小
- private int size ;
- private static final boolean RED = false ;
- private static final boolean BLACK = true ;
- //table数组存储每一个插入的红黑树的根节点Node
- private Node
[] table ; - //table数组一开始默认给一个初始长度(哈希表数组最好长度为 2的n次幂)
- private static final int DEFAULT_CAPACITY = 1 << 4 ;
-
- public HashMap() {
- table = new Node[DEFAULT_CAPACITY] ;
- }
- @Override
- public int size() {
- return size;
- }
-
- @Override
- public boolean isEmpty() {
- return size == 0 ;
- }
-
- @Override
- public void clear() {
- if (size == 0) {
- return;//如果哈希表没有Node节点 那么直接返回
- }
- for(int i = 0 ; i
- table[i] = null ;
- }
- size = 0 ;
- }
-
- /**
- * 添加操作
- * @param key
- * @param value
- * @return 返回值为被覆盖的value值
- */
- @Override
- public V put(K key, V value) {
- resize() ;
- int index = index(key) ;
- //取出index 位置的红黑树根节点
- Node
root = table[index] ; - boolean searched = false ;
- if (root == null) {
- root = new Node(key,value, root.hash, null) ;
- table[index] = root ;
- size ++ ;
- afterPut(root);
- return null ;
- }
- //2.说明添加的不是树中第一个元素
- //2.1 确定根节点 root
- Node
node = root ; - //2.2 假设出待插入节点对应的父节点的值
- Node
parent = null ; - //2.3 记录最后一次比较器返回的值 来确定待插入节点插入到哪个方向
- int cmp = 0 ;
- K k1 = key ;
- int h1 = key == null ? 0 : key.hashCode() ;//找出传入的key对应的hash值
- //2.4 循环寻找待插入节点需要插入到的位置
- do {
- //记录cmp
- cmp = compare(node.key,key,h1,node.hash) ;
- //记录parent
- parent = node ;
- K k2 = node.key ;
- int h2 = node.hash ;
- Node
result = null ; - //和node方法思路基本一致 所以详细注释见node方法
- if (h1 > h2) {
- cmp = 1 ;
- } else if (h1 < h2) {
- cmp = -1 ;
- } else if (Objects.equals(k1,k2)) {//已经判断过内存地址相同的情况了
- cmp = 0 ;
- } else if (k1 != null && k2 != null
- && k1.getClass() == k2.getClass()
- && k1 instanceof Comparable
- && (cmp = ((Comparable)k1).compareTo(k2))!=0) {
- //排除compareTo方法返回结果为0的情况 因为为0也并不代表两个对象是相同的
-
- } else if (!searched) {//searched = false
- //先扫描红黑树 如果不存在待添加的key 那么再进行使用内存地址比较!
- if ((node.left != null && (result = node(node.left,k1))!=null)
- ||(node.right != null && (result = node(node.right,k1))!=null)) {
- //说明已经存在待添加的key,此时result为待添加的节点在红黑树中所找到的节点 。node为根节点
- //要新赋值node节点 重复利用之后的代码
- node = result ;
- cmp = 0 ;
- } else { //不存在这个key
- searched = true ;
- //说明不存在待添加节点对应的key值 那么只可以使用内存地址进行比较添加
- cmp = System.identityHashCode(k1) - System.identityHashCode(k2) ;
- }
- } else {
- //searched = true
- cmp = System.identityHashCode(k1) - System.identityHashCode(k2) ;
- }
- if (cmp > 0) {
- //说明已存在节点元素值大,那么插到左边
- node = node.right ;
- } else if (cmp < 0) {
- node = node.left ;
- } else {
- //cmp == 0 如果自定义比较器比较出相等时,我们这里的处理方式是覆盖值操作
- node.key = key ;
- V oldValue = node.value ;
- node.value = value ;
- return oldValue ;//返回的是被覆盖的value值
- }
- } while (node != null) ;
- //3.退出循环之后,根据记录的parent值 进行创建待插入节点对应的Node对象
- Node
newNode = new Node<>(key,value,h1,parent) ; - //4.根据记录的cmp值和parent值,进行确定把待插入节点插入到哪一个位置
- if (cmp > 0) {
- parent.left = newNode ;
- } else {
- //cmp < 0 的情况,cmp == 0 直接就返回了已经
- parent.right = newNode ;
- }
- size ++ ;
- //添加node节点后 进行的调整
- afterPut(node); ;
- return null ;
- }
-
- /**
- * 获取对应key的value
- * @param key 传入的key键值
- * @return 返回key对应的value值
- */
- @Override
- public V get(K key) {
- Node
node = node(key) ; - return node != null ? node.value : null ;
- }
-
- @Override
- public V remove(K key) {
- return remove(node(key));
- }
-
- @Override
- public boolean containsKey(K key) {
- return node(key) != null ;
- }
-
- /**
- * 对于value的值不可以直接查询到 需要遍历所有节点
- * @param value
- * @return
- */
- @Override
- public boolean containsValue(V value) {
- if (size == 0) {//如果没有节点 那么返回false
- return false ;
- }
- Queue
> queue = new LinkedList<>() ; - //这里的层序遍历和之前不同 需要遍历多个红黑树 所以使用for循环
- for (int i = 0 ;i< table.length ;i++) {
- if (table[i] == null) {
- return false ;
- }
- queue.offer(table[i]) ;
- while (!queue.isEmpty()) {
- Node
node = queue.poll() ; - if (Objects.equals(value,node.value)) {
- return true ;
- }
- if (node.left != null) {
- queue.offer(node.left) ;
- }
- if (node.left != null) {
- queue.offer(node.right) ;
- }
- }
- }
- return false;
- }
-
- @Override
- public void traversal(Visitor
visitor) { - if (size == 0 || visitor == null) {
- return;
- }
- Queue
> queue = new LinkedList<>() ; - for (int i = 0;i < table.length ;i++) {
- if (table[i] == null) {
- return;
- }
- queue.offer(table[i]) ;
- while (!queue.isEmpty()) {
- Node
node = queue.poll() ; - if (visitor.visit(node.key,node.value)) {
- return;
- }
- if (node.left != null) {
- queue.offer(node.left) ;
- }
- if (node.right != null) {
- queue.offer(node.right) ;
- }
- }
- }
- }
-
- private void resize() {
- //说明装填因子 <= 0.75 无需扩容 直接返回
- if (size / table.length <= DEFAULT_CAPACITY) {
- return;
- }
- // >0.75 扩容到原来的两倍
- Node
[]oldTable = table ; - table = new Node[oldTable.length >> 1] ;
- Queue
> queue = new LinkedList<>() ; - //把原来table数组上的key-value键值对每一个节点都进行转移到新数组
- /**
- * 当扩容为原来容量的2倍时,节点的索引有2种情况:
- * 1.保持不变
- * 2.要么就是index = index + 旧容量
- */
- for (int i = 0 ; i< oldTable.length ;i++) {
- if (oldTable[i] == null) {
- return;
- }
- queue.offer(oldTable[i]) ;
- while (!queue.isEmpty()) {
- Node
node = queue.poll() ; - if (node.left != null) {
- queue.offer(node.left) ;
- }
- if (node.right != null) {
- queue.offer(node.right) ;
- }
- moveNode(node) ;//移动遍历到旧数组的每一个元素
- }
- }
- }
- private void moveNode(Node
newNode) { - //重置清空所有节点的父子关系以及左右关系
- newNode.parent = null ;
- newNode.left = null ;
- newNode.right = null ;
-
- int index = index(newNode.key) ;
- //取出index 位置的红黑树根节点
- Node
root = table[index] ; - if (root == null) {//说明该桶索引位置还没有节点插入
- root = newNode ;//所以直接插给root
- table[index] = root ;
- afterPut(root);
- return;
- }
- //2.说明添加的不是树中第一个元素
- //2.1 确定根节点 root
- Node
node = root ; - //2.2 假设出待插入节点对应的父节点的值
- Node
parent = null ; - //2.3 记录最后一次比较器返回的值 来确定待插入节点插入到哪个方向
- int cmp = 0 ;
- K k1 = newNode.key ;
- int h1 = newNode.hash ;//找出传入的key对应的hash值
- //2.4 循环寻找待插入节点需要插入到的位置
- do {
- //记录parent
- parent = node ;
- K k2 = node.key ;
- int h2 = node.hash ;
- Node
result = null ; - //和node方法思路基本一致 所以详细注释见node方法
- if (h1 > h2) {
- cmp = 1 ;
- } else if (h1 < h2) {
- cmp = -1 ;
- } else if (k1 != null && k2 != null
- && k1 instanceof Comparable
- && k1.getClass() == k2.getClass()
- && (cmp = ((Comparable) k1).compareTo(k2))!=0) {
-
- } else {
- //searched = true
- cmp = System.identityHashCode(k1) - System.identityHashCode(k2) ;
- }
- if (cmp > 0) {
- //说明已存在节点元素值大,那么插到左边
- node = node.right ;
- } else if (cmp < 0) {
- node = node.left ;
- }
- } while (node != null) ;
- //4.根据记录的cmp值和parent值,进行确定把待插入节点插入到哪一个位置
- if (cmp > 0) {
- parent.left = newNode ;
- } else {
- //cmp < 0 的情况,cmp == 0 直接就返回了已经
- parent.right = newNode ;
- }
- newNode.parent = parent ;
- //添加node节点后 进行的调整
- afterPut(node);
- }
- private V remove(Node
node) { - if (node == null) {
- return null;
- }
- V oldValue = node.value ;
- size -- ;
- //(1)删除的是度为2的节点
- if (node.hasTwoChildren()) {
- //找到该节点对应的前驱或后继节点
- Node
s = succeed(node) ;//找到后继节点 - //使用后继节点的值进行覆盖度为2的节点的值
- node.key = s.key ;
- node.value = s.value ;
- //把待删除的节点进行赋值为前驱或后继节点,使用后面的逻辑进行删除前驱后继节点
- node = s ;
- }
- //必须搞出一个replacement进行记录删除的是根节点左子树还是右子树的节点
- Node
replacement = node.left != null ? node.left : node.right ; - int index = index(node.key) ;
- //(2)删除的是度为1的节点
- if (replacement != null) {
- //统一进行更新删除节点度为1的操作之后的parent指向值
- replacement.parent = node.parent ;
- //2.1 如果是度为1的节点并且是root根节点
- if (node.parent == null) {
- table[index] = replacement ;
- } else if (node == node.parent.left) {
- //2.2 如果是左子树上的度为1的节点
- node.parent.left = replacement ;
- } else if (node == node.parent.right) {
- //2.3 如果是右子树上的度为1的节点
- node.parent.right = replacement ;
- }
- //真正被删除的是度为1或2的前驱或后继节点
- afterRemove(node,replacement) ;
- } else if (node.parent == null) {// (3) 如果删除的是叶子节点并且为root根节点
- table[index] = null ;
- } else {//(4) 如果删除的是叶子节点但是不为root根节点
- if (node == node.parent.right) {
- //4.1 如果是左子树上的度为0的节点
- node.parent.right = null ;
- } else {//node = node.parent.left
- //4.2 如果是右子树上的度为0的节点
- node.parent.left = null ;
- }
- }
- return oldValue ;
- }
- /**
- * 找到后继节点
- * @param node
- * @return
- */
- private Node
succeed(Node node) { - if (node == null) {
- return null ;
- }
- //1,
- Node
p = node ; - if (p.right != null) {
- p = p.right ;
- while (p.left != null) {
- p = p.left ;
- }
- return p ;
- }
- //p.right == null
- //目的是找node节点的后继节点,所以要找第一个比它大的
- //所以在父节点不为空 并且一直在变化的父节点的右子树 、
- // 如果它发现在左子树后,那么就要退出循环,说明找到了第一个比它大的节点
- if (p.parent != null && p == p.parent.right) {
- p = p.parent ;
- }
- //p.parent == null
- //p == p.parent.left
- return p.parent ;
- }
- /**
- * HashMap中的key是不一定 也不太可能具备可比较性的
- * 所以使用hash值去进行决定出加到哪个位置
- * @param k1 键值1
- * @param k2 键值2
- * @param h1 k1对应的hash值
- * @param h2 k2对应的hash值
- * @return
- */
- private int compare(K k1,K k2,int h1,int h2) {
- //比较哈希值
- int result = h1 - h2 ;
- if (result != 0) {
- return result ;
- }
- // result == 0 说明哈希值相等 但是还要进行比较是否equals
- boolean equals = Objects.equals(k1, k2);//已经判断出k1==null && k2 == null的情况了。
- if (equals) {
- //说明k1,k2equals
- return 0 ;
- }
- //说明只是哈希值相等 但是不equals
- //但是总要分辨出谁大谁小 所以比较类名
- if (k1 != null && k2 != null) {
- String k1Cls = k1.getClass().getName() ;
- String k2Cls = k2.getClass().getName() ;
- result = k1Cls.compareTo(k2Cls) ;
- if (result != 0) {
- return result ;
- }
- //result == 0 说明是同一种类型并且具有可比较性
- if (k1 instanceof Comparable) {
- return ((Comparable)k1).compareTo(k2) ;
- }
- }
- //说明是同一种类型 哈希值相等 但是不具备可比较性
- //k1 != null && k2 == null
- //k1 == null && k2 != null
- //此时只可以进行两个key键值内存地址的比较
- return System.identityHashCode(k1) - System.identityHashCode(k2) ;
- }
-
- private void afterRemove(Node
node, Node replacement) { - //1。如果删除的节点是红色RED,删除之后无需处理
- if (isRed(node)) return;
- //2.用以取代被删除的node的子节点是RED时
- if (isRed(replacement)) {
- black(replacement) ;
- return;
- }
- Node
parent = node.parent ; - //删除的是根节点
- if (parent == null) return;
- //3.删除的是黑色叶子节点
- //判断被删除的node是左还有右:
- //parent.left == null说明被删除的node节点是parent的左子树,
- // 那么被删除的node节点的兄弟节点即是parent的右子树,反之同理即可
- boolean left = parent.left == null ;
- Node
sibling = left ? parent.right : parent.left ; - if (left) {//3.1被删除的节点在左边,那么兄弟节点sibling在右边
- if (isRed(sibling)) {
- //3.2.1删除的是黑色叶子节点并且sibling是红色,那么转换为sibling为黑色
- black(sibling) ;
- red(parent) ;
- rotateRight(parent) ;
- //更换兄弟
- sibling = parent.right ;
- }
- //3.2.2删除的是黑色叶子节点并且sibling为BLACK
- //3.2.2.1 兄弟节点没有一个是红色子节点,父节点要下根兄弟节点合并
- if (isBlack(sibling.right) && isBlack(sibling.left)) {
- //记录一下是否父节点也会下溢
- boolean parentBlack = isBlack(parent) ;
- black(parent) ;
- red(sibling) ;
- if (parentBlack) {
- afterRemove(parent,null) ;
- }
- } else { //3.2.2.2 兄弟节点至少有1个红色子节点,被删除节点向兄弟节点借用元素
- //兄弟节点的左边是黑色,说明右边是红色子节点
- if (isBlack(sibling.right)) {
- rotateLeft(sibling) ;
- sibling = parent.right ;//旋转后要更新兄弟节点
- color(sibling,colorOf(parent)) ;
- black(sibling.right) ;
- black(parent) ;
- }
- //兄弟节点的左边是红色,说明左边是红色子节点
- color(sibling,colorOf(parent)) ;
- black(sibling.right) ;
- black(parent) ;
- rotateRight(parent);
- }
- } else {//3.2被删除的节点在右边,那么兄弟节点sibling在左边
- if (isRed(sibling)) {
- //3.2.1删除的是黑色叶子节点并且sibling是红色,那么转换为sibling为黑色
- black(sibling) ;
- red(parent) ;
- rotateRight(parent) ;
- //更换兄弟
- sibling = parent.left ;
- }
- //3.2.2删除的是黑色叶子节点并且sibling为BLACK
- //3.2.2.1 兄弟节点没有一个是红色子节点,父节点要下根兄弟节点合并
- if (isBlack(sibling.left) && isBlack(sibling.right)) {
- //记录一下是否父节点也会下溢
- boolean parentBlack = isBlack(parent) ;
- black(parent) ;
- red(sibling) ;
- if (parentBlack) {
- afterRemove(parent,null) ;
- }
- } else { //3.2.2.2 兄弟节点至少有1个红色子节点,被删除节点向兄弟节点借用元素
- //兄弟节点的左边是黑色,说明右边是红色子节点
- if (isBlack(sibling.left)) {
- rotateLeft(sibling) ;
- sibling = parent.left ;//旋转后要更新兄弟节点
- color(sibling,colorOf(parent)) ;
- black(sibling.left) ;
- black(parent) ;
- }
- //兄弟节点的左边是红色,说明左边是红色子节点
- color(sibling,colorOf(parent)) ;
- black(sibling.left) ;
- black(parent) ;
- rotateRight(parent);
- }
- }
- }
- /**
- * 添加操作可能毁坏红黑树的性质
- * afterPut修复红黑树的性质
- * @param node
- */
- private void afterPut(Node
node) { - Node
parent = node.parent ; - //1.情况一中的四种添加位置,是无需进行修复处理红黑树的。
- //1.1添加的是根节点
- if (parent == null) {
- black(node) ;//根节点直接染成黑色
- return;
- }
- //1.2如果父节点是黑色,直接返回
- if (isBlack(parent)) return ;
- //2.情况二中的八种添加位置,是需要进行修复处理红黑树的。
- //2.1拿到uncle节点进行判断 uncle节点:叔父节点即是父节点的兄弟节点
- Node
uncle = parent.sibling() ; - //2.2祖父节点
- Node
grand = parent.parent ; - //2.3 叔父节点为RED,只需进行染色 然后进行递归grand作为新添加的节点进行处理
- if (isRed(uncle)) {
- black(parent) ;
- black(uncle) ;
- //把祖父节点当作是新添加的节点进行处理
- red(grand) ;
- afterPut(grand); ;
- return;
- }
- //2.4 叔父节点不为RED,需要进行旋转处理
- if (parent.isLeftChild()) { //parent是grand的左子树节点 L
- if (node.isLeftChild()) { //node是parent的左子树节点 LL
- //LL和RR为一类,parent染成BLack,grand染成RED
- black(parent) ;
- red(grand) ;
- rotateRight(grand);
- } else { //LR
- //LR 和 RL为一类,新添加的节点染成Black,grand染成RED
- black(node) ;
- red(grand) ;
- rotateLeft(parent) ;
- rotateRight(grand) ;
- }
- } else { //R
- if (node.isLeftChild()) { //RL
- //LR 和 RL为一类,新添加的节点染成Black,grand染成RED
- black(node) ;
- red(grand) ;
- rotateRight(parent); ;
- rotateLeft(grand); ;
- } else {//RR
- //LL和RR为一类,parent染成BLack,grand染成RED
- black(parent) ;
- red(grand) ;
- rotateLeft(grand);
- }
- }
- }
-
- /**
- * 对传入的节点进行染色处理
- * @param node 想要染色的节点
- * @param color 把该节点node染成color颜色
- * @return 返回染色后的节点
- */
- private Node
color(Node node, boolean color) { - if (node == null) {
- return node ;
- }
- node.color = color ;
- return node ;
- }
-
- /**
- * 把该node节点染成RED
- */
- private Node
red(Node node) { - return color(node,RED) ;
- }
-
- /**
- * 把该node节点染成BLACK
- */
- private Node
black(Node node) { - return color(node,BLACK) ;
- }
-
- /**
- * 传入一个node节点,进行返回该节点是什么颜色
- */
- private boolean colorOf(Node
node) { - //如果node为null,即是null节点 即是BLACK
- // 【对null节点进行特殊处理的原因是:我们一开始并没有对null节点的颜色进行设置】
- //如果不为空,那么返回node实际的颜色即可
- return node == null ? BLACK : node.color ;
- }
-
- /**
- * 判断该node节点颜色color是否为BLACK
- */
- private boolean isBlack(Node
node) { - return colorOf(node) == BLACK ;
- }
-
- /**
- * 判断该node节点颜色color是否为RED
- */
- private boolean isRed(Node
node) { - return colorOf(node) == RED ;
- }
-
- /**
- * 左旋转
- * @param grand 想要进行旋转的节点,传过来的是什么,什么节点就进行左旋转
- */
- private void rotateLeft(Node
grand) { - //进行左旋转,说明是RR,可以确定parent
- Node
parent = grand.right ; - //child即是T1
- Node
child = parent.left ; - grand.right = child; //1.
- parent.left = grand ; //2.
- //执行旋转之后的逻辑
- afterRotate(grand,parent,child);
- }
- /**
- * 右旋转
- * @param grand 想要进行旋转的节点,传过来的是什么,什么节点就进行右旋转
- */
- private void rotateRight(Node
grand) { - //1.进行右旋转,说明是LL,那么可以确定parent
- Node
parent = grand.left ; - //2.找出T2
- Node
child = parent.right ; - //3.对g执行右旋转
- grand.left = child ; //3.1
- parent.right = grand ;//3.2
- //执行旋转之后的逻辑
- afterRotate(grand,parent,child);
- }
- /**
- * 旋转之后统一要做的逻辑
- * @param grand (最低的失去平衡的节点)
- * @param parent (parent是grand的子节点)
- * @param child 旋转过程中父节点改变的节点(T1或T2)
- */
- private void afterRotate(Node
grand, Node parent, Node child) { - int index = index(grand.key) ;
- //3.让parent成为这颗子树的根节点
- //3.1parent先连上grand的父节点
- parent.parent = grand.parent ;
- //3.2 grand再去连上parent
- if (grand.isLeftChild()) {
- //如果grand是父节点的左子树
- grand.parent.left = parent ;
- } else if (grand.isRightChild()) {
- //如果grand是父节点的右子树
- grand.parent.right = parent ;
- } else {//既不是父节点的左子树又不是右子树,那么说明grand即是root根节点
- table[index] = parent ;
- }
- //4.更新child(T1)的parent
- if (child != null) {
- child.parent = grand ;
- }
- //5.更新grand的parent
- grand.parent = parent ;
- }
- /**
- * 根据key找到Node节点
- */
- public Node
node(K key) { - Node
root = table[index(key)] ; - return root == null ? null : node(root,key) ;//递归调用
- }
- /**
- * 根据key找到Node节点
- */
- private Node
node(Node node,K k1) { - int index = index(k1) ;
- int h1 = k1 == null ? 0 : k1.hashCode() ;
- Node
result = null ; - int cmp = 0 ;
- while (node != null) {
- K k2 = node.key ;
- int h2 = node.hash ;
- //先比较哈希值
- if (h1 > h2) {
- node = node.right ;
- } else if (h1 < h2) {
- node = node.left ;
- } else if (Objects.equals(k1,k2)) {
- //哈希值相等 并且equals相等 并且已经判断过内存地址相同的情况了 之后都是内存地址不相等的情况
- return node ;//那么找到了
- } else if (k1 != null && k2 != null
- && k1.getClass() == k2.getClass()
- && k1 instanceof Comparable
- && (cmp = ((Comparable)k1).compareTo(k2))!=0) { //哈希值相等 但是equals不相等 但是类相同 并且具备可比较性
- if (cmp > 0) {
- node = node.right ;
- } else if (cmp < 0) {
- node = node.left;
- }
- // } else { //cmp == 0 并不代表两个对象真的就是相等的,所以要执行后面遍历搜索的代码
- // return node ;
- // }
- } else if (node.right != null && (result = node(node.right,k1))!=null) {
- //哈希值相等 但是不具备可比较性 也不equals。之后进行递归 如果向右可以找到的话
- return result;
- // } else if (node.left != null && (result = node(node.left,k1)) !=null) {
- // //哈希值相等 但是不具备可比较性 也不equals。之后进行递归 如果向左可以找到的话
- // return result ;
- // } else {//找不到
- // return null ;
- // }
- } else {//只能往左边,减少递归
- node = node.left ;
- }
- }
- return null ;//找不到
- }
- /**
- * 根据key生成对应的索引(在桶数组的位置)
- */
- private int index(K key) {
- //1.使用自身定义的hashCode方法获取哈希值
- int hash = key.hashCode();
- //2.自身hashCode方法或许获取的哈希值是不稳定的 所以jdk再进行帮忙均匀计算一下
- hash = hash ^ (hash >>> 16) ;
- //3.计算出索引值并且返回
- return hash & (table.length - 1) ;
- }
- private static class Node
{ - K key ;
- V value ;
- boolean color = RED;
- int hash ;
- Node
left ;//左子节点 - Node
right ;//右子节点 - Node
parent ;//父节点 - int height ;
- public Node(K key, V value , int hash, Node
parent) { - this.key = key ;
- this.value = value ;
- this.hash = key == null ? 0 : key.hashCode() ;
- this.parent = parent ;//除了根节点,其余节点都具有父节点,所以要在构造方法传入
- }
- //判断是否为叶子节点
- public boolean isLeaf() {
- return left == null && right == null ;
- }
- //判断是否具有两个子节点
- public boolean hasTwoChildren() {
- return left != null && right != null ;
- }
- //说明是parent的左子树
- public boolean isLeftChild() {
- return parent != null && this == parent.left ;
- }
- //说明是parent的右子树
- public boolean isRightChild() {
- return parent != null && this == parent.right ;
- }
- /**
- * 找出兄弟节点
- * @return
- */
- public Node
sibling() { - if (isLeftChild()) {
- //说明该节点是父节点的左子树
- return parent.right ;//说明该节点的兄弟节点即是父节点的右子树
- }
- if (isRightChild()) {
- //说明该节点是父节点的右子树
- return parent.left ;//说明该节点的兄弟节点即是父节点的左子树
- }
- //说明该节点既不是父节点的左子树 也不是父节点的右子树
- //那么说明该节点没有父节点,那么就没有兄弟节点
- return null ;
- }
- }
- }