Skip to content

Commit 76980ea

Browse files
committed
SONARJAVA-1389 SE: using persistent data structures
1 parent d49c2be commit 76980ea

9 files changed

Lines changed: 782 additions & 14 deletions

File tree

Lines changed: 388 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,388 @@
1+
/*
2+
* SonarQube Java
3+
* Copyright (C) 2012 SonarSource
4+
* sonarqube@googlegroups.com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public
17+
* License along with this program; if not, write to the Free Software
18+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
19+
*/
20+
package org.sonar.java.collections;
21+
22+
import com.google.common.base.Preconditions;
23+
24+
import javax.annotation.Nullable;
25+
26+
import java.util.Comparator;
27+
import java.util.Objects;
28+
29+
/**
30+
* AVL Tree.
31+
*
32+
* https://en.wikipedia.org/wiki/AVL_tree
33+
*
34+
* @author Evgeny Mandrikov
35+
*/
36+
public abstract class AVLTree<K, V> implements PMap<K, V>, PSet<K> {
37+
38+
/**
39+
* @return empty tree
40+
*/
41+
@SuppressWarnings("unchecked")
42+
public static <K, V> AVLTree<K, V> create() {
43+
return EMPTY;
44+
}
45+
46+
@SuppressWarnings("unchecked")
47+
@Override
48+
public AVLTree<K, V> add(K e) {
49+
return put(e, e, this);
50+
}
51+
52+
@Override
53+
public boolean contains(K k) {
54+
return get(k) != null;
55+
}
56+
57+
@SuppressWarnings("unchecked")
58+
@Override
59+
public AVLTree<K, V> put(K key, V value) {
60+
Preconditions.checkNotNull(key);
61+
Preconditions.checkNotNull(value);
62+
return put(key, value, this);
63+
}
64+
65+
@SuppressWarnings("unchecked")
66+
@Override
67+
public AVLTree<K, V> remove(K key) {
68+
Preconditions.checkNotNull(key);
69+
return remove(key, this);
70+
}
71+
72+
@SuppressWarnings("unchecked")
73+
@Nullable
74+
@Override
75+
public V get(K key) {
76+
Preconditions.checkNotNull(key);
77+
AVLTree t = this;
78+
while (!t.isEmpty()) {
79+
int c = KEY_COMPARATOR.compare(key, t.key());
80+
if (c == 0) {
81+
return (V) t.value();
82+
} else if (c < 0) {
83+
t = t.left();
84+
} else {
85+
t = t.right();
86+
}
87+
}
88+
return null;
89+
}
90+
91+
@Override
92+
public void forEach(PSet.Consumer<K> action) {
93+
forEach(this, action);
94+
}
95+
96+
@SuppressWarnings("unchecked")
97+
private void forEach(AVLTree t, PSet.Consumer<K> action) {
98+
if (t.isEmpty()) {
99+
return;
100+
}
101+
action.accept((K) t.key());
102+
forEach(t.left(), action);
103+
forEach(t.right(), action);
104+
}
105+
106+
@Override
107+
public void forEach(PMap.Consumer<K, V> consumer) {
108+
forEach(this, consumer);
109+
}
110+
111+
@SuppressWarnings("unchecked")
112+
private void forEach(AVLTree t, PMap.Consumer<K, V> action) {
113+
if (t.isEmpty()) {
114+
return;
115+
}
116+
action.accept((K) t.key(), (V) t.value());
117+
forEach(t.left(), action);
118+
forEach(t.right(), action);
119+
}
120+
121+
public interface Visitor<K, V> {
122+
void visit(K key, V value);
123+
}
124+
125+
protected abstract AVLTree left();
126+
127+
protected abstract AVLTree right();
128+
129+
protected abstract Object key();
130+
131+
protected abstract Object value();
132+
133+
protected abstract int height();
134+
135+
private static AVLTree put(Object key, Object value, AVLTree t) {
136+
if (t.isEmpty()) {
137+
return createNode(t, key, value, t);
138+
}
139+
int result = KEY_COMPARATOR.compare(key, t.key());
140+
if (result == 0) {
141+
if (value.equals(t.value())) {
142+
return t;
143+
}
144+
return createNode(t.left(), key, value, t.right());
145+
} else if (result < 0) {
146+
AVLTree left = put(key, value, t.left());
147+
if (left == t.left()) {
148+
return t;
149+
}
150+
return balance(left, t.key(), t.value(), t.right());
151+
} else {
152+
AVLTree right = put(key, value, t.right());
153+
if (right == t.right()) {
154+
return t;
155+
}
156+
return balance(t.left(), t.key(), t.value(), right);
157+
}
158+
}
159+
160+
private static AVLTree remove(Object key, AVLTree t) {
161+
if (t.isEmpty()) {
162+
return t;
163+
}
164+
int result = KEY_COMPARATOR.compare(key, t.key());
165+
if (result == 0) {
166+
return combineTrees(t.left(), t.right());
167+
} else if (result < 0) {
168+
AVLTree left = remove(key, t.left());
169+
if (left == t.left()) {
170+
return t;
171+
}
172+
return balance(left, t.key(), t.value(), t.right());
173+
} else {
174+
AVLTree right = remove(key, t.right());
175+
if (right == t.right()) {
176+
return t;
177+
}
178+
return balance(t.left(), t.key(), t.value(), right);
179+
}
180+
}
181+
182+
private static AVLTree combineTrees(AVLTree l, AVLTree r) {
183+
if (l.isEmpty()) {
184+
return r;
185+
}
186+
if (r.isEmpty()) {
187+
return l;
188+
}
189+
NodeRef oldNode = new NodeRef();
190+
AVLTree newRight = removeMinBinding(r, oldNode);
191+
return balance(l, oldNode.node.key(), oldNode.node.value(), newRight);
192+
}
193+
194+
private static class NodeRef {
195+
AVLTree node;
196+
}
197+
198+
private static AVLTree removeMinBinding(AVLTree t, NodeRef noderemoved) {
199+
assert !t.isEmpty();
200+
if (t.left().isEmpty()) {
201+
noderemoved.node = t;
202+
return t.right();
203+
}
204+
return balance(removeMinBinding(t.left(), noderemoved), t.key(), t.value(), t.right());
205+
}
206+
207+
private static AVLTree balance(AVLTree l, Object key, Object value, AVLTree r) {
208+
if (l.height() > r.height() + 2) {
209+
assert !l.isEmpty();
210+
AVLTree ll = l.left();
211+
AVLTree lr = l.right();
212+
if (ll.height() >= lr.height()) {
213+
return createNode(ll, l, createNode(lr, key, value, r));
214+
}
215+
assert !lr.isEmpty();
216+
AVLTree lrl = lr.left();
217+
AVLTree lrr = lr.right();
218+
return createNode(createNode(ll, l, lrl), lr, createNode(lrr, key, value, r));
219+
}
220+
if (r.height() > l.height() + 2) {
221+
assert !r.isEmpty();
222+
AVLTree rl = r.left();
223+
AVLTree rr = r.right();
224+
if (rr.height() >= rl.height()) {
225+
return createNode(createNode(l, key, value, rl), r, rr);
226+
}
227+
assert !rl.isEmpty();
228+
AVLTree rll = rl.left();
229+
AVLTree rlr = rl.right();
230+
return createNode(createNode(l, key, value, rll), rl, createNode(rlr, r, rr));
231+
}
232+
return createNode(l, key, value, r);
233+
}
234+
235+
private static AVLTree createNode(AVLTree newLeft, AVLTree oldTree, AVLTree newRight) {
236+
return createNode(newLeft, oldTree.key(), oldTree.value(), newRight);
237+
}
238+
239+
private static AVLTree createNode(AVLTree l, Object key, Object value, AVLTree r) {
240+
return new Node(l, r, key, value, incrementHeight(l, r));
241+
}
242+
243+
private static int incrementHeight(AVLTree l, AVLTree r) {
244+
return (l.height() > r.height() ? l.height() : r.height()) + 1;
245+
}
246+
247+
private static class Node extends AVLTree {
248+
private final AVLTree left;
249+
private final AVLTree right;
250+
private final int height;
251+
252+
private final Object key;
253+
private final Object value;
254+
private int hashCode;
255+
256+
public Node(AVLTree left, AVLTree right, Object key, Object value, int height) {
257+
this.left = left;
258+
this.right = right;
259+
this.key = key;
260+
this.value = value;
261+
this.height = height;
262+
}
263+
264+
@Override
265+
protected AVLTree left() {
266+
return left;
267+
}
268+
269+
@Override
270+
protected AVLTree right() {
271+
return right;
272+
}
273+
274+
@Override
275+
protected Object key() {
276+
return key;
277+
}
278+
279+
@Override
280+
protected Object value() {
281+
return value;
282+
}
283+
284+
@Override
285+
protected int height() {
286+
return height;
287+
}
288+
289+
@Override
290+
public boolean isEmpty() {
291+
return false;
292+
}
293+
294+
@Override
295+
public int hashCode() {
296+
if (hashCode == 0) {
297+
int result = key.hashCode();
298+
result = result * 31 + left.hashCode();
299+
result = result * 31 + right.hashCode();
300+
result = result * 31 + value.hashCode();
301+
hashCode = result;
302+
}
303+
return hashCode;
304+
}
305+
306+
@Override
307+
public boolean equals(Object obj) {
308+
if (this == obj) {
309+
return true;
310+
}
311+
if (obj instanceof Node) {
312+
Node other = (Node) obj;
313+
return Objects.equals(this.key, other.key)
314+
&& Objects.equals(this.value, other.value)
315+
&& Objects.equals(this.left, other.left)
316+
&& Objects.equals(this.right, other.right);
317+
}
318+
return false;
319+
}
320+
321+
@Override
322+
public String toString() {
323+
return " " + key + "->" + value + left.toString() + right.toString();
324+
}
325+
}
326+
327+
private static final AVLTree EMPTY = new AVLTree() {
328+
@Override
329+
protected AVLTree left() {
330+
throw new UnsupportedOperationException();
331+
}
332+
333+
@Override
334+
protected AVLTree right() {
335+
throw new UnsupportedOperationException();
336+
}
337+
338+
@Override
339+
protected Object key() {
340+
throw new UnsupportedOperationException();
341+
}
342+
343+
@Override
344+
protected Object value() {
345+
throw new UnsupportedOperationException();
346+
}
347+
348+
@Override
349+
protected int height() {
350+
return 0;
351+
}
352+
353+
@Override
354+
public boolean isEmpty() {
355+
return true;
356+
}
357+
358+
@Override
359+
public boolean equals(Object obj) {
360+
return (obj instanceof AVLTree) && ((AVLTree) obj).isEmpty();
361+
}
362+
363+
@Override
364+
public int hashCode() {
365+
return 0;
366+
}
367+
368+
@Override
369+
public String toString() {
370+
return "";
371+
}
372+
};
373+
374+
/**
375+
* Not total ordering, but stable.
376+
*/
377+
private static final Comparator KEY_COMPARATOR = new Comparator() {
378+
@Override
379+
public int compare(Object o1, Object o2) {
380+
int h1 = o1.hashCode();
381+
int h2 = o2.hashCode();
382+
if (h1 == h2) {
383+
return o1.equals(o2) ? 0 : 1;
384+
}
385+
return h1 - h2;
386+
}
387+
};
388+
}

0 commit comments

Comments
 (0)