diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TAbstractMap.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TAbstractMap.java index 37cf36506..21b17bd2f 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TAbstractMap.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TAbstractMap.java @@ -16,11 +16,9 @@ package org.teavm.classlib.java.util; import org.teavm.classlib.java.io.TSerializable; -import org.teavm.classlib.java.lang.ObjectNativeGenerator; import org.teavm.classlib.java.lang.TCloneNotSupportedException; import org.teavm.classlib.java.lang.TObject; import org.teavm.classlib.java.lang.TUnsupportedOperationException; -import org.teavm.dependency.PluggableDependency; /** * @@ -266,7 +264,6 @@ public abstract class TAbstractMap extends TObject implements TMap { } @Override - @PluggableDependency(ObjectNativeGenerator.class) protected Object clone() throws TCloneNotSupportedException { TAbstractMap copy = (TAbstractMap)super.clone(); copy.cachedKeySet = null; diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TTreeMap.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TTreeMap.java index acf784d67..068914504 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TTreeMap.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TTreeMap.java @@ -13,58 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package org.teavm.classlib.java.util; import org.teavm.classlib.java.io.TSerializable; import org.teavm.classlib.java.lang.*; public class TTreeMap extends TAbstractMap implements TCloneable, TSerializable, TSortedMap { - class TreeNode implements Entry { - K key; - V value; - TreeNode left; - TreeNode right; + static class TreeNode extends SimpleEntry { + TreeNode left; + TreeNode right; int height = 1; int size = 1; public TreeNode(K key) { - this.key = key; + super(key, null); } - @Override - public K getKey() { - return key; - } - - @Override - public V getValue() { - return value; - } - - @Override - public V setValue(V value) { - V old = this.value; - this.value = value; - return old; - } - - public TreeNode balance() { + public TreeNode balance() { int factor = factor(); if (factor == 2) { if (right.factor() < 0) { @@ -85,8 +50,8 @@ public class TTreeMap extends TAbstractMap implements TCloneable, TS return (right != null ? right.height : 0) - (left != null ? left.height : 0); } - public TreeNode rotateRight() { - TreeNode left = this.left; + public TreeNode rotateRight() { + TreeNode left = this.left; this.left = left.right; left.right = this; fix(); @@ -94,8 +59,8 @@ public class TTreeMap extends TAbstractMap implements TCloneable, TS return left; } - public TreeNode rotateLeft() { - TreeNode right = this.right; + public TreeNode rotateLeft() { + TreeNode right = this.right; this.right = right.left; right.left = this; fix(); @@ -115,10 +80,15 @@ public class TTreeMap extends TAbstractMap implements TCloneable, TS } } - TreeNode root; + TreeNode root; private TComparator comparator; private TComparator originalComparator; private int modCount = 0; + private EntrySet cachedEntrySet; + + public TTreeMap() { + this((TComparator)null); + } public TTreeMap(TComparator comparator) { this.originalComparator = comparator; @@ -133,31 +103,70 @@ public class TTreeMap extends TAbstractMap implements TCloneable, TS this.comparator = comparator; } + public TTreeMap(TMap m) { + this((TComparator)null); + @SuppressWarnings("unchecked") + Entry[] entries = (Entry[])new Entry[m.size()]; + entries = m.entrySet().toArray(entries); + TArrays.sort(entries, new TComparator>() { + @Override public int compare(Entry o1, Entry o2) { + return comparator.compare(o1.getKey(), o2.getKey()); + } + }); + fillMap(entries); + } + + public TTreeMap(TSortedMap m) { + this(m.comparator()); + @SuppressWarnings("unchecked") + Entry[] entries = (Entry[])new Entry[m.size()]; + entries = m.entrySet().toArray(entries); + fillMap(entries); + } + + private void fillMap(Entry[] entries) { + root = createNode(entries, 0, entries.length - 1); + } + + private TreeNode createNode(Entry[] entries, int l, int u) { + if (l > u) { + return null; + } + int mid = (l + u) / 2; + Entry entry = entries[mid]; + TreeNode node = new TreeNode(entry.getKey()); + node.setValue(entry.getValue()); + node.left = createNode(entries, l, mid - 1); + node.right = createNode(entries, mid + 1, u); + node.fix(); + return node; + } + @Override public V get(Object key) { - TreeNode node = findNode(key); - return node != null ? node.value : null; + TreeNode node = findExact(key); + return node != null ? node.getValue() : null; } @Override public V put(K key, V value) { root = getOrCreateNode(root, key); - TreeNode node = findNode(key); - V old = node.value; - node.value = value; + TreeNode node = findExact(key); + V old = node.setValue(value); + node.setValue(value); modCount++; return old; } @Override public V remove(Object key) { - TreeNode node = findNode(key); + TreeNode node = findExact(key); if (node == null) { return null; } root = deleteNode(root, key); modCount++; - return node.value; + return node.getValue(); } @Override @@ -173,14 +182,14 @@ public class TTreeMap extends TAbstractMap implements TCloneable, TS @Override public boolean containsKey(Object key) { - return findNode(key) != null; + return findExact(key) != null; } - TreeNode findNode(Object key) { - TreeNode node = root; + TreeNode findExact(Object key) { + TreeNode node = root; while (node != null) { @SuppressWarnings("unchecked") - int cmp = comparator.compare((K)key, node.key); + int cmp = comparator.compare((K)key, node.getKey()); if (cmp == 0) { return node; } else if (cmp < 0) { @@ -192,11 +201,136 @@ public class TTreeMap extends TAbstractMap implements TCloneable, TS return null; } - private TreeNode getOrCreateNode(TreeNode root, K key) { - if (root == null) { - return new TreeNode(key); + TreeNode findExactOrNext(Object key) { + TreeNode node = root; + TreeNode lastLeftTurn = null; + while (node != null) { + @SuppressWarnings("unchecked") + int cmp = comparator.compare((K)key, node.getKey()); + if (cmp == 0) { + return node; + } else if (cmp < 0) { + lastLeftTurn = node; + node = node.left; + } else { + node = node.right; + } } - int cmp = comparator.compare(key, root.key); + return lastLeftTurn; + } + + TreeNode[] pathToExactOrNext(Object key) { + @SuppressWarnings("unchecked") + TreeNode[] path = (TreeNode[])new TreeNode[height()]; + int depth = 0; + TreeNode node = root; + while (node != null) { + @SuppressWarnings("unchecked") + int cmp = comparator.compare((K)key, node.getKey()); + if (cmp == 0) { + path[depth++] = node; + break; + } else if (cmp < 0) { + path[depth++] = node; + node = node.left; + } else { + node = node.right; + } + } + return TArrays.copyOf(path, depth); + } + + TreeNode findNext(Object key) { + TreeNode node = root; + TreeNode lastLeftTurn = null; + while (node != null) { + @SuppressWarnings("unchecked") + int cmp = comparator.compare((K)key, node.getKey()); + if (cmp == 0) { + break; + } else if (cmp < 0) { + lastLeftTurn = node; + node = node.left; + } else { + node = node.right; + } + } + return lastLeftTurn; + } + + TreeNode[] pathToNext(Object key) { + @SuppressWarnings("unchecked") + TreeNode[] path = (TreeNode[])new TreeNode[height()]; + int depth = 0; + TreeNode node = root; + while (node != null) { + @SuppressWarnings("unchecked") + int cmp = comparator.compare((K)key, node.getKey()); + if (cmp == 0) { + break; + } else if (cmp < 0) { + path[depth++] = node; + node = node.left; + } else { + node = node.right; + } + } + return TArrays.copyOf(path, depth); + } + + TreeNode findExactOrPrev(Object key) { + TreeNode node = root; + TreeNode lastRightTurn = null; + while (node != null) { + @SuppressWarnings("unchecked") + int cmp = comparator.compare((K)key, node.getKey()); + if (cmp == 0) { + return node; + } else if (cmp > 0) { + lastRightTurn = node; + node = node.right; + } else { + node = node.left; + } + } + return lastRightTurn; + } + + TreeNode[] pathToFirst() { + @SuppressWarnings("unchecked") + TreeNode[] path = (TreeNode[])new TreeNode[height()]; + int depth = 0; + TreeNode node = root; + while (node != null) { + path[depth++] = node; + node = node.left; + } + return TArrays.copyOf(path, depth); + } + + TreeNode findPrev(Object key) { + TreeNode node = root; + TreeNode lastRightTurn = null; + while (node != null) { + @SuppressWarnings("unchecked") + int cmp = comparator.compare((K)key, node.getKey()); + if (cmp == 0) { + return node; + } else if (cmp > 0) { + lastRightTurn = node; + node = node.right; + } else { + node = node.left; + } + } + return lastRightTurn; + } + + private TreeNode getOrCreateNode(TreeNode root, K key) { + if (root == null) { + return new TreeNode<>(key); + } + int cmp = comparator.compare(key, root.getKey()); if (cmp == 0) { return root; } else if (cmp < 0) { @@ -204,15 +338,16 @@ public class TTreeMap extends TAbstractMap implements TCloneable, TS } else { root.right = getOrCreateNode(root.right, key); } + root.fix(); return root.balance(); } - private TreeNode deleteNode(TreeNode root, Object key) { + private TreeNode deleteNode(TreeNode root, Object key) { if (root == null) { return null; } @SuppressWarnings("unchecked") - int cmp = comparator.compare((K)key, root.key); + int cmp = comparator.compare((K)key, root.getKey()); if (cmp < 0) { root.left = deleteNode(root.left, key); } else if (cmp > 0) { @@ -220,19 +355,19 @@ public class TTreeMap extends TAbstractMap implements TCloneable, TS } else if (root.right == null) { return root.left; } else { - TreeNode left = root.left; - TreeNode right = root.right; - TreeNode min = right; - Object[] pathToMin = new Object[right.height]; + TreeNode left = root.left; + TreeNode right = root.right; + TreeNode min = right; + @SuppressWarnings("unchecked") + TreeNode[] pathToMin = (TreeNode[])new TreeNode[right.height]; int minDepth = 0; while (min.left != null) { pathToMin[minDepth++] = min; min = min.left; } right = min.right; - while (minDepth >= 0) { - @SuppressWarnings("unchecked") - TreeNode node = (TreeNode)pathToMin[--minDepth]; + while (minDepth > 0) { + TreeNode node = pathToMin[--minDepth]; node.left = right; right = node; node.balance(); @@ -246,7 +381,10 @@ public class TTreeMap extends TAbstractMap implements TCloneable, TS @Override public TSet> entrySet() { - return new EntrySet(new Object[0], null); + if (cachedEntrySet == null) { + cachedEntrySet = new EntrySet<>(this, null, true, false, null, true, false); + } + return cachedEntrySet; } @Override @@ -256,49 +394,58 @@ public class TTreeMap extends TAbstractMap implements TCloneable, TS @Override public TSortedMap subMap(K fromKey, K toKey) { - return null; + if (comparator.compare(fromKey, toKey) > 0) { + throw new TIllegalArgumentException(); + } + return new SubMap<>(this, fromKey, true, true, toKey, false, true); } @Override public TSortedMap headMap(K toKey) { - return null; + return new SubMap<>(this, null, true, false, toKey, false, true); } @Override public TSortedMap tailMap(K fromKey) { - return null; + return new SubMap<>(this, fromKey, false, true, null, false, false); } @Override public K firstKey() { - return firstEntry().key; + TreeNode node = firstNode(); + if (node == null) { + throw new TNoSuchElementException(); + } + return node.getKey(); } @Override public K lastKey() { - return lastEntry().key; - } - - private TreeNode firstEntry() { - if (isEmpty()) { + TreeNode node = lastNode(); + if (node == null) { throw new TNoSuchElementException(); } - TreeNode node = root; - while (node.left != null) { - node = node.left; - } - return node; + return node.getKey(); } - private TreeNode lastEntry() { - if (isEmpty()) { - throw new TNoSuchElementException(); - } - TreeNode node = root; - while (node.left != null) { + private TreeNode firstNode() { + TreeNode node = root; + TreeNode prev = null; + while (node != null) { + prev = node; node = node.left; } - return node; + return prev; + } + + private TreeNode lastNode() { + TreeNode node = root; + TreeNode prev = null; + while (node != null) { + prev = node; + node = node.right; + } + return prev; } @Override @@ -306,28 +453,50 @@ public class TTreeMap extends TAbstractMap implements TCloneable, TS return root != null ? root.size : 0; } - private class EntrySet extends TAbstractSet> { - private Object[] path; - private TreeNode from; - private TreeNode to; + int height() { + return root != null ? root.height : 0; + } - @SuppressWarnings("unchecked") - public EntrySet(Object[] path, TreeNode to) { - this.path = path; - this.from = this.path.length > 0 ? (TreeNode)this.path[this.path.length - 1] : null; + @Override + public Object clone() { + TTreeMap copy = (TTreeMap)super.clone(); + copy.cachedEntrySet = null; + return copy; + } + + private static class EntrySet extends TAbstractSet> { + private TTreeMap owner; + private K from; + private boolean fromIncluded; + private boolean fromChecked; + private K to; + private boolean toIncluded; + private boolean toChecked; + + public EntrySet(TTreeMap owner, K from, boolean fromIncluded, boolean fromChecked, + K to, boolean toIncluded, boolean toChecked) { + this.owner = owner; + this.from = from; + this.fromIncluded = fromIncluded; + this.fromChecked = fromChecked; this.to = to; + this.toIncluded = toIncluded; + this.toChecked = toChecked; } @Override public int size() { - int size = TTreeMap.this.size(); - if (from != null && from.left != null) { - size -= from.left.size; + int size = owner.size(); + if (fromChecked) { + TreeNode node = fromIncluded ? owner.findPrev(from) : owner.findExactOrPrev(from); + if (node != null) { + size -= node.size; + } } - if (to != null) { - size -= 1; - if (to.right != null) { - size -= to.right.size; + if (toChecked) { + TreeNode node = toIncluded ? owner.findNext(to) : owner.findExactOrNext(to); + if (node != null) { + size -= node.size; } } return size; @@ -335,50 +504,283 @@ public class TTreeMap extends TAbstractMap implements TCloneable, TS @Override public TIterator> iterator() { - return null; + TreeNode[] fromPath; + if (fromChecked) { + fromPath = fromIncluded ? owner.pathToExactOrNext(from) : owner.pathToNext(from); + } else { + fromPath = owner.pathToFirst(); + } + TreeNode toEntry; + if (toChecked) { + toEntry = toIncluded ? owner.findExactOrPrev(to) : owner.findPrev(to); + } else { + toEntry = owner.lastNode(); + } + return new EntryIterator<>(owner, fromPath, toEntry); + } + + @Override + public boolean contains(Object o) { + if (!(o instanceof Entry)) { + return false; + } + Entry entry = (Entry)o; + Object key = entry.getKey(); + if (from != null) { + @SuppressWarnings("unchecked") + int cmp = owner.comparator.compare((K)key, from); + if (fromIncluded ? cmp < 0 : cmp <= 0) { + return false; + } + } + if (to != null) { + @SuppressWarnings("unchecked") + int cmp = owner.comparator.compare((K)key, to); + if (toIncluded ? cmp > 0 : cmp >= 0) { + return false; + } + } + TreeNode node = owner.findExact(key); + return node != null && node.equals(o); + } + + @Override + public boolean isEmpty() { + return from == to; } } - private class EntryIterator implements TIterator> { - private int modCount = TTreeMap.this.modCount; - private Object[] path = new Object[root != null ? root.height : 0]; - private TreeNode last; - private TreeNode to; - private int pathLength; - public EntryIterator(Object[] path, TreeNode to) { - TreeNode node = root; - while (node != null) { - path[pathLength++] = node; - node = node.left; - } + private static class EntryIterator implements TIterator> { + private int modCount; + private TTreeMap owner; + private TreeNode[] path; + private TreeNode last; + private TreeNode to; + private int depth; + + public EntryIterator(TTreeMap owner, TreeNode[] path, TreeNode to) { + this.owner = owner; + modCount = owner.modCount; + this.path = TArrays.copyOf(path, owner.root.height); + depth = path.length; + this.to = to; } - @Override public boolean hasNext() { - return pathLength > 0; + + @Override + public boolean hasNext() { + return depth > 0; } - @SuppressWarnings("unchecked") @Override public Entry next() { - if (modCount != TTreeMap.this.modCount) { + + @Override + public Entry next() { + if (modCount != owner.modCount) { throw new TConcurrentModificationException(); } - if (pathLength == 0) { + if (depth == 0) { throw new TNoSuchElementException(); } - TreeNode node = (TreeNode)path[--pathLength]; + TreeNode node = path[--depth]; last = node; if (node.right != null) { node = node.right; while (node != null) { - path[pathLength++] = node; + path[depth++] = node; node = node.left; } } + if (last == to) { + depth = 0; + } return last; } - @Override public void remove() { + + @Override + public void remove() { + if (modCount != owner.modCount) { + throw new TConcurrentModificationException(); + } if (last == null) { throw new TNoSuchElementException(); } - deleteNode(root, last); + owner.root = owner.deleteNode(owner.root, last.getKey()); + modCount = ++owner.modCount; last = null; } } + + private static class SubMap extends TAbstractMap implements TSortedMap { + private TTreeMap owner; + private K from; + private boolean fromIncluded; + private boolean fromChecked; + private K to; + private boolean toIncluded; + private boolean toChecked; + private EntrySet entrySetCache; + + public SubMap(TTreeMap owner, K from, boolean fromIncluded, boolean fromChecked, + K to, boolean toIncluded, boolean toChecked) { + this.owner = owner; + this.from = from; + this.fromIncluded = fromIncluded; + this.fromChecked = fromChecked; + this.to = to; + this.toIncluded = toIncluded; + this.toChecked = toChecked; + } + + @Override + public TSet> entrySet() { + if (entrySetCache == null) { + entrySetCache = new EntrySet<>(owner, from, fromIncluded, fromChecked, to, toIncluded, toChecked); + } + return entrySetCache; + } + + @Override + public TComparator comparator() { + return owner.originalComparator; + } + + private void checkKey(K key) { + if (fromChecked) { + int cmp = owner.comparator.compare(key, from); + if (fromIncluded ? cmp < 0 : cmp <= 0) { + throw new TIllegalArgumentException(); + } + } + if (toChecked) { + int cmp = owner.comparator.compare(key, to); + if (fromIncluded ? cmp > 0 : cmp >= 0) { + throw new TIllegalArgumentException(); + } + } + } + + @SuppressWarnings("unchecked") + @Override + public V get(Object key) { + checkKey((K)key); + return owner.get(key); + } + + @SuppressWarnings("unchecked") + @Override + public V remove(Object key) { + checkKey((K)key); + return owner.remove(key); + } + + @Override + public V put(K key, V value) { + checkKey(key); + return owner.put(key, value); + } + + @Override + public int size() { + int size = owner.size(); + if (fromChecked) { + TreeNode node = fromIncluded ? owner.findPrev(from) : owner.findExactOrPrev(from); + if (node != null) { + size -= node.size; + } + } + if (toChecked) { + TreeNode node = toIncluded ? owner.findNext(to) : owner.findExactOrNext(to); + if (node != null) { + size -= node.size; + } + } + return size; + } + + @Override + public boolean containsKey(Object key) { + if (fromChecked) { + @SuppressWarnings("unchecked") + int cmp = owner.comparator.compare((K)key, from); + if (fromIncluded ? cmp < 0 : cmp <= 0) { + return false; + } + } + if (toChecked) { + @SuppressWarnings("unchecked") + int cmp = owner.comparator.compare((K)key, to); + if (fromIncluded ? cmp > 0 : cmp >= 0) { + return false; + } + } + return owner.containsKey(key); + } + + @Override + public TSortedMap subMap(K fromKey, K toKey) { + checkKey(fromKey); + checkKey(toKey); + return new SubMap<>(owner, fromKey, true, true, toKey, false, true); + } + + @Override + public TSortedMap headMap(K toKey) { + checkKey(toKey); + return new SubMap<>(owner, from, fromIncluded, fromChecked, toKey, false, true); + } + + @Override + public TSortedMap tailMap(K fromKey) { + checkKey(fromKey); + return new SubMap<>(owner, fromKey, true, true, to, toIncluded, toChecked); + } + + @Override + public K firstKey() { + TreeNode node = firstNode(); + if (node == null) { + throw new TNoSuchElementException(); + } + return node.getKey(); + } + + @Override + public K lastKey() { + TreeNode node = lastNode(); + if (node == null) { + throw new TNoSuchElementException(); + } + return node.getKey(); + } + + private TreeNode firstNode() { + TreeNode node; + if (fromChecked) { + node = fromIncluded ? owner.findExactOrNext(from) : owner.findNext(from); + } else { + node = owner.firstNode(); + } + if (toChecked) { + int cmp = owner.comparator.compare(node.getKey(), to); + if (toIncluded ? cmp > 0 : cmp >= 0) { + return null; + } + } + return node; + } + + private TreeNode lastNode() { + TreeNode node; + if (toChecked) { + node = toIncluded ? owner.findExactOrPrev(to) : owner.findPrev(to); + } else { + node = owner.lastNode(); + } + if (fromChecked) { + int cmp = owner.comparator.compare(node.getKey(), from); + if (fromIncluded ? cmp < 0 : cmp <= 0) { + return null; + } + } + return node; + } + } } diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/TreeMapTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/util/TreeMapTest.java index 793225857..f8979caa1 100644 --- a/teavm-classlib/src/test/java/org/teavm/classlib/java/util/TreeMapTest.java +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/util/TreeMapTest.java @@ -441,22 +441,16 @@ public class TreeMapTest { Map subMap = map.subMap("", "test"); assertEquals(3, subMap.size()); - Set> entrySet = subMap.entrySet(); - Iterator> iter = entrySet.iterator(); int size = 0; - while (iter.hasNext()) { - Map.Entry entry = iter.next(); + for (Map.Entry entry : subMap.entrySet()) { assertTrue(map.containsKey(entry.getKey())); assertTrue(map.containsValue(entry.getValue())); size++; } assertEquals(map.size(), size); - Set keySet = subMap.keySet(); - Iterator keyIter = keySet.iterator(); size = 0; - while (keyIter.hasNext()) { - String key = keyIter.next(); + for (String key : subMap.keySet()) { assertTrue(map.containsKey(key)); size++; }