You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by gn...@apache.org on 2017/10/05 18:22:00 UTC
karaf git commit: [KARAF-5395] Further improve memory consumption for
capabilities / requirements
Repository: karaf
Updated Branches:
refs/heads/master 03bc8dfed -> e5bd746b3
[KARAF-5395] Further improve memory consumption for capabilities / requirements
Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/e5bd746b
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/e5bd746b
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/e5bd746b
Branch: refs/heads/master
Commit: e5bd746b34300ff10ba4a0ead6e6c08efd357982
Parents: 03bc8df
Author: Guillaume Nodet <gn...@gmail.com>
Authored: Thu Oct 5 15:29:44 2017 +0200
Committer: Guillaume Nodet <gn...@gmail.com>
Committed: Thu Oct 5 20:21:35 2017 +0200
----------------------------------------------------------------------
.../internal/resolver/CapabilityImpl.java | 7 +-
.../internal/resolver/FeatureResource.java | 6 +-
.../internal/resolver/RequirementImpl.java | 7 +-
.../internal/resolver/ResourceBuilder.java | 29 +-
.../internal/resolver/ResourceImpl.java | 6 +-
.../internal/resolver/ResourceUtils.java | 31 +-
.../features/internal/util/StringArrayMap.java | 282 +++++++++++++++++++
7 files changed, 332 insertions(+), 36 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/karaf/blob/e5bd746b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/CapabilityImpl.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/CapabilityImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/CapabilityImpl.java
index ba6bfd7..991e3c9 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/CapabilityImpl.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/CapabilityImpl.java
@@ -22,6 +22,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
+import org.apache.karaf.features.internal.util.StringArrayMap;
import org.osgi.framework.Constants;
import org.osgi.resource.Capability;
import org.osgi.resource.Resource;
@@ -36,10 +37,10 @@ public class CapabilityImpl extends BaseClause implements Capability {
public CapabilityImpl(Resource resource, String namespace,
Map<String, String> dirs, Map<String, Object> attrs) {
- this.namespace = namespace;
+ this.namespace = namespace.intern();
this.resource = resource;
- this.dirs = dirs;
- this.attrs = attrs;
+ this.dirs = StringArrayMap.reduceMemory(dirs);
+ this.attrs = StringArrayMap.reduceMemory(attrs);
// Handle mandatory directive
Set<String> mandatory = Collections.emptySet();
http://git-wip-us.apache.org/repos/asf/karaf/blob/e5bd746b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/FeatureResource.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/FeatureResource.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/FeatureResource.java
index 6e1a164..e0017a8 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/FeatureResource.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/FeatureResource.java
@@ -19,6 +19,7 @@ package org.apache.karaf.features.internal.resolver;
import java.util.List;
import java.util.Map;
+import org.apache.felix.utils.version.VersionRange;
import org.apache.felix.utils.version.VersionTable;
import org.apache.karaf.features.BundleInfo;
import org.apache.karaf.features.Conditional;
@@ -107,10 +108,7 @@ public final class FeatureResource extends ResourceImpl {
} else if (!version.startsWith("[") && !version.startsWith("(")) {
version = Macro.transform(featureRange, version);
}
- RequirementImpl requirement = addIdentityRequirement(resource, name, TYPE_FEATURE, version);
- if (condition) {
- requirement.getDirectives().put(REQUIREMENT_CONDITIONAL_DIRECTIVE, CONDITIONAL_TRUE);
- }
+ addIdentityRequirement(resource, name, TYPE_FEATURE, version != null ? new VersionRange(version) : null, true, condition);
}
public Feature getFeature() {
http://git-wip-us.apache.org/repos/asf/karaf/blob/e5bd746b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/RequirementImpl.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/RequirementImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/RequirementImpl.java
index f03fbb3..9dbf6c2 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/RequirementImpl.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/RequirementImpl.java
@@ -18,6 +18,7 @@ package org.apache.karaf.features.internal.resolver;
import java.util.Map;
+import org.apache.karaf.features.internal.util.StringArrayMap;
import org.osgi.framework.Constants;
import org.osgi.resource.Capability;
import org.osgi.resource.Requirement;
@@ -36,9 +37,9 @@ public class RequirementImpl extends BaseClause implements Requirement {
Resource resource, String namespace,
Map<String, String> dirs, Map<String, Object> attrs, SimpleFilter filter) {
this.resource = resource;
- this.namespace = namespace;
- this.dirs = dirs;
- this.attrs = attrs;
+ this.namespace = namespace.intern();
+ this.dirs = StringArrayMap.reduceMemory(dirs);
+ this.attrs = StringArrayMap.reduceMemory(attrs);
this.filter = filter;
// Find resolution import directives.
optional = Constants.RESOLUTION_OPTIONAL.equals(this.dirs.get(Constants.RESOLUTION_DIRECTIVE));
http://git-wip-us.apache.org/repos/asf/karaf/blob/e5bd746b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceBuilder.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceBuilder.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceBuilder.java
index afb05a1..68cb48f 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceBuilder.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceBuilder.java
@@ -18,7 +18,6 @@ package org.apache.karaf.features.internal.resolver;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
@@ -28,6 +27,7 @@ import java.util.Set;
import org.apache.felix.utils.version.VersionRange;
import org.apache.felix.utils.version.VersionTable;
+import org.apache.karaf.features.internal.util.StringArrayMap;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.Version;
@@ -113,7 +113,7 @@ public final class ResourceBuilder {
// Now that we have symbolic name and version, create the resource
String type = headerMap.get(Constants.FRAGMENT_HOST) == null ? IdentityNamespace.TYPE_BUNDLE : IdentityNamespace.TYPE_FRAGMENT;
{
- Map<String, Object> attrs = new HashMap<>(4);
+ Map<String, Object> attrs = new StringArrayMap<>(3);
attrs.put(IdentityNamespace.IDENTITY_NAMESPACE, bundleSymbolicName);
attrs.put(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE, type);
attrs.put(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, bundleVersion);
@@ -138,9 +138,14 @@ public final class ResourceBuilder {
String attachment = bundleCap.dirs.get(Constants.FRAGMENT_ATTACHMENT_DIRECTIVE);
attachment = (attachment == null) ? Constants.FRAGMENT_ATTACHMENT_RESOLVETIME : attachment;
if (!attachment.equalsIgnoreCase(Constants.FRAGMENT_ATTACHMENT_NEVER)) {
- Map<String, Object> hostAttrs = new HashMap<>(bundleCap.attrs);
- Object value = hostAttrs.remove(BundleRevision.BUNDLE_NAMESPACE);
- hostAttrs.put(BundleRevision.HOST_NAMESPACE, value);
+ Map<String, Object> hostAttrs = new StringArrayMap<>(bundleCap.attrs.size());
+ for (Map.Entry<String, Object> e : bundleCap.attrs.entrySet()) {
+ String k = e.getKey();
+ if (BundleRevision.BUNDLE_NAMESPACE.equals(k)) {
+ k = BundleRevision.HOST_NAMESPACE;
+ }
+ hostAttrs.put(k, e.getValue());
+ }
resource.addCapability(new CapabilityImpl(
resource, BundleRevision.HOST_NAMESPACE,
bundleCap.dirs,
@@ -410,7 +415,7 @@ public final class ResourceBuilder {
// Inject filter directive.
// TODO: OSGi R4.3 - Can we insert this on demand somehow?
Map<String, String> dirs = clause.dirs;
- Map<String, String> newDirs = new HashMap<>(dirs.size() + 1);
+ Map<String, String> newDirs = new StringArrayMap<>(dirs.size() + 1);
newDirs.putAll(dirs);
newDirs.put(Constants.FILTER_DIRECTIVE, sf.toString());
@@ -759,7 +764,7 @@ public final class ResourceBuilder {
for (String pkgName : clause.paths) {
// Prepend the package name to the array of attributes.
Map<String, Object> attrs = clause.attrs;
- Map<String, Object> newAttrs = new HashMap<>(attrs.size() + 1);
+ Map<String, Object> newAttrs = new StringArrayMap<>(attrs.size() + 1);
newAttrs.putAll(attrs);
newAttrs.put(BundleRevision.PACKAGE_NAMESPACE, pkgName);
@@ -834,7 +839,7 @@ public final class ResourceBuilder {
// Inject filter directive.
// TODO: OSGi R4.3 - Can we insert this on demand somehow?
Map<String, String> dirs = clauses.get(0).dirs;
- Map<String, String> newDirs = new HashMap<>(dirs.size() + 1);
+ Map<String, String> newDirs = new StringArrayMap<>(dirs.size() + 1);
newDirs.putAll(dirs);
newDirs.put(Constants.FILTER_DIRECTIVE, sf.toString());
@@ -983,7 +988,7 @@ public final class ResourceBuilder {
// Inject filter directive.
// TODO: OSGi R4.3 - Can we insert this on demand somehow?
Map<String, String> dirs = clause.dirs;
- Map<String, String> newDirs = new HashMap<>(dirs.size() + 1);
+ Map<String, String> newDirs = new StringArrayMap<>(dirs.size() + 1);
newDirs.putAll(dirs);
newDirs.put(Constants.FILTER_DIRECTIVE, sf.toString());
@@ -1197,8 +1202,8 @@ public final class ResourceBuilder {
static class ParsedHeaderClause {
public final List<String> paths = new ArrayList<>();
- public final Map<String, String> dirs = new LinkedHashMap<>(1);
- public final Map<String, Object> attrs = new LinkedHashMap<>(1);
- public final Map<String, String> types = new LinkedHashMap<>(1);
+ public final Map<String, String> dirs = new StringArrayMap<>(0);
+ public final Map<String, Object> attrs = new StringArrayMap<>(0);
+ public final Map<String, String> types = new StringArrayMap<>(0);
}
}
http://git-wip-us.apache.org/repos/asf/karaf/blob/e5bd746b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceImpl.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceImpl.java
index 756c440..0d85843 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceImpl.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceImpl.java
@@ -17,10 +17,10 @@
package org.apache.karaf.features.internal.resolver;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.apache.karaf.features.internal.util.StringArrayMap;
import org.osgi.framework.Version;
import org.osgi.framework.namespace.IdentityNamespace;
import org.osgi.resource.Capability;
@@ -45,8 +45,8 @@ public class ResourceImpl implements Resource {
public ResourceImpl(String name, String type, Version version) {
caps = new ArrayList<>();
- Map<String, String> dirs = new HashMap<>();
- Map<String, Object> attrs = new HashMap<>();
+ Map<String, String> dirs = new StringArrayMap<>(0);
+ Map<String, Object> attrs = new StringArrayMap<>(3);
attrs.put(IdentityNamespace.IDENTITY_NAMESPACE, name);
attrs.put(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE, type);
attrs.put(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE, version);
http://git-wip-us.apache.org/repos/asf/karaf/blob/e5bd746b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceUtils.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceUtils.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceUtils.java
index 80af07b..c1798e9 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceUtils.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceUtils.java
@@ -16,17 +16,20 @@
*/
package org.apache.karaf.features.internal.resolver;
-import java.util.HashMap;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.felix.utils.version.VersionRange;
import org.apache.felix.utils.version.VersionTable;
+import org.apache.karaf.features.internal.util.StringArrayMap;
import org.osgi.framework.Constants;
import org.osgi.framework.Version;
import org.osgi.resource.Capability;
import org.osgi.resource.Resource;
+import static org.apache.karaf.features.internal.resolver.FeatureResource.CONDITIONAL_TRUE;
+import static org.apache.karaf.features.internal.resolver.FeatureResource.REQUIREMENT_CONDITIONAL_DIRECTIVE;
import static org.osgi.framework.namespace.IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE;
import static org.osgi.framework.namespace.IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE;
import static org.osgi.framework.namespace.IdentityNamespace.IDENTITY_NAMESPACE;
@@ -94,11 +97,17 @@ public final class ResourceUtils {
}
public static RequirementImpl addIdentityRequirement(ResourceImpl resource, String name, String type, VersionRange range, boolean mandatory) {
- Map<String, String> dirs = new HashMap<>(1);
- Map<String, Object> attrs = new HashMap<>(4);
+ return addIdentityRequirement(resource, name, type, range, mandatory, false);
+ }
+ public static RequirementImpl addIdentityRequirement(ResourceImpl resource, String name, String type, VersionRange range, boolean mandatory, boolean conditional) {
+ Map<String, String> dirs = new StringArrayMap<>((mandatory ? 0 : 1) + (conditional ? 1 : 0));
+ Map<String, Object> attrs = new StringArrayMap<>((name != null ? 1 : 0) + (type != null ? 1 : 0) + (range != null ? 1 : 0));
if (!mandatory) {
dirs.put(REQUIREMENT_RESOLUTION_DIRECTIVE, RESOLUTION_OPTIONAL);
}
+ if (conditional) {
+ dirs.put(REQUIREMENT_CONDITIONAL_DIRECTIVE, CONDITIONAL_TRUE);
+ }
if (name != null) {
attrs.put(IDENTITY_NAMESPACE, name);
}
@@ -121,12 +130,12 @@ public final class ResourceUtils {
for (Capability cap : required.getCapabilities(null)) {
if (cap.getNamespace().equals(IDENTITY_NAMESPACE)) {
Map<String, Object> attributes = cap.getAttributes();
- Map<String, String> dirs = new HashMap<>(1);
+ Map<String, String> dirs = new StringArrayMap<>(1);
dirs.put(REQUIREMENT_RESOLUTION_DIRECTIVE, mandatory ? RESOLUTION_MANDATORY : RESOLUTION_OPTIONAL);
- Map<String, Object> attrs = new HashMap<>(4);
+ Version version = (Version) attributes.get(CAPABILITY_VERSION_ATTRIBUTE);
+ Map<String, Object> attrs = new StringArrayMap<>(version != null ? 3 : 2);
attrs.put(IDENTITY_NAMESPACE, attributes.get(IDENTITY_NAMESPACE));
attrs.put(CAPABILITY_TYPE_ATTRIBUTE, attributes.get(CAPABILITY_TYPE_ATTRIBUTE));
- Version version = (Version) attributes.get(CAPABILITY_VERSION_ATTRIBUTE);
if (version != null) {
attrs.put(CAPABILITY_VERSION_ATTRIBUTE, new VersionRange(version, true));
}
@@ -137,21 +146,21 @@ public final class ResourceUtils {
public static String toFeatureRequirement(String feature) {
String[] parts = feature.split("/");
- Map<String, Object> attrs = new HashMap<>(4);
+ Map<String, Object> attrs = new StringArrayMap<>(parts.length > 1 ? 3 : 2);
attrs.put(IDENTITY_NAMESPACE, parts[0]);
attrs.put(CAPABILITY_TYPE_ATTRIBUTE, TYPE_FEATURE);
if (parts.length > 1) {
attrs.put(CAPABILITY_VERSION_ATTRIBUTE, new VersionRange(parts[1]));
}
- Map<String, String> dirs = new HashMap<>();
- dirs.put(Constants.FILTER_DIRECTIVE, SimpleFilter.convert(attrs).toString());
+ Map<String, String> dirs = Collections.singletonMap(
+ Constants.FILTER_DIRECTIVE, SimpleFilter.convert(attrs).toString());
return new RequirementImpl(null, IDENTITY_NAMESPACE, dirs, attrs).toString();
}
public static String toFeatureCapability(String feature) {
String[] parts = feature.split("/");
- Map<String, String> dirs = new HashMap<>(1);
- Map<String, Object> attrs = new HashMap<>(4);
+ Map<String, String> dirs = Collections.emptyMap();
+ Map<String, Object> attrs = new StringArrayMap<>(parts.length > 1 ? 3 : 2);
attrs.put(IDENTITY_NAMESPACE, parts[0]);
attrs.put(CAPABILITY_TYPE_ATTRIBUTE, TYPE_FEATURE);
if (parts.length > 1) {
http://git-wip-us.apache.org/repos/asf/karaf/blob/e5bd746b/features/core/src/main/java/org/apache/karaf/features/internal/util/StringArrayMap.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/util/StringArrayMap.java b/features/core/src/main/java/org/apache/karaf/features/internal/util/StringArrayMap.java
new file mode 100644
index 0000000..d05b7a2
--- /dev/null
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/util/StringArrayMap.java
@@ -0,0 +1,282 @@
+/*
+ * 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.
+ */
+package org.apache.karaf.features.internal.util;
+
+import java.util.AbstractCollection;
+import java.util.AbstractSet;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.Set;
+
+public class StringArrayMap<V> implements Map<String, V> {
+
+ private Object[] table;
+ private int size;
+
+ public static <T> Map<String, T> reduceMemory(Map<String, T> map) {
+ switch (map.size()) {
+ case 0:
+ return Collections.emptyMap();
+ case 1:
+ Entry<String, T> e = map.entrySet().iterator().next();
+ return Collections.singletonMap(e.getKey().intern(), e.getValue());
+ default:
+ if (map instanceof StringArrayMap) {
+ StringArrayMap<T> m = (StringArrayMap) map;
+ if (m.size == m.table.length / 2) {
+ return map;
+ }
+ }
+ return new StringArrayMap<>(map);
+ }
+ }
+
+ public StringArrayMap(Map<String, ? extends V> map) {
+ if (map instanceof StringArrayMap) {
+ size = ((StringArrayMap) map).size;
+ table = Arrays.copyOf(((StringArrayMap) map).table, size * 2);
+ } else {
+ size = 0;
+ table = new Object[map.size() * 2];
+ for (Entry<String, ? extends V> e : map.entrySet()) {
+ int i = size++ << 1;
+ table[i++] = e.getKey().intern();
+ table[i] = e.getValue();
+ }
+ }
+ }
+
+ public StringArrayMap() {
+ this(32);
+ }
+
+ public StringArrayMap(int capacity) {
+ table = new Object[capacity * 2];
+ size = 0;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public V get(Object key) {
+ String k = ((String) key).intern();
+ for (int i = 0, l = size << 1; i < l; i += 2) {
+ if (k == table[i]) {
+ return (V) table[i + 1];
+ }
+ }
+ return null;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public V put(String key, V value) {
+ key = key.intern();
+ for (int i = 0, l = size << 1; i < l; i += 2) {
+ if (key == table[i]) {
+ V old = (V) table[i + 1];
+ table[i + 1] = value;
+ return old;
+ }
+ }
+ if (table.length == 0) {
+ table = new Object[2];
+ } else if (size * 2 == table.length) {
+ Object[] n = new Object[table.length * 2];
+ System.arraycopy(table, 0, n, 0, table.length);
+ table = n;
+ }
+ int i = size++ << 1;
+ table[i++] = key;
+ table[i] = value;
+ return null;
+ }
+
+ @Override
+ public Set<String> keySet() {
+ return new AbstractSet<String>() {
+ @Override
+ public Iterator<String> iterator() {
+ return new Iterator<String>() {
+ int index = 0;
+
+ @Override
+ public boolean hasNext() {
+ return index < size;
+ }
+
+ @Override
+ public String next() {
+ if (index >= size) {
+ throw new NoSuchElementException();
+ }
+ return (String) table[(index++ << 1)];
+ }
+ };
+ }
+
+ @Override
+ public int size() {
+ return size;
+ }
+ };
+ }
+
+ @Override
+ public Collection<V> values() {
+ return new AbstractCollection<V>() {
+ @Override
+ public Iterator<V> iterator() {
+ return new Iterator<V>() {
+ int index = 0;
+
+ public boolean hasNext() {
+ return index < size;
+ }
+
+ @SuppressWarnings("unchecked")
+ public V next() {
+ if (index >= size) {
+ throw new NoSuchElementException();
+ }
+ return (V) table[(index++ << 1) + 1];
+ }
+ };
+ }
+
+ @Override
+ public int size() {
+ return size;
+ }
+ };
+ }
+
+ @Override
+ public Set<Entry<String, V>> entrySet() {
+ return new AbstractSet<Entry<String, V>>() {
+ @Override
+ public Iterator<Entry<String, V>> iterator() {
+ return new Iterator<Entry<String, V>>() {
+ int index = 0;
+
+ public boolean hasNext() {
+ return index < size;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Entry<String, V> next() {
+ if (index >= size) {
+ throw new NoSuchElementException();
+ }
+ int i = index << 1;
+ index++;
+ return new Entry<String, V>() {
+ @Override
+ public String getKey() {
+ return (String) table[i];
+ }
+
+ @Override
+ public V getValue() {
+ return (V) table[i + 1];
+ }
+
+ @Override
+ public V setValue(V value) {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+ };
+ }
+
+ @Override
+ public int size() {
+ return size;
+ }
+ };
+ }
+
+ @Override
+ public int size() {
+ return size;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return size == 0;
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ String k = ((String) key).intern();
+ for (int i = 0, l = size * 2; i < l; i += 2) {
+ if (table[i] == k) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ for (int i = 0, l = size * 2; i < l; i += 2) {
+ if (Objects.equals(table[i + 1], value)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public V remove(Object key) {
+ String k = ((String) key).intern();
+ for (int i = 0, l = size * 2; i < l; i += 2) {
+ if (table[i] == k) {
+ Object v = table[i + 1];
+ if (i < l - 2) {
+ System.arraycopy(table, i + 2, table, i, l - 2 - i);
+ }
+ table[l - 1] = null;
+ table[l - 2] = null;
+ size--;
+ return (V) v;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void putAll(Map<? extends String, ? extends V> m) {
+ for (Map.Entry<? extends String, ? extends V> e : m.entrySet()) {
+ put(e.getKey(), e.getValue());
+ }
+ }
+
+ @Override
+ public void clear() {
+ size = 0;
+ Arrays.fill(table, null);
+ }
+
+}