Skip to content

Commit

Permalink
Collapse CompactStringObjectMap into single class again (remove 1/2-e…
Browse files Browse the repository at this point in the history
…lement optimized version)
  • Loading branch information
cowtowncoder committed Mar 23, 2015
1 parent a294f4c commit 19e5ed4
Showing 1 changed file with 90 additions and 164 deletions.
Expand Up @@ -14,196 +14,122 @@
* *
* @since 2.6 * @since 2.6
*/ */
public abstract class CompactStringObjectMap public final class CompactStringObjectMap
{ {
public static CompactStringObjectMap empty() { /**
return Small.EMPTY; * Shared instance that can be used when there are no contents to Map.
} */

private final static CompactStringObjectMap EMPTY = new CompactStringObjectMap(1, 0,
public static <T> CompactStringObjectMap construct(Map<String,T> contents) new Object[4]);
{
if (contents.size() == 0) { // can this occur?
return empty();
}
Iterator<Map.Entry<String,T>> it = contents.entrySet().iterator();
switch (contents.size()) {
case 1:
return new Small(it.next(), null);
case 2:
return new Small(it.next(), it.next());
}
// General-purpose "big" one needed:
return Big.construct(contents);
}

public abstract Object find(String key);

public abstract List<String> keys();


final static class Small extends CompactStringObjectMap private final int _hashMask, _spillCount;
{
public final static Small EMPTY = new Small(null, null);

protected final String key1, key2;
protected final Object value1, value2;


public Small(Map.Entry<String,?> e1, Map.Entry<String,?> e2) private final Object[] _hashArea;
{
if (e1 == null) {
key1 = null;
value1 = null;
} else {
key1 = e1.getKey();
value1 = e1.getValue();
}
if (e2 == null) {
key2 = null;
value2 = null;
} else {
key2 = e2.getKey();
value2 = e2.getValue();
}
}

@Override
public Object find(String key) {
// no assumption of key being intern()ed:
if (key.equals(key1)) {
return value1;
}
if (key.equals(key2)) {
return value2;
}
return null;
}


@Override private CompactStringObjectMap(int hashMask, int spillCount, Object[] hashArea)
public List<String> keys() {
ArrayList<String> keys = new ArrayList<String>(2);
if (key1 != null) {
keys.add(key1);
}
if (key2 != null) {
keys.add(key2);
}
return keys;
}
}

/**
* Raw mapping from keys to indices, optimized for fast access via
* better memory efficiency. Hash area divide in three; main hash,
* half-size secondary, followed by as-big-as-needed spillover.
*/
final static class Big extends CompactStringObjectMap
{ {
private final int _hashMask, _spillCount; _hashMask = hashMask;

_spillCount = spillCount;
private final Object[] _hashArea; _hashArea = hashArea;
}


private Big(int hashMask, int spillCount, Object[] hashArea) public static <T> CompactStringObjectMap construct(Map<String,T> all)
{ {
_hashMask = hashMask; if (all.isEmpty()) { // can this happen?
_spillCount = spillCount; return EMPTY;
_hashArea = hashArea;
} }


public static <T> Big construct(Map<String,T> all) // First: calculate size of primary hash area
{ final int size = findSize(all.size());
// First: calculate size of primary hash area final int mask = size-1;
final int size = findSize(all.size()); // and allocate enough to contain primary/secondary, expand for spillovers as need be
final int mask = size-1; int alloc = (size + (size>>1)) * 2;
// and allocate enough to contain primary/secondary, expand for spillovers as need be Object[] hashArea = new Object[alloc];
int alloc = (size + (size>>1)) * 2; int spillCount = 0;
Object[] hashArea = new Object[alloc];
int spillCount = 0;


for (Map.Entry<String,T> entry : all.entrySet()) { for (Map.Entry<String,T> entry : all.entrySet()) {
String key = entry.getKey(); String key = entry.getKey();


int slot = key.hashCode() & mask; int slot = key.hashCode() & mask;
int ix = slot+slot; int ix = slot+slot;


// primary slot not free? // primary slot not free?
if (hashArea[ix] != null) {
// secondary?
ix = (size + (slot >> 1)) << 1;
if (hashArea[ix] != null) { if (hashArea[ix] != null) {
// secondary? // ok, spill over.
ix = (size + (slot >> 1)) << 1; ix = ((size + (size >> 1) ) << 1) + spillCount;
if (hashArea[ix] != null) { spillCount += 2;
// ok, spill over. if (ix >= hashArea.length) {
ix = ((size + (size >> 1) ) << 1) + spillCount; hashArea = Arrays.copyOf(hashArea, hashArea.length + 4);
spillCount += 2;
if (ix >= hashArea.length) {
hashArea = Arrays.copyOf(hashArea, hashArea.length + 4);
}
} }
} }
hashArea[ix] = key;
hashArea[ix+1] = entry.getValue();
} }
return new Big(mask, spillCount, hashArea); hashArea[ix] = key;
hashArea[ix+1] = entry.getValue();
} }
return new CompactStringObjectMap(mask, spillCount, hashArea);
}


private final static int findSize(int size) private final static int findSize(int size)
{ {
if (size <= 5) { if (size <= 5) {
return 8; return 8;
}
if (size <= 12) {
return 16;
}
int needed = size + (size >> 2); // at most 80% full
int result = 32;
while (result < needed) {
result += result;
}
return result;
} }
if (size <= 12) {
return 16;
}
int needed = size + (size >> 2); // at most 80% full
int result = 32;
while (result < needed) {
result += result;
}
return result;
}


@Override public Object find(String key) {
public Object find(String key) { int slot = key.hashCode() & _hashMask;
int slot = key.hashCode() & _hashMask; int ix = (slot<<1);
int ix = (slot<<1); Object match = _hashArea[ix];
Object match = _hashArea[ix]; if ((match == key) || key.equals(match)) {
if ((match == key) || key.equals(match)) { return _hashArea[ix+1];
return _hashArea[ix+1];
}
return _find2(key, slot, match);
} }
return _find2(key, slot, match);
}


private final Object _find2(String key, int slot, Object match) private final Object _find2(String key, int slot, Object match)
{ {
if (match == null) { if (match == null) {
return null; return null;
} }
int hashSize = _hashMask+1; int hashSize = _hashMask+1;
int ix = hashSize + (slot>>1) << 1; int ix = hashSize + (slot>>1) << 1;
match = _hashArea[ix]; match = _hashArea[ix];
if (key.equals(match)) { if (key.equals(match)) {
return _hashArea[ix+1]; return _hashArea[ix+1];
} }
if (match != null) { // _findFromSpill(...) if (match != null) { // _findFromSpill(...)
int i = (hashSize + (hashSize>>1)) << 1; int i = (hashSize + (hashSize>>1)) << 1;
for (int end = i + _spillCount; i < end; i += 2) { for (int end = i + _spillCount; i < end; i += 2) {
match = _hashArea[i]; match = _hashArea[i];
if ((match == key) || key.equals(match)) { if ((match == key) || key.equals(match)) {
return _hashArea[i+1]; return _hashArea[i+1];
}
} }
} }
return null;
} }
return null;
}


@Override public List<String> keys() {
public List<String> keys() { final int end = _hashArea.length;
final int end = _hashArea.length; List<String> keys = new ArrayList<String>(end >> 2);
List<String> keys = new ArrayList<String>(end >> 2); for (int i = 0; i < end; i += 2) {
for (int i = 0; i < end; i += 2) { Object key = _hashArea[i];
Object key = _hashArea[i]; if (key != null) {
if (key != null) { keys.add((String) key);
keys.add((String) key);
}
} }
return keys;
} }
return keys;
} }
} }

0 comments on commit 19e5ed4

Please sign in to comment.