All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.firebase.database.collection.ArraySortedMap Maven / Gradle / Ivy

Go to download

This is the official Firebase Admin Java SDK. Build extraordinary native JVM apps in minutes with Firebase. The Firebase platform can power your app’s backend, user authentication, static hosting, and more.

There is a newer version: 9.2.0
Show newest version
/*
 * 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.AbstractMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * This is an array backed implementation of ImmutableSortedMap. It uses arrays and linear lookups
 * to achieve good memory efficiency while maintaining good performance for small collections. To
 * avoid degrading performance with increasing collection size it will automatically convert to a
 * RBTreeSortedMap after an insert call above a certain threshold.
 */
public class ArraySortedMap extends ImmutableSortedMap {

  private final K[] keys;
  private final V[] values;
  private final Comparator comparator;

  @SuppressWarnings("unchecked")
  public ArraySortedMap(Comparator comparator) {
    this.keys = (K[]) new Object[0];
    this.values = (V[]) new Object[0];
    this.comparator = comparator;
  }

  @SuppressWarnings("unchecked")
  private ArraySortedMap(Comparator comparator, K[] keys, V[] values) {
    this.keys = keys;
    this.values = values;
    this.comparator = comparator;
  }

  @SuppressWarnings("unchecked")
  public static  ArraySortedMap buildFrom(
      List keys,
      Map values,
      Builder.KeyTranslator translator,
      Comparator comparator) {
    Collections.sort(keys, comparator);
    int size = keys.size();
    A[] keyArray = (A[]) new Object[size];
    C[] valueArray = (C[]) new Object[size];
    int pos = 0;
    for (A k : keys) {
      keyArray[pos] = k;
      C value = values.get(translator.translate(k));
      valueArray[pos] = value;
      pos++;
    }
    return new ArraySortedMap<>(comparator, keyArray, valueArray);
  }

  public static  ArraySortedMap fromMap(Map map, Comparator comparator) {
    return buildFrom(
        new ArrayList<>(map.keySet()), map, Builder.identityTranslator(), comparator);
  }

  @SuppressWarnings("unchecked")
  private static  T[] removeFromArray(T[] arr, int pos) {
    int newSize = arr.length - 1;
    T[] newArray = (T[]) new Object[newSize];
    System.arraycopy(arr, 0, newArray, 0, pos);
    System.arraycopy(arr, pos + 1, newArray, pos, newSize - pos);
    return newArray;
  }

  @SuppressWarnings("unchecked")
  private static  T[] addToArray(T[] arr, int pos, T value) {
    int newSize = arr.length + 1;
    T[] newArray = (T[]) new Object[newSize];
    System.arraycopy(arr, 0, newArray, 0, pos);
    newArray[pos] = value;
    System.arraycopy(arr, pos, newArray, pos + 1, newSize - pos - 1);
    return newArray;
  }

  @SuppressWarnings("unchecked")
  private static  T[] replaceInArray(T[] arr, int pos, T value) {
    int size = arr.length;
    T[] newArray = (T[]) new Object[size];
    System.arraycopy(arr, 0, newArray, 0, size);
    newArray[pos] = value;
    return newArray;
  }

  @Override
  public boolean containsKey(K key) {
    return findKey(key) != -1;
  }

  @Override
  public V get(K key) {
    int pos = findKey(key);
    return pos != -1 ? this.values[pos] : null;
  }

  @Override
  public ImmutableSortedMap remove(K key) {
    int pos = findKey(key);
    if (pos == -1) {
      return this;
    } else {
      K[] keys = removeFromArray(this.keys, pos);
      V[] values = removeFromArray(this.values, pos);
      return new ArraySortedMap<>(this.comparator, keys, values);
    }
  }

