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

com.fasterxml.jackson.databind.util.CompactStringObjectMap Maven / Gradle / Ivy

The newest version!
package com.fasterxml.jackson.databind.util;

import java.util.*;

/**
 * Specialized lookup class that implements functionality similar to
 * {@link java.util.Map}, but for special case of key always being
 * {@link java.lang.String} and using more compact (and memory-access
 * friendly) hashing scheme. Assumption is also that keys are typically
 * intern()ed.
 *

* Generics are not used to avoid bridge methods and since these maps * are not exposed as part of external API. * * @since 2.6 */ public final class CompactStringObjectMap implements java.io.Serializable // since 2.6.2 { private static final long serialVersionUID = 1L; /** * Shared instance that can be used when there are no contents to Map. */ private final static CompactStringObjectMap EMPTY = new CompactStringObjectMap(1, 0, new Object[4]); private final int _hashMask, _spillCount; private final Object[] _hashArea; private CompactStringObjectMap(int hashMask, int spillCount, Object[] hashArea) { _hashMask = hashMask; _spillCount = spillCount; _hashArea = hashArea; } public static CompactStringObjectMap construct(Map all) { if (all.isEmpty()) { // can this happen? return EMPTY; } // First: calculate size of primary hash area final int size = findSize(all.size()); final int mask = size-1; // and allocate enough to contain primary/secondary, expand for spillovers as need be int alloc = (size + (size>>1)) * 2; Object[] hashArea = new Object[alloc]; int spillCount = 0; for (Map.Entry entry : all.entrySet()) { String key = entry.getKey(); // 09-Sep-2019, tatu: [databind#2309] skip `null`s if any included if (key == null) { continue; } int slot = key.hashCode() & mask; int ix = slot+slot; // primary slot not free? if (hashArea[ix] != null) { // secondary? ix = (size + (slot >> 1)) << 1; if (hashArea[ix] != null) { // ok, spill over. ix = ((size + (size >> 1) ) << 1) + spillCount; spillCount += 2; if (ix >= hashArea.length) { hashArea = Arrays.copyOf(hashArea, hashArea.length + 4); } } } hashArea[ix] = key; hashArea[ix+1] = entry.getValue(); } return new CompactStringObjectMap(mask, spillCount, hashArea); } private final static int findSize(int size) { if (size <= 5) { 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; } public Object find(String key) { int slot = key.hashCode() & _hashMask; int ix = (slot<<1); Object match = _hashArea[ix]; if ((match == key) || key.equals(match)) { return _hashArea[ix+1]; } return _find2(key, slot, match); } private final Object _find2(String key, int slot, Object match) { if (match == null) { return null; } int hashSize = _hashMask+1; int ix = (hashSize + (slot>>1)) << 1; match = _hashArea[ix]; if (key.equals(match)) { return _hashArea[ix+1]; } if (match != null) { // _findFromSpill(...) int i = (hashSize + (hashSize>>1)) << 1; for (int end = i + _spillCount; i < end; i += 2) { match = _hashArea[i]; if ((match == key) || key.equals(match)) { return _hashArea[i+1]; } } } return null; } // @since 2.9 public Object findCaseInsensitive(String key) { for (int i = 0, end = _hashArea.length; i < end; i += 2) { Object k2 = _hashArea[i]; if (k2 != null) { String s = (String) k2; if (s.equalsIgnoreCase(key)) { return _hashArea[i+1]; // lgtm [java/index-out-of-bounds] } } } return null; } public List keys() { final int end = _hashArea.length; List keys = new ArrayList(end >> 2); for (int i = 0; i < end; i += 2) { Object key = _hashArea[i]; if (key != null) { keys.add((String) key); } } return keys; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy