/* * JVSTM: a Java library for Software Transactional Memory * Copyright (C) 2005 INESC-ID Software Engineering Group * http://www.esw.inesc-id.pt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Author's contact: * INESC-ID Software Engineering Group * Rua Alves Redol 9 * 1000 - 029 Lisboa * Portugal */ package jvstm.util; import java.util.Comparator; import java.util.Iterator; import java.util.NoSuchElementException; public class RedBlackTreeNode<K,V> { private static final boolean RED = true; private static final boolean BLACK = false; private static final int MODE_REPLACE = 0; private static final int MODE_IF_ABSENT = 1; private static final int MODE_ALWAYS = 2; public static final RedBlackTreeNode EMPTY = new RedBlackTreeNode(BLACK, null, null, null, null); private boolean color; private K key; private V value; private RedBlackTreeNode<K,V> left; private RedBlackTreeNode<K,V> right; private RedBlackTreeNode(boolean color, K key, V value, RedBlackTreeNode<K,V> left, RedBlackTreeNode<K,V> right) { this.color = color; this.key = key; this.value = value; this.left = left; this.right = right; } /** * Returns a new node that represents the tree with <tt>key</tt> * mapped to <tt>value</tt>. The node returned is always a new * node, regardless of whether the key existed already in the tree * or not. * * @param key key with which the specified value is to be associated. * @param value value to be associated with the specified key. * @param comparator the comparator that will be used to sort the * keys in the tree. If <tt>null</tt>, the natural * order of the <tt>key</tt> is used, as per the * Comparable interface. * @return a new node that represents a tree where the <tt>key</tt> * is mapped to <tt>value</tt>. */ public RedBlackTreeNode<K,V> put(K key, V value, Comparator<? super K> comparator) { return insert(key, value, comparator, MODE_ALWAYS).first; } public Pair<RedBlackTreeNode<K,V>,V> replace(K key, V value, Comparator<? super K> comparator) { return insert(key, value, comparator, MODE_REPLACE); } public Pair<RedBlackTreeNode<K,V>,V> putIfAbsent(K key, V value, Comparator<? super K> comparator) { return insert(key, value, comparator, MODE_IF_ABSENT); } private Pair<RedBlackTreeNode<K,V>,V> insert(K key, V value, Comparator<? super K> comparator, int mode) { Pair<RedBlackTreeNode<K,V>,V> result = new Pair<RedBlackTreeNode<K,V>,V>(); if (comparator == null) { insertComparable((Comparable<K>)key, value, result, mode); } else { insert(key, value, comparator, result, mode); } if (result.first != null) { result.first.color = BLACK; } return result; } private void insert(K key, V value, Comparator<? super K> comparator, Pair<RedBlackTreeNode<K,V>,V> result, int mode) { if (this == EMPTY) { if (mode != MODE_REPLACE) { result.first = new RedBlackTreeNode<K,V>(RED, key, value, EMPTY, EMPTY); } } else { int cmp = comparator.compare(key, this.key); if (cmp < 0) { this.left.insert(key, value, comparator, result, mode); if (result.first != null) { lbalance(this, result); } } else if (cmp > 0) { this.right.insert(key, value, comparator, result, mode); if (result.first != null) { rbalance(this, result); } } else { // key exists already if (mode != MODE_IF_ABSENT) { result.first = new RedBlackTreeNode<K,V>(this.color, key, value, this.left, this.right); } result.second = this.value; } } } private void insertComparable(Comparable<K> key, V value, Pair<RedBlackTreeNode<K,V>,V> result, int mode) { if (this == EMPTY) { if (mode != MODE_REPLACE) { result.first = new RedBlackTreeNode<K,V>(RED, (K)key, value, EMPTY, EMPTY); } } else { int cmp = key.compareTo(this.key); if (cmp < 0) { this.left.insertComparable(key, value, result, mode); if (result.first != null) { lbalance(this, result); } } else if (cmp > 0) { this.right.insertComparable(key, value, result, mode); if (result.first != null) { rbalance(this, result); } } else { // key exists already if (mode != MODE_IF_ABSENT) { result.first = new RedBlackTreeNode<K,V>(this.color, (K)key, value, this.left, this.right); } result.second = this.value; } } } private void lbalance(RedBlackTreeNode<K,V> node, Pair<RedBlackTreeNode<K,V>,V> result) { RedBlackTreeNode<K,V> left = result.first; if ((node.color == BLACK) && (left.color == RED)) { if (left.left.color == RED) { result.first = new RedBlackTreeNode<K,V>(RED, left.key, left.value, new RedBlackTreeNode<K,V>(BLACK, left.left.key, left.left.value, left.left.left, left.left.right), new RedBlackTreeNode<K,V>(BLACK, node.key, node.value, left.right, node.right)); return; } if (left.right.color == RED) { result.first = new RedBlackTreeNode<K,V>(RED, left.right.key, left.right.value, new RedBlackTreeNode<K,V>(BLACK, left.key, left.value, left.left, left.right.left), new RedBlackTreeNode<K,V>(BLACK, node.key, node.value, left.right.right, node.right)); return; } } result.first = new RedBlackTreeNode<K,V>(node.color, node.key, node.value, left, node.right); } private void rbalance(RedBlackTreeNode<K,V> node, Pair<RedBlackTreeNode<K,V>,V> result) { RedBlackTreeNode<K,V> right = result.first; if ((node.color == BLACK) && (right.color == RED)) { if (right.left.color == RED) { result.first = new RedBlackTreeNode<K,V>(RED, right.left.key, right.left.value, new RedBlackTreeNode<K,V>(BLACK, node.key, node.value, node.left, right.left.left), new RedBlackTreeNode<K,V>(BLACK, right.key, right.value, right.left.right, right.right)); return; } if (right.right.color == RED) { result.first = new RedBlackTreeNode<K,V>(RED, right.key, right.value, new RedBlackTreeNode<K,V>(BLACK, node.key, node.value, node.left, right.left), new RedBlackTreeNode<K,V>(BLACK, right.right.key, right.right.value, right.right.left, right.right.right)); return; } } result.first = new RedBlackTreeNode<K,V>(node.color, node.key, node.value, node.left, right); } public V get(K key, Comparator<? super K> comparator) { RedBlackTreeNode<K,V> node = getNode(key, comparator); return (node == null) ? null : node.value; } protected RedBlackTreeNode<K,V> getNode(K key, Comparator<? super K> comparator) { if (comparator == null) { return findNodeComparable((Comparable<K>)key); } else { return findNode(key, comparator); } } private RedBlackTreeNode<K,V> findNode(K key, Comparator<? super K> comparator) { RedBlackTreeNode<K,V> iter = this; while (iter != EMPTY) { int cmp = comparator.compare(key, iter.key); if (cmp < 0) { iter = iter.left; } else if (cmp > 0) { iter = iter.right; } else { return iter; } } return null; } private RedBlackTreeNode<K,V> findNodeComparable(Comparable<K> key) { RedBlackTreeNode<K,V> iter = this; while (iter != EMPTY) { int cmp = key.compareTo(iter.key); if (cmp < 0) { iter = iter.left; } else if (cmp > 0) { iter = iter.right; } else { return iter; } } return null; } public Iterator<RedBlackTreeNode<K,V>> iterator() { return new RBTIterator<K,V>(this); } static class RBTIterator<K,V> implements Iterator<RedBlackTreeNode<K,V>> { protected Cons<RedBlackTreeNode<K,V>> path; protected RedBlackTreeNode<K,V> next; RBTIterator() { this.path = Cons.empty(); } RBTIterator(RedBlackTreeNode<K,V> root) { this(); if (root != EMPTY) { findLeftmost(root); } } private void findLeftmost(RedBlackTreeNode<K,V> node) { while (node.left != EMPTY) { path = path.cons(node); node = node.left; } this.next = node; } public boolean hasNext() { return next != null; } public RedBlackTreeNode<K,V> next() { if (next == null) { throw new NoSuchElementException(); } else { RedBlackTreeNode<K,V> result = next; if (next.right != EMPTY) { findLeftmost(next.right); } else { // no elements to the right, so climb up the tree if (path == Cons.EMPTY) { next = null; } else { next = path.first; path = path.rest; } } return result; } } public void remove() { throw new UnsupportedOperationException(); } } }