  @Override
  public ImmutableSortedMap insert(K key, V value) {
    int pos = findKey(key);
    if (pos != -1) {
      if (this.keys[pos] == key && this.values[pos] == value) {
        return this;
      } else {
        // The key and/or value might have changed, even though the comparison might
        // still yield 0
        K[] newKeys = replaceInArray(this.keys, pos, key);
        V[] newValues = replaceInArray(this.values, pos, value);
        return new ArraySortedMap<>(this.comparator, newKeys, newValues);
      }
    } else {
      if (this.keys.length > Builder.ARRAY_TO_RB_TREE_SIZE_THRESHOLD) {
        @SuppressWarnings("unchecked")
        Map map = new HashMap<>(this.keys.length + 1);
        for (int i = 0; i < this.keys.length; i++) {
          map.put(this.keys[i], this.values[i]);
        }
        map.put(key, value);
        return RBTreeSortedMap.fromMap(map, this.comparator);
      } else {
        int newPos = findKeyOrInsertPosition(key);
        K[] keys = addToArray(this.keys, newPos, key);
        V[] values = addToArray(this.values, newPos, value);
        return new ArraySortedMap<>(this.comparator, keys, values);
      }
    }
  }

  @Override
  public K getMinKey() {
    return this.keys.length > 0 ? this.keys[0] : null;
  }

  @Override
  public K getMaxKey() {
    return this.keys.length > 0 ? this.keys[this.keys.length - 1] : null;
  }

  @Override
  public int size() {
    return this.keys.length;
  }

  @Override
  public boolean isEmpty() {
    return this.keys.length == 0;
  }

  @Override
  public void inOrderTraversal(LLRBNode.NodeVisitor visitor) {
    for (int i = 0; i < this.keys.length; i++) {
      visitor.visitEntry(this.keys[i], this.values[i]);
    }
  }

  private Iterator> iterator(final int pos, final boolean reverse) {
    return new Iterator>() {
      int currentPos = pos;

      @Override
      public boolean hasNext() {
        return reverse ? currentPos >= 0 : currentPos < keys.length;
      }

      @Override
      public Map.Entry next() {
        final K key = keys[currentPos];
        final V value = values[currentPos];
        currentPos = reverse ? currentPos - 1 : currentPos + 1;
        return new AbstractMap.SimpleImmutableEntry<>(key, value);
      }

      @Override
      public void remove() {
        throw new UnsupportedOperationException("Can't remove elements from ImmutableSortedMap");
      }
    };
  }

  @Override
  public Iterator> iterator() {
    return iterator(0, false);
  }

  @Override
  public Iterator> iteratorFrom(K key) {
    int pos = findKeyOrInsertPosition(key);
    return iterator(pos, false);
  }

  @Override
  public Iterator> reverseIteratorFrom(K key) {
    int pos = findKeyOrInsertPosition(key);
    // if there's no exact match, findKeyOrInsertPosition will return the index *after* the
    // closest match, but
    // since this is a reverse iterator, we want to start just *before* the closest match.
    if (pos < this.keys.length && this.comparator.compare(this.keys[pos], key) == 0) {
      return iterator(pos, true);
    } else {
      return iterator(pos - 1, true);
    }
  }

  @Override
  public Iterator> reverseIterator() {
    return iterator(this.keys.length - 1, true);
  }

  @Override
  public K getPredecessorKey(K key) {
    int pos = findKey(key);
    if (pos == -1) {
      throw new IllegalArgumentException("Can't find predecessor of nonexistent key");
    } else {
      return (pos > 0) ? this.keys[pos - 1] : null;
    }
  }

  @Override
  public K getSuccessorKey(K key) {
    int pos = findKey(key);
    if (pos == -1) {
      throw new IllegalArgumentException("Can't find successor of nonexistent key");
    } else {
      return (pos < this.keys.length - 1) ? this.keys[pos + 1] : null;
    }
  }

  @Override
  public Comparator getComparator() {
    return comparator;
  }

  /**
   * This does a linear scan which is simpler than a binary search. For a small collection size this
   * still should be as fast a as binary search.
   */
  private int findKeyOrInsertPosition(K key) {
    int newPos = 0;
    while (newPos < this.keys.length && this.comparator.compare(this.keys[newPos], key) < 0) {
      newPos++;
    }
    return newPos;
  }

  /**
   * This does a linear scan which is simpler than a binary search. For a small collection size this
   * still should be as fast a as binary search.
   */
  private int findKey(K key) {
    int i = 0;
    for (K otherKey : this.keys) {
      if (this.comparator.compare(key, otherKey) == 0) {
        return i;
      }
      i++;
    }
    return -1;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy