/* * Copyright 2015 the original author or authors. * @https://github.com/scouter-project/scouter * * Licensed 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. * * The initial idea for this class is from "org.apache.commons.lang.IntHashMap"; * http://commons.apache.org/commons-lang-2.6-src.zip * */ package scouter.util; import java.util.HashSet; import java.util.NoSuchElementException; /** * @author Paul Kim (sjkim@whatap.io) */ public class StringSet { private static final int DEFAULT_CAPACITY = 101; private static final float DEFAULT_LOAD_FACTOR = 0.75f; private transient StringSetry table[]; private transient int count; private int threshold; private float loadFactor; public StringSet(int initCapacity, float loadFactor) { if (initCapacity < 0) throw new RuntimeException("Capacity Error: " + initCapacity); if (loadFactor <= 0) throw new RuntimeException("Load Count Error: " + loadFactor); if (initCapacity == 0) initCapacity = 1; this.loadFactor = loadFactor; table = new StringSetry[initCapacity]; threshold = (int) (initCapacity * loadFactor); } public StringSet() { this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR); } public StringSet(String[] arr) { this(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR); if (arr == null) return; for (int i = 0; i < arr.length; i++) { this.put(arr[i]); } } public int size() { return count; } public synchronized StringEnumer keys() { return new Enumer(); } public synchronized boolean hasKey(String key) { if (key == null) return false; StringSetry tab[] = table; int hash = key.hashCode(); int index = (hash & Integer.MAX_VALUE) % tab.length; for (StringSetry e = tab[index]; e != null; e = e.next) { if ((e.hash == hash) && CompareUtil.equals(e.key, key)) { return true; } } return false; } protected void rehash() { int oldCapacity = table.length; StringSetry oldMap[] = table; int newCapacity = oldCapacity * 2 + 1; StringSetry newMap[] = new StringSetry[newCapacity]; threshold = (int) (newCapacity * loadFactor); table = newMap; for (int i = oldCapacity; i-- > 0;) { StringSetry old = oldMap[i]; while (old != null) { StringSetry e = old; old = old.next; int index = (e.hash & Integer.MAX_VALUE) % newCapacity; e.next = newMap[index]; newMap[index] = e; } } } public String put(String key) { return unipoint(key); } /** * add a key to StringSet and return hashcode of the key * @param key String * @return String - parameter key */ public synchronized String unipoint(String key) { if (key == null) return null; StringSetry tab[] = table; int hash = key.hashCode(); int index = (hash & Integer.MAX_VALUE) % tab.length; for (StringSetry e = tab[index]; e != null; e = e.next) { if ((e.hash == hash) && CompareUtil.equals(e.key, key)) { return e.key; } } if (count >= threshold) { rehash(); tab = table; index = (hash & Integer.MAX_VALUE) % tab.length; } StringSetry e = new StringSetry(hash, key, tab[index]); tab[index] = e; count++; return key; } public synchronized boolean remove(String key) { if (key == null) return false; StringSetry tab[] = table; int hash = key.hashCode(); int index = (hash & Integer.MAX_VALUE) % tab.length; for (StringSetry e = tab[index], prev = null; e != null; prev = e, e = e.next) { if ((e.hash == hash) && CompareUtil.equals(e.key, key)) { if (prev != null) { prev.next = e.next; } else { tab[index] = e.next; } count--; return true; } } return false; } public synchronized void clear() { StringSetry tab[] = table; for (int index = tab.length; --index >= 0;) tab[index] = null; count = 0; } public synchronized String toString() { int max = size() - 1; StringBuffer buf = new StringBuffer(); StringEnumer it = keys(); buf.append("{"); for (int i = 0; i <= max; i++) { buf.append(it.nextString()); if (i < max) buf.append(", "); } buf.append("}"); return buf.toString(); } private static class StringSetry { int hash; String key; StringSetry next; protected StringSetry(int hash, String key, StringSetry next) { this.hash = hash; this.key = key; this.next = next; } protected Object clone() { return new StringSetry(hash, key, (next == null ? null : (StringSetry) next.clone())); } public String getKey() { return key; } public boolean equals(Object o) { if (!(o instanceof StringSetry)) return false; StringSetry e = (StringSetry) o; return CompareUtil.equals(e.key, key); } public int hashCode() { return hash; } public String toString() { return key.toString(); } } public static StringEnumer emptyEnumer = new StringEnumer() { public String nextString() { return null; } public boolean hasMoreElements() { return false; } }; private class Enumer implements StringEnumer { StringSetry[] table = StringSet.this.table; int index = table.length; StringSetry entry = null; Enumer() { } public boolean hasMoreElements() { while (entry == null && index > 0) entry = table[--index]; return entry != null; } public String nextString() { while (entry == null && index > 0) entry = table[--index]; if (entry != null) { StringSetry e = entry; entry = e.next; return e.key; } throw new NoSuchElementException("no more next"); } } public static void main(String[] args) { HashSet<String> st = new HashSet(); st.add("sss1"); st.add("sss2"); st.add("sss3"); System.out.println(st); } }