mirror of https://github.com/apache/lucene.git
LUCENE-6095: Remove RamUsageTester.IdentityHashSet and use Collections.newSetFromMap(new IdentityHashMap<E, Boolean>()) instead.
git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1644587 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
08cdb3c867
commit
e9366323e8
|
@ -1,56 +0,0 @@
|
|||
package org.apache.lucene.util;
|
||||
|
||||
/*
|
||||
* 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.*;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestIdentityHashSet extends LuceneTestCase {
|
||||
@Test
|
||||
public void testCheck() {
|
||||
Random rnd = random();
|
||||
Set<Object> jdk = Collections.newSetFromMap(
|
||||
new IdentityHashMap<Object,Boolean>());
|
||||
RamUsageTester.IdentityHashSet<Object> us = new RamUsageTester.IdentityHashSet<>();
|
||||
|
||||
int max = 100000;
|
||||
int threshold = 256;
|
||||
for (int i = 0; i < max; i++) {
|
||||
// some of these will be interned and some will not so there will be collisions.
|
||||
Integer v = rnd.nextInt(threshold);
|
||||
|
||||
boolean e1 = jdk.contains(v);
|
||||
boolean e2 = us.contains(v);
|
||||
Assert.assertEquals(e1, e2);
|
||||
|
||||
e1 = jdk.add(v);
|
||||
e2 = us.add(v);
|
||||
Assert.assertEquals(e1, e2);
|
||||
}
|
||||
|
||||
Set<Object> collected = Collections.newSetFromMap(
|
||||
new IdentityHashMap<Object,Boolean>());
|
||||
for (Object o : us) {
|
||||
collected.add(o);
|
||||
}
|
||||
|
||||
Assert.assertEquals(collected, jdk);
|
||||
}
|
||||
}
|
|
@ -22,15 +22,13 @@ import java.lang.reflect.Field;
|
|||
import java.lang.reflect.Modifier;
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
|
||||
/** Crawls object graph to collect RAM usage for testing */
|
||||
public final class RamUsageTester {
|
||||
|
@ -91,7 +89,7 @@ public final class RamUsageTester {
|
|||
*/
|
||||
private static long measureObjectSize(Object root, Accumulator accumulator) {
|
||||
// Objects seen so far.
|
||||
final IdentityHashSet<Object> seen = new IdentityHashSet<>();
|
||||
final Set<Object> seen = Collections.newSetFromMap(new IdentityHashMap<Object, Boolean>());
|
||||
// Class cache with reference Field and precalculated shallow size.
|
||||
final IdentityHashMap<Class<?>, ClassCache> classCache = new IdentityHashMap<>();
|
||||
// Stack of objects pending traversal. Recursion caused stack overflows.
|
||||
|
@ -213,243 +211,5 @@ public final class RamUsageTester {
|
|||
referenceFields.toArray(new Field[referenceFields.size()]));
|
||||
return cachedInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* An identity hash set implemented using open addressing. No null keys are allowed.
|
||||
*
|
||||
* TODO: If this is useful outside this class, make it public - needs some work
|
||||
*/
|
||||
static final class IdentityHashSet<KType> implements Iterable<KType> {
|
||||
/**
|
||||
* Default load factor.
|
||||
*/
|
||||
public final static float DEFAULT_LOAD_FACTOR = 0.75f;
|
||||
|
||||
/**
|
||||
* Minimum capacity for the set.
|
||||
*/
|
||||
public final static int MIN_CAPACITY = 4;
|
||||
|
||||
/**
|
||||
* All of set entries. Always of power of two length.
|
||||
*/
|
||||
public Object[] keys;
|
||||
|
||||
/**
|
||||
* Cached number of assigned slots.
|
||||
*/
|
||||
public int assigned;
|
||||
|
||||
/**
|
||||
* The load factor for this set (fraction of allocated or deleted slots before
|
||||
* the buffers must be rehashed or reallocated).
|
||||
*/
|
||||
public final float loadFactor;
|
||||
|
||||
/**
|
||||
* Cached capacity threshold at which we must resize the buffers.
|
||||
*/
|
||||
private int resizeThreshold;
|
||||
|
||||
/**
|
||||
* Creates a hash set with the default capacity of 16.
|
||||
* load factor of {@value #DEFAULT_LOAD_FACTOR}. `
|
||||
*/
|
||||
public IdentityHashSet() {
|
||||
this(16, DEFAULT_LOAD_FACTOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a hash set with the given capacity, load factor of
|
||||
* {@value #DEFAULT_LOAD_FACTOR}.
|
||||
*/
|
||||
public IdentityHashSet(int initialCapacity) {
|
||||
this(initialCapacity, DEFAULT_LOAD_FACTOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a hash set with the given capacity and load factor.
|
||||
*/
|
||||
public IdentityHashSet(int initialCapacity, float loadFactor) {
|
||||
initialCapacity = Math.max(MIN_CAPACITY, initialCapacity);
|
||||
|
||||
assert initialCapacity > 0 : "Initial capacity must be between (0, "
|
||||
+ Integer.MAX_VALUE + "].";
|
||||
assert loadFactor > 0 && loadFactor < 1 : "Load factor must be between (0, 1).";
|
||||
this.loadFactor = loadFactor;
|
||||
allocateBuffers(roundCapacity(initialCapacity));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a reference to the set. Null keys are not allowed.
|
||||
*/
|
||||
public boolean add(KType e) {
|
||||
assert e != null : "Null keys not allowed.";
|
||||
|
||||
if (assigned >= resizeThreshold) expandAndRehash();
|
||||
|
||||
final int mask = keys.length - 1;
|
||||
int slot = rehash(e) & mask;
|
||||
Object existing;
|
||||
while ((existing = keys[slot]) != null) {
|
||||
if (e == existing) {
|
||||
return false; // already found.
|
||||
}
|
||||
slot = (slot + 1) & mask;
|
||||
}
|
||||
assigned++;
|
||||
keys[slot] = e;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the set contains a given ref.
|
||||
*/
|
||||
public boolean contains(KType e) {
|
||||
final int mask = keys.length - 1;
|
||||
int slot = rehash(e) & mask;
|
||||
Object existing;
|
||||
while ((existing = keys[slot]) != null) {
|
||||
if (e == existing) {
|
||||
return true;
|
||||
}
|
||||
slot = (slot + 1) & mask;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Rehash via MurmurHash.
|
||||
*
|
||||
* <p>The implementation is based on the
|
||||
* finalization step from Austin Appleby's
|
||||
* <code>MurmurHash3</code>.
|
||||
*
|
||||
* @see <a href="http://sites.google.com/site/murmurhash/">http://sites.google.com/site/murmurhash/</a>
|
||||
*/
|
||||
private static int rehash(Object o) {
|
||||
int k = System.identityHashCode(o);
|
||||
k ^= k >>> 16;
|
||||
k *= 0x85ebca6b;
|
||||
k ^= k >>> 13;
|
||||
k *= 0xc2b2ae35;
|
||||
k ^= k >>> 16;
|
||||
return k;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand the internal storage buffers (capacity) or rehash current keys and
|
||||
* values if there are a lot of deleted slots.
|
||||
*/
|
||||
private void expandAndRehash() {
|
||||
final Object[] oldKeys = this.keys;
|
||||
|
||||
assert assigned >= resizeThreshold;
|
||||
allocateBuffers(nextCapacity(keys.length));
|
||||
|
||||
/*
|
||||
* Rehash all assigned slots from the old hash table.
|
||||
*/
|
||||
final int mask = keys.length - 1;
|
||||
for (int i = 0; i < oldKeys.length; i++) {
|
||||
final Object key = oldKeys[i];
|
||||
if (key != null) {
|
||||
int slot = rehash(key) & mask;
|
||||
while (keys[slot] != null) {
|
||||
slot = (slot + 1) & mask;
|
||||
}
|
||||
keys[slot] = key;
|
||||
}
|
||||
}
|
||||
Arrays.fill(oldKeys, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate internal buffers for a given capacity.
|
||||
*
|
||||
* @param capacity
|
||||
* New capacity (must be a power of two).
|
||||
*/
|
||||
private void allocateBuffers(int capacity) {
|
||||
this.keys = new Object[capacity];
|
||||
this.resizeThreshold = (int) (capacity * DEFAULT_LOAD_FACTOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next possible capacity, counting from the current buffers' size.
|
||||
*/
|
||||
protected int nextCapacity(int current) {
|
||||
assert current > 0 && Long.bitCount(current) == 1 : "Capacity must be a power of two.";
|
||||
assert ((current << 1) > 0) : "Maximum capacity exceeded ("
|
||||
+ (0x80000000 >>> 1) + ").";
|
||||
|
||||
if (current < MIN_CAPACITY / 2) current = MIN_CAPACITY / 2;
|
||||
return current << 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Round the capacity to the next allowed value.
|
||||
*/
|
||||
protected int roundCapacity(int requestedCapacity) {
|
||||
// Maximum positive integer that is a power of two.
|
||||
if (requestedCapacity > (0x80000000 >>> 1)) return (0x80000000 >>> 1);
|
||||
|
||||
int capacity = MIN_CAPACITY;
|
||||
while (capacity < requestedCapacity) {
|
||||
capacity <<= 1;
|
||||
}
|
||||
|
||||
return capacity;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
assigned = 0;
|
||||
Arrays.fill(keys, null);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return assigned;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<KType> iterator() {
|
||||
return new Iterator<KType>() {
|
||||
int pos = -1;
|
||||
Object nextElement = fetchNext();
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return nextElement != null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public KType next() {
|
||||
Object r = this.nextElement;
|
||||
if (r == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
this.nextElement = fetchNext();
|
||||
return (KType) r;
|
||||
}
|
||||
|
||||
private Object fetchNext() {
|
||||
pos++;
|
||||
while (pos < keys.length && keys[pos] == null) {
|
||||
pos++;
|
||||
}
|
||||
|
||||
return (pos >= keys.length ? null : keys[pos]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue