3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
20 package org
.apache
.hadoop
.hbase
.util
;
22 import java
.util
.Iterator
;
23 import java
.util
.concurrent
.atomic
.AtomicBoolean
;
25 import org
.apache
.hadoop
.hbase
.classification
.InterfaceAudience
;
26 import org
.apache
.hadoop
.hbase
.classification
.InterfaceStability
;
29 * Helper class that allows to create and manipulate an AvlTree.
30 * The main utility is in cases where over time we have a lot of add/remove of the same object,
31 * and we want to avoid all the allocations/deallocations of the "node" objects that the
32 * java containers will create.
34 @InterfaceAudience.Private
35 @InterfaceStability.Evolving
36 public final class AvlUtil
{
40 * This class represent a node that will be used in an AvlTree.
41 * Instead of creating another object for the tree node,
42 * like the TreeMap and the other java contains, here the node can be extended
43 * and the content can be embedded directly in the node itself.
44 * This is useful in cases where over time we have a lot of add/remove of the same object.
46 @InterfaceAudience.Private
47 public static abstract class AvlNode
<TNode
extends AvlNode
> {
48 protected TNode avlLeft
;
49 protected TNode avlRight
;
50 protected int avlHeight
;
52 public abstract int compareTo(TNode other
);
56 * This class extends the AvlNode and adds two links that will be used in conjunction
57 * with the AvlIterableList class.
58 * This is useful in situations where your node must be in a map to have a quick lookup by key,
59 * but it also require to be in something like a list/queue.
60 * This is useful in cases where over time we have a lot of add/remove of the same object.
62 @InterfaceAudience.Private
63 public static abstract class AvlLinkedNode
<TNode
extends AvlLinkedNode
> extends AvlNode
<TNode
> {
64 protected TNode iterNext
= null;
65 protected TNode iterPrev
= null;
68 @InterfaceAudience.Private
69 public interface AvlInsertOrReplace
<TNode
extends AvlNode
> {
70 TNode
insert(Object searchKey
);
71 TNode
replace(Object searchKey
, TNode prevNode
);
75 * The AvlTree allows to lookup an object using a custom key.
76 * e.g. the java Map allows only to lookup by key using the Comparator
77 * specified in the constructor.
78 * In this case you can pass a specific comparator for every needs.
80 @InterfaceAudience.Private
81 public static interface AvlKeyComparator
<TNode
extends AvlNode
> {
82 int compareKey(TNode node
, Object key
);
86 * Visitor that allows to traverse a set of AvlNodes.
87 * If you don't like the callback style of the visitor you can always use the AvlTreeIterator.
89 @InterfaceAudience.Private
90 public static interface AvlNodeVisitor
<TNode
extends AvlNode
> {
92 * @param node the node that we are currently visiting
93 * @return false to stop the iteration. true to continue.
95 boolean visitNode(TNode node
);
99 * Helper class that allows to create and manipulate an AVL Tree
101 @InterfaceAudience.Private
102 public static class AvlTree
{
104 * @param root the current root of the tree
105 * @param key the key for the node we are trying to find
106 * @param keyComparator the comparator to use to match node and key
107 * @return the node that matches the specified key or null in case of node not found.
109 public static <TNode
extends AvlNode
> TNode
get(TNode root
, final Object key
,
110 final AvlKeyComparator
<TNode
> keyComparator
) {
111 while (root
!= null) {
112 int cmp
= keyComparator
.compareKey(root
, key
);
114 root
= (TNode
)root
.avlLeft
;
115 } else if (cmp
< 0) {
116 root
= (TNode
)root
.avlRight
;
125 * @param root the current root of the tree
126 * @return the first (min) node of the tree
128 public static <TNode
extends AvlNode
> TNode
getFirst(TNode root
) {
130 while (root
.avlLeft
!= null) {
131 root
= (TNode
)root
.avlLeft
;
138 * @param root the current root of the tree
139 * @return the last (max) node of the tree
141 public static <TNode
extends AvlNode
> TNode
getLast(TNode root
) {
143 while (root
.avlRight
!= null) {
144 root
= (TNode
)root
.avlRight
;
151 * Insert a node into the tree. It uses the AvlNode.compareTo() for ordering.
152 * NOTE: The node must not be already in the tree.
153 * @param root the current root of the tree
154 * @param node the node to insert
155 * @return the new root of the tree
157 public static <TNode
extends AvlNode
> TNode
insert(TNode root
, TNode node
) {
158 if (root
== null) return node
;
160 int cmp
= node
.compareTo(root
);
161 assert cmp
!= 0 : "node already inserted: " + root
;
163 root
.avlLeft
= insert(root
.avlLeft
, node
);
165 root
.avlRight
= insert(root
.avlRight
, node
);
167 return balance(root
);
171 * Insert a node into the tree.
172 * This is useful when you want to create a new node or replace the content
173 * depending if the node already exists or not.
174 * Using AvlInsertOrReplace class you can return the node to add/replace.
176 * @param root the current root of the tree
177 * @param key the key for the node we are trying to insert
178 * @param keyComparator the comparator to use to match node and key
179 * @param insertOrReplace the class to use to insert or replace the node
180 * @return the new root of the tree
182 public static <TNode
extends AvlNode
> TNode
insert(TNode root
, Object key
,
183 final AvlKeyComparator
<TNode
> keyComparator
,
184 final AvlInsertOrReplace
<TNode
> insertOrReplace
) {
186 return insertOrReplace
.insert(key
);
189 int cmp
= keyComparator
.compareKey(root
, key
);
191 root
.avlLeft
= insert((TNode
)root
.avlLeft
, key
, keyComparator
, insertOrReplace
);
192 } else if (cmp
> 0) {
193 root
.avlRight
= insert((TNode
)root
.avlRight
, key
, keyComparator
, insertOrReplace
);
195 TNode left
= (TNode
)root
.avlLeft
;
196 TNode right
= (TNode
)root
.avlRight
;
197 root
= insertOrReplace
.replace(key
, root
);
199 root
.avlRight
= right
;
202 return balance(root
);
205 private static <TNode
extends AvlNode
> TNode
removeMin(TNode p
) {
206 if (p
.avlLeft
== null)
207 return (TNode
)p
.avlRight
;
208 p
.avlLeft
= removeMin(p
.avlLeft
);
213 * Removes the node matching the specified key from the tree
214 * @param root the current root of the tree
215 * @param key the key for the node we are trying to find
216 * @param keyComparator the comparator to use to match node and key
217 * @return the new root of the tree
219 public static <TNode
extends AvlNode
> TNode
remove(TNode root
, Object key
,
220 final AvlKeyComparator
<TNode
> keyComparator
) {
221 return remove(root
, key
, keyComparator
, null);
225 * Removes the node matching the specified key from the tree
226 * @param root the current root of the tree
227 * @param key the key for the node we are trying to find
228 * @param keyComparator the comparator to use to match node and key
229 * @param removed will be set to true if the node was found and removed, otherwise false
230 * @return the new root of the tree
232 public static <TNode
extends AvlNode
> TNode
remove(TNode root
, Object key
,
233 final AvlKeyComparator
<TNode
> keyComparator
, final AtomicBoolean removed
) {
234 if (root
== null) return null;
236 int cmp
= keyComparator
.compareKey(root
, key
);
238 if (removed
!= null) removed
.set(true);
240 TNode q
= (TNode
)root
.avlLeft
;
241 TNode r
= (TNode
)root
.avlRight
;
242 if (r
== null) return q
;
243 TNode min
= getFirst(r
);
244 min
.avlRight
= removeMin(r
);
247 } else if (cmp
> 0) {
248 root
.avlLeft
= remove((TNode
)root
.avlLeft
, key
, keyComparator
);
249 } else /* if (cmp < 0) */ {
250 root
.avlRight
= remove((TNode
)root
.avlRight
, key
, keyComparator
);
252 return balance(root
);
256 * Visit each node of the tree
257 * @param root the current root of the tree
258 * @param visitor the AvlNodeVisitor instance
260 public static <TNode
extends AvlNode
> void visit(final TNode root
,
261 final AvlNodeVisitor
<TNode
> visitor
) {
262 if (root
== null) return;
264 final AvlTreeIterator
<TNode
> iterator
= new AvlTreeIterator
<>(root
);
265 boolean visitNext
= true;
266 while (visitNext
&& iterator
.hasNext()) {
267 visitNext
= visitor
.visitNode(iterator
.next());
271 private static <TNode
extends AvlNode
> TNode
balance(TNode p
) {
273 int balance
= balanceFactor(p
);
275 if (balanceFactor(p
.avlRight
) < 0) {
276 p
.avlRight
= rotateRight(p
.avlRight
);
278 return rotateLeft(p
);
279 } else if (balance
== -2) {
280 if (balanceFactor(p
.avlLeft
) > 0) {
281 p
.avlLeft
= rotateLeft(p
.avlLeft
);
283 return rotateRight(p
);
288 private static <TNode
extends AvlNode
> TNode
rotateRight(TNode p
) {
289 TNode q
= (TNode
)p
.avlLeft
;
290 p
.avlLeft
= q
.avlRight
;
297 private static <TNode
extends AvlNode
> TNode
rotateLeft(TNode q
) {
298 TNode p
= (TNode
)q
.avlRight
;
299 q
.avlRight
= p
.avlLeft
;
306 private static <TNode
extends AvlNode
> void fixHeight(TNode node
) {
307 final int heightLeft
= height(node
.avlLeft
);
308 final int heightRight
= height(node
.avlRight
);
309 node
.avlHeight
= 1 + Math
.max(heightLeft
, heightRight
);
312 private static <TNode
extends AvlNode
> int height(TNode node
) {
313 return node
!= null ? node
.avlHeight
: 0;
316 private static <TNode
extends AvlNode
> int balanceFactor(TNode node
) {
317 return height(node
.avlRight
) - height(node
.avlLeft
);
322 * Iterator for the AvlTree
324 @InterfaceAudience.Private
325 public static class AvlTreeIterator
<TNode
extends AvlNode
> implements Iterator
<TNode
> {
326 private final Object
[] stack
= new Object
[64];
328 private TNode current
= null;
329 private int height
= 0;
331 public AvlTreeIterator() {
335 * Create the iterator starting from the first (min) node of the tree
337 public AvlTreeIterator(final TNode root
) {
342 * Create the iterator starting from the specified key
343 * @param root the current root of the tree
344 * @param key the key for the node we are trying to find
345 * @param keyComparator the comparator to use to match node and key
347 public AvlTreeIterator(final TNode root
, final Object key
,
348 final AvlKeyComparator
<TNode
> keyComparator
) {
349 seekTo(root
, key
, keyComparator
);
353 public boolean hasNext() {
354 return current
!= null;
358 public TNode
next() {
359 final TNode node
= this.current
;
365 public void remove() {
366 throw new UnsupportedOperationException();
370 * Reset the iterator, and seeks to the first (min) node of the tree
371 * @param root the current root of the tree
373 public void seekFirst(final TNode root
) {
377 while (current
.avlLeft
!= null) {
378 stack
[height
++] = current
;
379 current
= (TNode
)current
.avlLeft
;
385 * Reset the iterator, and seeks to the specified key
386 * @param root the current root of the tree
387 * @param key the key for the node we are trying to find
388 * @param keyComparator the comparator to use to match node and key
390 public void seekTo(final TNode root
, final Object key
,
391 final AvlKeyComparator
<TNode
> keyComparator
) {
396 while (node
!= null) {
397 if (keyComparator
.compareKey(node
, key
) >= 0) {
398 if (node
.avlLeft
!= null) {
399 stack
[height
++] = node
;
400 node
= (TNode
)node
.avlLeft
;
406 if (node
.avlRight
!= null) {
407 stack
[height
++] = node
;
408 node
= (TNode
)node
.avlRight
;
411 TNode parent
= (TNode
)stack
[--height
];
412 while (node
== parent
.avlRight
) {
418 parent
= (TNode
)stack
[--height
];
430 private void seekNext() {
431 if (current
== null) return;
432 if (current
.avlRight
!= null) {
433 stack
[height
++] = current
;
434 current
= (TNode
)current
.avlRight
;
435 while (current
.avlLeft
!= null) {
436 stack
[height
++] = current
;
437 current
= (TNode
)current
.avlLeft
;
447 current
= (TNode
)stack
[--height
];
448 } while (current
.avlRight
== node
);
454 * Helper class that allows to create and manipulate a linked list of AvlLinkedNodes
456 @InterfaceAudience.Private
457 public static class AvlIterableList
{
459 * @param node the current node
460 * @return the successor of the current node
462 public static <TNode
extends AvlLinkedNode
> TNode
readNext(TNode node
) {
463 return (TNode
)node
.iterNext
;
467 * @param node the current node
468 * @return the predecessor of the current node
470 public static <TNode
extends AvlLinkedNode
> TNode
readPrev(TNode node
) {
471 return (TNode
)node
.iterPrev
;
475 * @param head the head of the linked list
476 * @param node the node to add to the front of the list
477 * @return the new head of the list
479 public static <TNode
extends AvlLinkedNode
> TNode
prepend(TNode head
, TNode node
) {
480 assert !isLinked(node
) : node
+ " is already linked";
482 TNode tail
= (TNode
)head
.iterPrev
;
483 tail
.iterNext
= node
;
484 head
.iterPrev
= node
;
485 node
.iterNext
= head
;
486 node
.iterPrev
= tail
;
488 node
.iterNext
= node
;
489 node
.iterPrev
= node
;
495 * @param head the head of the linked list
496 * @param node the node to add to the tail of the list
497 * @return the new head of the list
499 public static <TNode
extends AvlLinkedNode
> TNode
append(TNode head
, TNode node
) {
500 assert !isLinked(node
) : node
+ " is already linked";
502 TNode tail
= (TNode
)head
.iterPrev
;
503 tail
.iterNext
= node
;
504 node
.iterNext
= head
;
505 node
.iterPrev
= tail
;
506 head
.iterPrev
= node
;
509 node
.iterNext
= node
;
510 node
.iterPrev
= node
;
515 * @param head the head of the current linked list
516 * @param otherHead the head of the list to append to the current list
517 * @return the new head of the current list
519 public static <TNode
extends AvlLinkedNode
> TNode
appendList(TNode head
, TNode otherHead
) {
520 if (head
== null) return otherHead
;
521 if (otherHead
== null) return head
;
523 TNode tail
= (TNode
)head
.iterPrev
;
524 TNode otherTail
= (TNode
)otherHead
.iterPrev
;
525 tail
.iterNext
= otherHead
;
526 otherHead
.iterPrev
= tail
;
527 otherTail
.iterNext
= head
;
528 head
.iterPrev
= otherTail
;
533 * @param head the head of the linked list
534 * @param node the node to remove from the list
535 * @return the new head of the list
537 public static <TNode
extends AvlLinkedNode
> TNode
remove(TNode head
, TNode node
) {
538 assert isLinked(node
) : node
+ " is not linked";
539 if (node
!= node
.iterNext
) {
540 node
.iterPrev
.iterNext
= node
.iterNext
;
541 node
.iterNext
.iterPrev
= node
.iterPrev
;
542 head
= (head
== node
) ?
(TNode
)node
.iterNext
: head
;
546 node
.iterNext
= null;
547 node
.iterPrev
= null;
552 * @param node the node to check
553 * @return true if the node is linked to a list, false otherwise
555 public static <TNode
extends AvlLinkedNode
> boolean isLinked(TNode node
) {
556 return node
.iterPrev
!= null && node
.iterNext
!= null;