add KeyMap, custom key-only HashMap
This commit is contained in:
parent
bda73374ca
commit
d434c11e78
641
vtm/src/org/oscim/utils/KeyMap.java
Normal file
641
vtm/src/org/oscim/utils/KeyMap.java
Normal file
@ -0,0 +1,641 @@
|
||||
package org.oscim.utils;
|
||||
|
||||
/*
|
||||
* Copyright 2014 Hannes Janetzek
|
||||
*
|
||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
||||
*
|
||||
* This program 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 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Stripped down HashMap making HashItem entries public - So you have your custom
|
||||
* 'Entry' holding key and value. HashItem must implement equals() and hashCode()
|
||||
* only for the 'key' part.
|
||||
*
|
||||
* KeyMap.put(HashItem, boolean replace) allows to get or add an item in one invocation.
|
||||
*
|
||||
* TODO add to NOTICE file
|
||||
* The VTM library includes software developed as part of the Apache
|
||||
* Harmony project which is copyright 2006, The Apache Software Foundation and
|
||||
* released under the Apache License 2.0. http://harmony.apache.org
|
||||
*/
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.oscim.utils.KeyMap.HashItem;
|
||||
import org.oscim.utils.pool.Inlist;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Note: the implementation of {@code KeyMap} is not synchronized. If one thread
|
||||
* of several threads accessing an instance modifies the map structurally,
|
||||
* access to the map needs to be synchronized. A structural modification is an
|
||||
* operation that adds or removes an entry. Changes in the value of an entry are
|
||||
* not structural changes.
|
||||
*
|
||||
* @param <K> the type of keys maintained by this map
|
||||
*/
|
||||
public class KeyMap<K extends HashItem> {
|
||||
/**
|
||||
* Min capacity (other than zero) for a HashMap. Must be a power of two
|
||||
* greater than 1 (and less than 1 << 30).
|
||||
*/
|
||||
private static final int MINIMUM_CAPACITY = 4;
|
||||
|
||||
/**
|
||||
* Max capacity for a HashMap. Must be a power of two >= MINIMUM_CAPACITY.
|
||||
*/
|
||||
private static final int MAXIMUM_CAPACITY = 1 << 30;
|
||||
|
||||
/**
|
||||
* An empty table shared by all zero-capacity maps (typically from default
|
||||
* constructor). It is never written to, and replaced on first put. Its size
|
||||
* is set to half the minimum, so that the first resize will create a
|
||||
* minimum-sized table.
|
||||
*/
|
||||
private static final HashItem[] EMPTY_TABLE = new HashItem[MINIMUM_CAPACITY >>> 1];
|
||||
|
||||
/**
|
||||
* The default load factor. Note that this implementation ignores the
|
||||
* load factor, but cannot do away with it entirely because it's
|
||||
* mentioned in the API.
|
||||
*
|
||||
* <p>
|
||||
* Note that this constant has no impact on the behavior of the program, but
|
||||
* it is emitted as part of the serialized form. The load factor of .75 is
|
||||
* hardwired into the program, which uses cheap shifts in place of expensive
|
||||
* division.
|
||||
*/
|
||||
static final float DEFAULT_LOAD_FACTOR = .75F;
|
||||
|
||||
/**
|
||||
* The hash table. If this hash map contains a mapping for null, it is
|
||||
* not represented this hash table.
|
||||
*/
|
||||
HashItem[] table;
|
||||
|
||||
/**
|
||||
* The number of mappings in this hash map.
|
||||
*/
|
||||
int size;
|
||||
|
||||
/**
|
||||
* The table is rehashed when its size exceeds this threshold.
|
||||
* The value of this field is generally .75 * capacity, except when
|
||||
* the capacity is zero, as described in the EMPTY_TABLE declaration
|
||||
* above.
|
||||
*/
|
||||
private int threshold;
|
||||
|
||||
/**
|
||||
* Constructs a new empty {@code HashMap} instance.
|
||||
*/
|
||||
public KeyMap() {
|
||||
table = (HashItem[]) EMPTY_TABLE;
|
||||
threshold = -1; // Forces first put invocation to replace EMPTY_TABLE
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code HashMap} instance with the specified capacity.
|
||||
*
|
||||
* @param capacity
|
||||
* the initial capacity of this hash map.
|
||||
* @throws IllegalArgumentException
|
||||
* when the capacity is less than zero.
|
||||
*/
|
||||
public KeyMap(int capacity) {
|
||||
if (capacity < 0) {
|
||||
throw new IllegalArgumentException("Capacity: " + capacity);
|
||||
}
|
||||
|
||||
if (capacity == 0) {
|
||||
HashItem[] tab = (HashItem[]) EMPTY_TABLE;
|
||||
table = tab;
|
||||
threshold = -1; // Forces first put() to replace EMPTY_TABLE
|
||||
return;
|
||||
}
|
||||
|
||||
if (capacity < MINIMUM_CAPACITY) {
|
||||
capacity = MINIMUM_CAPACITY;
|
||||
} else if (capacity > MAXIMUM_CAPACITY) {
|
||||
capacity = MAXIMUM_CAPACITY;
|
||||
} else {
|
||||
capacity = roundUpToPowerOfTwo(capacity);
|
||||
}
|
||||
makeTable(capacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code HashMap} instance with the specified capacity and
|
||||
* load factor.
|
||||
*
|
||||
* @param capacity
|
||||
* the initial capacity of this hash map.
|
||||
* @param loadFactor
|
||||
* the initial load factor.
|
||||
* @throws IllegalArgumentException
|
||||
* when the capacity is less than zero or the load factor is
|
||||
* less or equal to zero or NaN.
|
||||
*/
|
||||
public KeyMap(int capacity, float loadFactor) {
|
||||
this(capacity);
|
||||
|
||||
if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
|
||||
throw new IllegalArgumentException("Load factor: " + loadFactor);
|
||||
}
|
||||
|
||||
/* Note that this implementation ignores loadFactor; it always uses
|
||||
* a load factor of 3/4. This simplifies the code and generally
|
||||
* improves performance. */
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an appropriate capacity for the specified initial size. Does
|
||||
* not round the result up to a power of two; the caller must do this!
|
||||
* The returned value will be between 0 and MAXIMUM_CAPACITY (inclusive).
|
||||
*/
|
||||
static int capacityForInitSize(int size) {
|
||||
int result = (size >> 1) + size; // Multiply by 3/2 to allow for growth
|
||||
|
||||
// boolean expr is equivalent to result >= 0 && result<MAXIMUM_CAPACITY
|
||||
return (result & ~(MAXIMUM_CAPACITY - 1)) == 0 ? result : MAXIMUM_CAPACITY;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from the pseudo-constructors (clone and readObject)
|
||||
* prior to invoking constructorPut/constructorPutAll, which invoke the
|
||||
* overridden constructorNewEntry method. Normally it is a VERY bad idea to
|
||||
* invoke an overridden method from a pseudo-constructor (Effective Java
|
||||
* Item 17). In this case it is unavoidable, and the init method provides a
|
||||
* workaround.
|
||||
*/
|
||||
void init() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this map is empty.
|
||||
*
|
||||
* @return {@code true} if this map has no elements, {@code false}
|
||||
* otherwise.
|
||||
* @see #size()
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return size == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of elements in this map.
|
||||
*
|
||||
* @return the number of elements in this map.
|
||||
*/
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the mapping with the specified key.
|
||||
*
|
||||
* @param key
|
||||
* the key.
|
||||
* @return the value of the mapping with the specified key, or {@code null}
|
||||
* if no mapping for the specified key is found.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public K get(HashItem key) {
|
||||
// if (key == null) {
|
||||
// HashItem e = entryForNullKey;
|
||||
// return e == null ? null : e.key;
|
||||
// }
|
||||
|
||||
// Doug Lea's supplemental secondaryHash function (inlined)
|
||||
int hash = key.hashCode();
|
||||
hash ^= (hash >>> 20) ^ (hash >>> 12);
|
||||
hash ^= (hash >>> 7) ^ (hash >>> 4);
|
||||
|
||||
HashItem[] tab = table;
|
||||
for (HashItem e = tab[hash & (tab.length - 1)]; e != null; e = e.next) {
|
||||
HashItem eKey = e;
|
||||
if (eKey == key || (e.hash == hash && key.equals(eKey))) {
|
||||
return (K) e;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Returns whether this map contains the specified key.
|
||||
// *
|
||||
// * @param key
|
||||
// * the key to search for.
|
||||
// * @return {@code true} if this map contains the specified key,
|
||||
// * {@code false} otherwise.
|
||||
// */
|
||||
// //@Override
|
||||
// public boolean containsKey(Object key) {
|
||||
// if (key == null) {
|
||||
// return entryForNullKey != null;
|
||||
// }
|
||||
//
|
||||
// // Doug Lea's supplemental secondaryHash function (inlined)
|
||||
// int hash = key.hashCode();
|
||||
// hash ^= (hash >>> 20) ^ (hash >>> 12);
|
||||
// hash ^= (hash >>> 7) ^ (hash >>> 4);
|
||||
//
|
||||
// HashItem[] tab = table;
|
||||
// for (HashItem e = tab[hash & (tab.length - 1)]; e != null; e = e.next) {
|
||||
// K eKey = e.key;
|
||||
// if (eKey == key || (e.hash == hash && key.equals(eKey))) {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Returns whether this map contains the specified value.
|
||||
// *
|
||||
// * @param value
|
||||
// * the value to search for.
|
||||
// * @return {@code true} if this map contains the specified value,
|
||||
// * {@code false} otherwise.
|
||||
// */
|
||||
// @Override
|
||||
// public boolean containsValue(Object value) {
|
||||
// HashMapEntry[] tab = table;
|
||||
// int len = tab.length;
|
||||
// if (value == null) {
|
||||
// for (int i = 0; i < len; i++) {
|
||||
// for (HashMapEntry e = tab[i]; e != null; e = e.next) {
|
||||
// if (e.value == null) {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return entryForNullKey != null && entryForNullKey.value == null;
|
||||
// }
|
||||
//
|
||||
// // value is non-null
|
||||
// for (int i = 0; i < len; i++) {
|
||||
// for (HashMapEntry e = tab[i]; e != null; e = e.next) {
|
||||
// if (value.equals(e.value)) {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return entryForNullKey != null && value.equals(entryForNullKey.value);
|
||||
// }
|
||||
|
||||
/**
|
||||
* Maps the specified key to the specified value.
|
||||
*
|
||||
* @param key
|
||||
* the key.
|
||||
* @param value
|
||||
* the value.
|
||||
* @return the value of any previous mapping with the specified key or
|
||||
* {@code null} if there was no such mapping.
|
||||
*/
|
||||
public K put(K key) {
|
||||
return put(key, true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public K put(K key, boolean replace) {
|
||||
|
||||
int hash = secondaryHash(key.hashCode());
|
||||
HashItem[] tab = table;
|
||||
int index = hash & (tab.length - 1);
|
||||
for (HashItem e = tab[index]; e != null; e = e.next) {
|
||||
if (e.hash == hash && key.equals(e)) {
|
||||
if (replace) {
|
||||
tab[index] = Inlist.remove(tab[index], e);
|
||||
tab[index] = Inlist.push(tab[index], key);
|
||||
}
|
||||
//V oldValue = e.value;
|
||||
//e.value = value;
|
||||
return (K) e; //oldValue;
|
||||
}
|
||||
}
|
||||
|
||||
// No entry key is present; create one
|
||||
if (size++ > threshold) {
|
||||
tab = doubleCapacity();
|
||||
index = hash & (tab.length - 1);
|
||||
}
|
||||
addNewEntry(key, hash, index);
|
||||
return null;
|
||||
}
|
||||
|
||||
// public K put(K key) {
|
||||
// // if (key == null) {
|
||||
// // return putValueForNullKey(value);
|
||||
// // }
|
||||
//
|
||||
// int hash = secondaryHash(key.hashCode());
|
||||
// HashItem[] tab = table;
|
||||
// int index = hash & (tab.length - 1);
|
||||
// for (HashItem e = tab[index]; e != null; e = e.next) {
|
||||
// if (e.hash == hash && key.equals(e.key)) {
|
||||
// preModify(e);
|
||||
// //V oldValue = e.value;
|
||||
// //e.value = value;
|
||||
// return e.key; //oldValue;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // No entry for (non-null) key is present; create one
|
||||
// modCount++;
|
||||
// if (size++ > threshold) {
|
||||
// tab = doubleCapacity();
|
||||
// index = hash & (tab.length - 1);
|
||||
// }
|
||||
// addNewEntry(key, hash, index);
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// private V putValueForNullKey(V value) {
|
||||
// HashMapEntry<K> entry = entryForNullKey;
|
||||
// if (entry == null) {
|
||||
// addNewEntryForNullKey(value);
|
||||
// size++;
|
||||
// modCount++;
|
||||
// return null;
|
||||
// } else {
|
||||
// preModify(entry);
|
||||
// V oldValue = entry.value;
|
||||
// entry.value = value;
|
||||
// return oldValue;
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* Creates a new entry for the given key, value, hash, and index and
|
||||
* inserts it into the hash table. This method is called by put
|
||||
* (and indirectly, putAll), and overridden by LinkedHashMap. The hash
|
||||
* must incorporate the secondary hash function.
|
||||
*/
|
||||
void addNewEntry(K key, int hash, int index) {
|
||||
key.setIndex(hash, table[index]);
|
||||
table[index] = key;
|
||||
}
|
||||
|
||||
///**
|
||||
// * Ensures that the hash table has sufficient capacity to store the
|
||||
// * specified number of mappings, with room to grow. If not, it increases the
|
||||
// * capacity as appropriate. Like doubleCapacity, this method moves existing
|
||||
// * entries to new buckets as appropriate. Unlike doubleCapacity, this method
|
||||
// * can grow the table by factors of 2^n for n > 1. Hopefully, a single call
|
||||
// * to this method will be faster than multiple calls to doubleCapacity.
|
||||
// *
|
||||
// * <p>
|
||||
// * This method is called only by putAll.
|
||||
// */
|
||||
//private void ensureCapacity(int numMappings) {
|
||||
// int newCapacity = roundUpToPowerOfTwo(capacityForInitSize(numMappings));
|
||||
// HashItem[] oldTable = table;
|
||||
// int oldCapacity = oldTable.length;
|
||||
// if (newCapacity <= oldCapacity) {
|
||||
// return;
|
||||
// }
|
||||
// if (newCapacity == oldCapacity * 2) {
|
||||
// doubleCapacity();
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // We're growing by at least 4x, rehash in the obvious way
|
||||
// HashItem[] newTable = makeTable(newCapacity);
|
||||
// if (size != 0) {
|
||||
// int newMask = newCapacity - 1;
|
||||
// for (int i = 0; i < oldCapacity; i++) {
|
||||
// for (HashItem e = oldTable[i]; e != null;) {
|
||||
// HashItem oldNext = e.next;
|
||||
// int newIndex = e.hash & newMask;
|
||||
// HashItem newNext = newTable[newIndex];
|
||||
// newTable[newIndex] = e;
|
||||
// e.next = newNext;
|
||||
// e = oldNext;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
/**
|
||||
* Allocate a table of the given capacity and set the threshold accordingly.
|
||||
*
|
||||
* @param newCapacity must be a power of two
|
||||
*/
|
||||
private HashItem[] makeTable(int newCapacity) {
|
||||
HashItem[] newTable = (HashItem[]) new HashItem[newCapacity];
|
||||
table = newTable;
|
||||
threshold = (newCapacity >> 1) + (newCapacity >> 2); // 3/4 capacity
|
||||
return newTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Doubles the capacity of the hash table. Existing entries are placed in
|
||||
* the correct bucket on the enlarged table. If the current capacity is,
|
||||
* MAXIMUM_CAPACITY, this method is a no-op. Returns the table, which
|
||||
* will be new unless we were already at MAXIMUM_CAPACITY.
|
||||
*/
|
||||
private HashItem[] doubleCapacity() {
|
||||
HashItem[] oldTable = table;
|
||||
int oldCapacity = oldTable.length;
|
||||
if (oldCapacity == MAXIMUM_CAPACITY) {
|
||||
return oldTable;
|
||||
}
|
||||
int newCapacity = oldCapacity * 2;
|
||||
HashItem[] newTable = makeTable(newCapacity);
|
||||
if (size == 0) {
|
||||
return newTable;
|
||||
}
|
||||
|
||||
for (int j = 0; j < oldCapacity; j++) {
|
||||
/* Rehash the bucket using the minimum number of field writes.
|
||||
* This is the most subtle and delicate code in the class. */
|
||||
HashItem e = oldTable[j];
|
||||
if (e == null) {
|
||||
continue;
|
||||
}
|
||||
int highBit = e.hash & oldCapacity;
|
||||
HashItem broken = null;
|
||||
newTable[j | highBit] = e;
|
||||
for (HashItem n = e.next; n != null; e = n, n = n.next) {
|
||||
int nextHighBit = n.hash & oldCapacity;
|
||||
if (nextHighBit != highBit) {
|
||||
if (broken == null)
|
||||
newTable[j | nextHighBit] = n;
|
||||
else
|
||||
broken.next = n;
|
||||
broken = e;
|
||||
highBit = nextHighBit;
|
||||
}
|
||||
}
|
||||
if (broken != null)
|
||||
broken.next = null;
|
||||
}
|
||||
return newTable;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Removes the mapping with the specified key from this map.
|
||||
// *
|
||||
// * @param key
|
||||
// * the key of the mapping to remove.
|
||||
// * @return the value of the removed mapping or {@code null} if no mapping
|
||||
// * for the specified key was found.
|
||||
// */
|
||||
// @Override
|
||||
// public V remove(Object key) {
|
||||
// if (key == null) {
|
||||
// return removeNullKey();
|
||||
// }
|
||||
// int hash = secondaryHash(key.hashCode());
|
||||
// HashMapEntry<K>[] tab = table;
|
||||
// int index = hash & (tab.length - 1);
|
||||
// for (HashMapEntry<K> e = tab[index], prev = null; e != null; prev = e, e = e.next) {
|
||||
// if (e.hash == hash && key.equals(e.key)) {
|
||||
// if (prev == null) {
|
||||
// tab[index] = e.next;
|
||||
// } else {
|
||||
// prev.next = e.next;
|
||||
// }
|
||||
// modCount++;
|
||||
// size--;
|
||||
// postRemove(e);
|
||||
// return e.value;
|
||||
// }
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Subclass overrides this method to unlink entry.
|
||||
*/
|
||||
void postRemove(HashItem e) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all mappings from this hash map, leaving it empty.
|
||||
*
|
||||
* @see #isEmpty
|
||||
* @see #size
|
||||
*/
|
||||
public void clear() {
|
||||
if (size != 0) {
|
||||
Arrays.fill(table, null);
|
||||
size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static final boolean STATS = false;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public K releaseItems() {
|
||||
if (size == 0)
|
||||
return null;
|
||||
|
||||
int collisions = 0;
|
||||
int max = 0;
|
||||
int sum = 0;
|
||||
|
||||
HashItem items = null;
|
||||
HashItem last;
|
||||
for (int i = 0, n = table.length; i < n; i++) {
|
||||
HashItem item = table[i];
|
||||
if (item == null)
|
||||
continue;
|
||||
table[i] = null;
|
||||
if (STATS) {
|
||||
sum = 0;
|
||||
last = item;
|
||||
while (last != null) {
|
||||
if (last.next == null)
|
||||
break;
|
||||
|
||||
sum++;
|
||||
last = last.next;
|
||||
}
|
||||
max = Math.max(max, sum);
|
||||
collisions += sum;
|
||||
} else {
|
||||
last = Inlist.last(item);
|
||||
}
|
||||
last.next = items;
|
||||
items = item;
|
||||
}
|
||||
if (STATS)
|
||||
System.out.println("collisions: " + collisions + " " + max + " " + size);
|
||||
|
||||
Arrays.fill(table, null);
|
||||
size = 0;
|
||||
|
||||
return (K) items;
|
||||
}
|
||||
|
||||
public static class HashItem extends Inlist<HashItem> {
|
||||
int hash;
|
||||
|
||||
public void setIndex(int hash, HashItem next) {
|
||||
this.hash = hash;
|
||||
this.next = next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a supplemental hash function to a given hashCode, which defends
|
||||
* against poor quality hash functions. This is critical because HashMap
|
||||
* uses power-of-two length hash tables, that otherwise encounter collisions
|
||||
* for hashCodes that do not differ in lower or upper bits.
|
||||
*/
|
||||
private static int secondaryHash(int h) {
|
||||
// Doug Lea's supplemental hash function
|
||||
h ^= (h >>> 20) ^ (h >>> 12);
|
||||
return h ^ (h >>> 7) ^ (h >>> 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the smallest power of two >= its argument, with several caveats:
|
||||
* If the argument is negative but not Integer.MIN_VALUE, the method returns
|
||||
* zero. If the argument is > 2^30 or equal to Integer.MIN_VALUE, the method
|
||||
* returns Integer.MIN_VALUE. If the argument is zero, the method returns
|
||||
* zero.
|
||||
*/
|
||||
private static int roundUpToPowerOfTwo(int i) {
|
||||
i--; // If input is a power of two, shift its high-order bit right
|
||||
|
||||
// "Smear" the high-order bit all the way to the right
|
||||
i |= i >>> 1;
|
||||
i |= i >>> 2;
|
||||
i |= i >>> 4;
|
||||
i |= i >>> 8;
|
||||
i |= i >>> 16;
|
||||
|
||||
return i + 1;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user