/* * Copyright 2017 Google Inc. * * Licensed 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 com.google.firebase.database.collection; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Map; public abstract class ImmutableSortedMap<K, V> implements Iterable<Map.Entry<K, V>> { public abstract boolean containsKey(K key); public abstract V get(K key); public abstract ImmutableSortedMap<K, V> remove(K key); public abstract ImmutableSortedMap<K, V> insert(K key, V value); public abstract K getMinKey(); public abstract K getMaxKey(); public abstract int size(); public abstract boolean isEmpty(); public abstract void inOrderTraversal(LLRBNode.NodeVisitor<K, V> visitor); public abstract Iterator<Map.Entry<K, V>> iterator(); public abstract Iterator<Map.Entry<K, V>> iteratorFrom(K key); public abstract Iterator<Map.Entry<K, V>> reverseIteratorFrom(K key); public abstract Iterator<Map.Entry<K, V>> reverseIterator(); public abstract K getPredecessorKey(K key); public abstract K getSuccessorKey(K key); public abstract Comparator<K> getComparator(); @Override @SuppressWarnings("unchecked") public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof ImmutableSortedMap)) { return false; } ImmutableSortedMap<K, V> that = (ImmutableSortedMap) o; if (!this.getComparator().equals(that.getComparator())) { return false; } if (this.size() != that.size()) { return false; } Iterator<Map.Entry<K, V>> thisIterator = this.iterator(); Iterator<Map.Entry<K, V>> thatIterator = that.iterator(); while (thisIterator.hasNext()) { if (!thisIterator.next().equals(thatIterator.next())) { return false; } } return true; } @Override public int hashCode() { int result = this.getComparator().hashCode(); for (Map.Entry<K, V> entry : this) { result = 31 * result + entry.hashCode(); } return result; } public String toString() { StringBuilder b = new StringBuilder(); b.append(this.getClass().getSimpleName()); b.append("{"); boolean first = true; for (Map.Entry<K, V> entry : this) { if (first) { first = false; } else { b.append(", "); } b.append("("); b.append(entry.getKey()); b.append("=>"); b.append(entry.getValue()); b.append(")"); } b.append("};"); return b.toString(); } public static class Builder { /** * The size threshold where we use a tree backed sorted map instead of an array backed sorted * map. This is a more or less arbitrary chosen value, that was chosen to be large enough to fit * most of object kind of Database data, but small enough to not notice degradation in * performance for inserting and lookups. Feel free to empirically determine this constant, but * don't expect much gain in real world performance. */ static final int ARRAY_TO_RB_TREE_SIZE_THRESHOLD = 25; private static final KeyTranslator IDENTITY_TRANSLATOR = new KeyTranslator() { @Override public Object translate(Object key) { return key; } }; public static <K, V> ImmutableSortedMap<K, V> emptyMap(Comparator<K> comparator) { return new ArraySortedMap<>(comparator); } @SuppressWarnings("unchecked") public static <A> KeyTranslator<A, A> identityTranslator() { return IDENTITY_TRANSLATOR; } public static <A, B> ImmutableSortedMap<A, B> fromMap( Map<A, B> values, Comparator<A> comparator) { if (values.size() < ARRAY_TO_RB_TREE_SIZE_THRESHOLD) { return ArraySortedMap.fromMap(values, comparator); } else { return RBTreeSortedMap.fromMap(values, comparator); } } public static <A, B, C> ImmutableSortedMap<A, C> buildFrom( List<A> keys, Map<B, C> values, ImmutableSortedMap.Builder.KeyTranslator<A, B> translator, Comparator<A> comparator) { if (keys.size() < ARRAY_TO_RB_TREE_SIZE_THRESHOLD) { return ArraySortedMap.buildFrom(keys, values, translator, comparator); } else { return RBTreeSortedMap.buildFrom(keys, values, translator, comparator); } } public interface KeyTranslator<C, D> { D translate(C key); } } }