initial commit

This commit is contained in:
Jörg Prante 2020-09-12 22:42:03 +02:00
commit aceaac08d6
37 changed files with 2607 additions and 0 deletions

14
.gitignore vendored Normal file
View file

@ -0,0 +1,14 @@
/data
/work
/logs
/.idea
/target
.DS_Store
/.settings
/.classpath
/.project
/.gradle
build
out
*~
*.iml

34
build.gradle Normal file
View file

@ -0,0 +1,34 @@
plugins {
id "de.marcphilipp.nexus-publish" version "0.4.0"
id "io.codearte.nexus-staging" version "0.21.1"
}
wrapper {
gradleVersion = "${rootProject.property('gradle.wrapper.version')}"
distributionType = Wrapper.DistributionType.ALL
}
ext {
user = 'jprante'
name = 'datastructures'
description = 'Data structures for Java'
inceptionYear = '2012'
url = 'https://github.com/' + user + '/' + name
scmUrl = 'https://github.com/' + user + '/' + name
scmConnection = 'scm:git:git://github.com/' + user + '/' + name + '.git'
scmDeveloperConnection = 'scm:git:ssh://git@github.com:' + user + '/' + name + '.git'
issueManagementSystem = 'Github'
issueManagementUrl = ext.scmUrl + '/issues'
licenseName = 'The Apache License, Version 2.0'
licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
subprojects {
apply plugin: 'java-library'
apply from: rootProject.file('gradle/ide/idea.gradle')
apply from: rootProject.file('gradle/compile/java.gradle')
apply from: rootProject.file('gradle/test/junit5.gradle')
apply from: rootProject.file('gradle/publishing/publication.gradle')
}
apply from: rootProject.file('gradle/publishing/sonatype.gradle')

View file

@ -0,0 +1,3 @@
module org.xbib.datastructures.common {
exports org.xbib.datastructures.common;
}

View file

@ -0,0 +1,207 @@
package org.xbib.datastructures.common;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* Abstract multi map.
*
* @param <K> the key type parameter
* @param <V> the value type parameter
*/
public abstract class AbstractMultiMap<K, V> implements MultiMap<K, V> {
private final Map<K, Collection<V>> map;
public AbstractMultiMap() {
this(null);
}
public AbstractMultiMap(MultiMap<K, V> map) {
this.map = newMap();
if (map != null) {
putAll(map);
}
}
@Override
public int size() {
return map.size();
}
@Override
public void clear() {
map.clear();
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public boolean containsKey(K key) {
return map.containsKey(key);
}
@Override
public Set<K> keySet() {
return map.keySet();
}
@Override
public boolean put(K key, V value) {
Collection<V> set = map.get(key);
if (set == null) {
set = newValues();
set.add(value);
map.put(key, set);
return true;
} else {
set.add(value);
return false;
}
}
@Override
public void putAll(K key, Iterable<V> values) {
if (values == null) {
return;
}
Collection<V> set = map.computeIfAbsent(key, k -> newValues());
for (V v : values) {
set.add(v);
}
}
@Override
public Collection<V> get(K key) {
return map.get(key);
}
@Override
public Collection<V> remove(K key) {
return map.remove(key);
}
@Override
public boolean remove(K key, V value) {
Collection<V> set = map.get(key);
return set != null && set.remove(value);
}
@Override
public void putAll(MultiMap<K, V> map) {
if (map != null) {
for (K key : map.keySet()) {
putAll(key, map.get(key));
}
}
}
@Override
public Map<K, Collection<V>> asMap() {
return map;
}
@Override
public String getString(K key) {
Collection<V> v = get(key);
return v != null ? v.iterator().next().toString() : null;
}
@Override
public String getString(K key, String defaultValue) {
Collection<V> collection = get(key);
Iterator<V> iterator = collection != null ? collection.iterator() : null;
V v = iterator != null ? iterator.next() : null;
return v != null ? v.toString() : defaultValue;
}
@Override
public Boolean getBoolean(K key, boolean defaultValue) {
Collection<V> collection = get(key);
Iterator<V> iterator = collection != null ? collection.iterator() : null;
V v = iterator != null ? iterator.next() : null;
return v != null ? Boolean.parseBoolean(v.toString()) : defaultValue;
}
@Override
public Short getShort(K key, short defaultValue) {
Collection<V> collection = get(key);
Iterator<V> iterator = collection != null ? collection.iterator() : null;
V v = iterator != null ? iterator.next() : null;
return v != null ? Short.parseShort(v.toString()) : defaultValue;
}
@Override
public Integer getInteger(K key, int defaultValue) {
Collection<V> collection = get(key);
Iterator<V> iterator = collection != null ? collection.iterator() : null;
V v = iterator != null ? iterator.next() : null;
return v != null ? Integer.parseInt(v.toString()) : defaultValue;
}
@Override
public Long getLong(K key, long defaultValue) {
Collection<V> collection = get(key);
Iterator<V> iterator = collection != null ? collection.iterator() : null;
V v = iterator != null ? iterator.next() : null;
return v != null ? Long.parseLong(v.toString()) : defaultValue;
}
@Override
public Float getFloat(K key, float defaultValue) {
Collection<V> collection = get(key);
Iterator<V> iterator = collection != null ? collection.iterator() : null;
V v = iterator != null ? iterator.next() : null;
return v != null ? Float.parseFloat(v.toString()) : defaultValue;
}
@Override
public Double getDouble(K key, double defaultValue) {
Collection<V> collection = get(key);
Iterator<V> iterator = collection != null ? collection.iterator() : null;
V v = iterator != null ? iterator.next() : null;
return v != null ? Double.parseDouble(v.toString()) : defaultValue;
}
@Override
public BigDecimal getBigDecimal(K key, BigDecimal defaultValue) {
Collection<V> collection = get(key);
Iterator<V> iterator = collection != null ? collection.iterator() : null;
V v = iterator != null ? iterator.next() : null;
return v != null ? new BigDecimal(v.toString()) : defaultValue;
}
@Override
public BigInteger getBigInteger(K key, BigInteger defaultValue) {
Collection<V> collection = get(key);
Iterator<V> iterator = collection != null ? collection.iterator() : null;
V v = iterator != null ? iterator.next() : null;
return v != null ? new BigInteger(v.toString()) : defaultValue;
}
@Override
public boolean equals(Object obj) {
return obj instanceof AbstractMultiMap && map.equals(((AbstractMultiMap<?, ?>) obj).map);
}
@Override
public int hashCode() {
return map.hashCode();
}
@Override
public String toString() {
return map.toString();
}
protected abstract Collection<V> newValues();
protected abstract Map<K, Collection<V>> newMap();
}

View file

@ -0,0 +1,33 @@
package org.xbib.datastructures.common;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
/**
* Linked multi map.
*
* @param <K> the key type parameter
* @param <V> the value type parameter
*/
public class LinkedHashSetMultiMap<K, V> extends AbstractMultiMap<K, V> {
public LinkedHashSetMultiMap() {
super();
}
public LinkedHashSetMultiMap(MultiMap<K, V> map) {
super(map);
}
@Override
protected Collection<V> newValues() {
return new LinkedHashSet<>();
}
@Override
protected Map<K, Collection<V>> newMap() {
return new LinkedHashMap<>();
}
}

View file

@ -0,0 +1,112 @@
package org.xbib.datastructures.common;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
*
*/
public class Maps {
private Maps() {
}
@SuppressWarnings({"unchecked"})
public static Map<String, Object> deepMerge(Map<String, Object> map, Map<String, Object> newMap) {
for (Map.Entry<String, Object> e : newMap.entrySet()) {
String key = e.getKey();
Object value = e.getValue();
if (map.containsKey(key)) {
Object originalValue = map.get(key);
if (originalValue instanceof Collection && value instanceof Collection) {
((Collection<Object>) originalValue).addAll((Collection<Object>) value);
} else if (originalValue instanceof Map && value instanceof Map) {
deepMerge((Map<String, Object>) originalValue, (Map<String, Object>) value);
}
} else {
map.put(key, value);
}
}
return map;
}
public static String getString(Map<?, ?> map, String key) {
Object object = get(map, key);
if (object instanceof List) {
return ((List<?>) object).get(0).toString();
}
if (object instanceof Map) {
return null;
}
return (String) object;
}
public static Integer getInteger(Map<?, ?> map, String key, Integer defaultValue) {
if (map.containsKey(key)) {
try {
Object o = get(map, key);
return o == null ? null : o instanceof Integer ? (Integer) o : Integer.parseInt(o.toString());
} catch (NumberFormatException e) {
return defaultValue;
}
} else {
return defaultValue;
}
}
public static Boolean getBoolean(Map<?, ?> map, String key, Boolean defaultValue) {
if (map.containsKey(key)) {
Object o = get(map, key);
return o == null ? null : o instanceof Boolean ? (Boolean) o : Boolean.parseBoolean(o.toString());
} else {
return defaultValue;
}
}
private static <T> T get(Map<?, ?> map, String key) {
return get(map, key.split("\\."));
}
@SuppressWarnings("unchecked")
private static <T> T get(Map<?, ?> map, String[] keys) {
if (map == null) {
return null;
}
String key = keys[0];
Object o = map.get(key);
if (o == null) {
return null;
}
if (!(o instanceof List)) {
o = Collections.singletonList(o);
}
List<?> list = (List<?>) o;
if (keys.length == 1) {
return (T) list.get(0);
}
for (Object oo : list) {
if (oo instanceof Map) {
Map<?, ?> m = (Map<?, ?>) oo;
if (keys.length == 2) {
if (m.containsKey(keys[1])) {
return (T) m.get(keys[1]);
}
} else {
Object ooo = get(m, Arrays.copyOfRange(keys, 1, keys.length));
if (ooo != null) {
return (T) ooo;
}
}
} else if (oo instanceof List) {
List<?> l = (List<?>) oo;
return (T) l.get(0);
} else {
return (T) oo;
}
}
return null;
}
}

View file

@ -0,0 +1,60 @@
package org.xbib.datastructures.common;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
/**
* MultiMap interface.
*
* @param <K> the key type parameter
* @param <V> the value type parameter
*/
public interface MultiMap<K, V> {
void clear();
int size();
boolean isEmpty();
boolean containsKey(K key);
Collection<V> get(K key);
Set<K> keySet();
boolean put(K key, V value);
void putAll(K key, Iterable<V> values);
void putAll(MultiMap<K, V> map);
Collection<V> remove(K key);
boolean remove(K key, V value);
Map<K, Collection<V>> asMap();
String getString(K key);
String getString(K key, String defaultValue);
Boolean getBoolean(K key, boolean defaultValue);
Short getShort(K key, short defaultValue);
Integer getInteger(K key, int defaultValue);
Long getLong(K key, long defaultValue);
Float getFloat(K key, float defaultValue);
Double getDouble(K key, double defaultValue);
BigDecimal getBigDecimal(K key, BigDecimal defaultValue);
BigInteger getBigInteger(K key, BigInteger defaultValue);
}

View file

@ -0,0 +1,26 @@
package org.xbib.datastructures.common;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import java.util.LinkedHashMap;
import java.util.Map;
/**
*
*/
public class MapsTest {
@Test
public void testDeepMerge() {
Map<String, Object> m1 = new LinkedHashMap<>();
Map<String, Object> m2 = new LinkedHashMap<>();
Map<String, Object> m3 = new LinkedHashMap<>();
Map<String, Object> m4 = new LinkedHashMap<>();
m1.put("a", "b");
m2.put("c", m1);
m3.put("d", "e");
m4.put("e", "f");
m3.put("c", m4);
assertEquals("{d=e, c={e=f, a=b}}", Maps.deepMerge(m3, m2).toString());
}
}

View file

@ -0,0 +1,13 @@
This implementation is a subset and simplified version of
https://github.com/intelie/tinymap
(master branch, as of 2020-09-12, Apache 2.0 license)
- Serialization removed
- Object reuse removed
- no TinyList
- no JSON parser
- Builder classes moved to internal classes
- multi map added

View file

@ -0,0 +1,3 @@
dependencies {
api project(':datastructures-common')
}

View file

@ -0,0 +1,4 @@
module org.xbib.datastructures.tiny {
exports org.xbib.datastructures.tiny;
requires transitive org.xbib.datastructures.common;
}

View file

@ -0,0 +1,28 @@
package org.xbib.datastructures.tiny;
import java.util.Collection;
import java.util.ListIterator;
public interface IndexedCollection<T> extends Collection<T> {
int addOrGetIndex(T obj);
void add(int index, T obj);
T set(int index, T obj);
int getIndex(Object key);
T getEntryAt(int index);
boolean removeAt(int index);
boolean isRemoved(int index);
int rawSize();
@Override
ListIterator<T> iterator();
ListIterator<T> iterator(int fromIndex);
}

View file

@ -0,0 +1,190 @@
package org.xbib.datastructures.tiny;
import java.util.AbstractCollection;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.function.Consumer;
public abstract class IndexedCollectionBase<T> extends AbstractCollection<T> implements IndexedCollection<T> {
@Override
public int getIndex(Object key) {
for (int i = 0; i < rawSize(); i++) {
if (!isRemoved(i) && Objects.equals(key, getEntryAt(i))) {
return i;
}
}
return -1;
}
@Override
public void clear() {
throw new UnsupportedOperationException("modification not supported: " + this);
}
@Override
public boolean isEmpty() {
return size() == 0;
}
@Override
public boolean contains(Object o) {
return getIndex(o) >= 0;
}
@Override
public ListIterator<T> iterator() {
return iterator(0);
}
@Override
public ListIterator<T> iterator(int fromIndex) {
return new CollectionIterator(fromIndex);
}
@Override
public void forEach(Consumer<? super T> action) {
for (int i = 0; i < rawSize(); i++) {
if (!isRemoved(i)) {
action.accept(getEntryAt(i));
}
}
}
@Override
public boolean add(T obj) {
return addOrGetIndex(obj) < 0;
}
@Override
public boolean remove(Object o) {
int index = getIndex(o);
if (index < 0) {
return false;
}
removeAt(index);
return true;
}
public interface NoAdditiveChange<T> extends IndexedCollection<T> {
@Override
default int addOrGetIndex(T obj) {
throw new UnsupportedOperationException("modification not supported: " + this);
}
@Override
default void add(int index, T obj) {
throw new UnsupportedOperationException("modification not supported: " + this);
}
@Override
default T set(int index, T obj) {
throw new UnsupportedOperationException("modification not supported: " + this);
}
}
public interface Immutable<T> extends NoAdditiveChange<T> {
@Override
default boolean removeAt(int index) {
throw new UnsupportedOperationException("modification not supported: " + this);
}
@Override
default boolean isRemoved(int index) {
return false;
}
@Override
default int rawSize() {
return size();
}
}
private class CollectionIterator implements ListIterator<T> {
private int current;
private int next;
private int prev;
public CollectionIterator(int fromIndex) {
this.current = -1;
this.next = findNext(fromIndex);
this.prev = findPrev(fromIndex - 1);
}
private int findNext(int index) {
while (index < rawSize() && isRemoved(index)) {
index++;
}
return index;
}
private int findPrev(int index) {
while (index >= 0 && isRemoved(index)) {
index--;
}
return index;
}
@Override
public boolean hasNext() {
return next < rawSize();
}
@Override
public boolean hasPrevious() {
return prev >= 0;
}
@Override
public int nextIndex() {
return next;
}
@Override
public int previousIndex() {
return prev;
}
@Override
public void set(T obj) {
Preconditions.checkState(current >= 0, "no iteration occurred");
IndexedCollectionBase.this.set(current, obj);
}
@Override
public void add(T obj) {
IndexedCollectionBase.this.add(next++, obj);
current = -1;
}
@Override
public void remove() {
Preconditions.checkState(current >= 0, "no iteration occurred");
if (removeAt(current)) {
next--;
}
current = -1;
}
@Override
public T previous() {
if (!hasPrevious()) {
throw new NoSuchElementException();
}
next = current = prev;
prev = findPrev(prev - 1);
return getEntryAt(current);
}
@Override
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
prev = current = next;
next = findNext(next + 1);
return getEntryAt(current);
}
}
}

View file

@ -0,0 +1,36 @@
package org.xbib.datastructures.tiny;
import java.util.Map;
public interface IndexedMap<K, V> extends Map<K, V> {
int getIndex(Object key);
K getKeyAt(int index);
V getValueAt(int index);
Entry<K, V> getEntryAt(int index);
V removeAt(int index);
V setValueAt(int index, V value);
boolean isRemoved(int index);
int rawSize();
Object getUnsafe(Object key, Object defaultValue);
@Override
IndexedSet<K> keySet();
@Override
IndexedSet<Map.Entry<K, V>> entrySet();
interface Entry<K, V> extends Map.Entry<K, V> {
int getIndex();
boolean isRemoved();
}
}

View file

@ -0,0 +1,352 @@
package org.xbib.datastructures.tiny;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiConsumer;
public abstract class IndexedMapBase<K, V> implements IndexedMap<K, V> {
private static final Object SENTINEL = new Object();
@Override
public V getOrDefault(Object key, V defaultValue) {
int index = getIndex(key);
if (index < 0) {
return defaultValue;
}
return getValueAt(index);
}
@Override
public V get(Object key) {
int index = getIndex(key);
if (index < 0) {
return null;
}
return getValueAt(index);
}
@Override
public boolean containsKey(Object key) {
return getUnsafe(key, SENTINEL) != SENTINEL;
}
@Override
public boolean isEmpty() {
return size() == 0;
}
@Override
public boolean containsValue(Object value) {
for (int i = 0; i < rawSize(); i++) {
if (!isRemoved(i) && Objects.equals(value, getValueAt(i))) {
return true;
}
}
return false;
}
@Override
public Entry<K, V> getEntryAt(int index) {
Preconditions.checkElementIndex(index, rawSize());
return new IndexedEntry(index);
}
@Override
public void forEach(BiConsumer<? super K, ? super V> action) {
int size = rawSize();
for (int i = 0; i < size; i++) {
if (!isRemoved(i)) {
action.accept(getKeyAt(i), getValueAt(i));
}
}
}
@Override
public V removeAt(int index) {
throw new UnsupportedOperationException("modification not supported: " + this);
}
@Override
public V setValueAt(int index, V value) {
throw new UnsupportedOperationException("modification not supported: " + this);
}
@Override
public boolean isRemoved(int index) {
return false;
}
@Override
public int rawSize() {
return size();
}
@Override
public Object getUnsafe(Object key, Object defaultValue) {
int index = getIndex(key);
if (index < 0) {
return defaultValue;
}
return getValueAt(index);
}
@Override
public V put(K key, V value) {
throw new UnsupportedOperationException("modification not supported: " + this);
}
@Override
public V remove(Object key) {
int index = getIndex(key);
if (index < 0) {
return null;
}
return removeAt(index);
}
@Override
public void clear() {
throw new UnsupportedOperationException("modification not supported: " + this);
}
@Override
public void putAll(Map<? extends K, ? extends V> m) {
m.forEach(this::put);
}
@Override
public IndexedSet<K> keySet() {
return new KeysView();
}
@Override
public Collection<V> values() {
return new ValuesView();
}
@Override
public IndexedSet<Map.Entry<K, V>> entrySet() {
return new EntriesView();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Map<?, ?>) || size() != ((Map<?, ?>) o).size()) {
return false;
}
for (Map.Entry<?, ?> entry : ((Map<?, ?>) o).entrySet()) {
if (!Objects.equals(entry.getValue(), getUnsafe(entry.getKey(), SENTINEL))) {
return false;
}
}
return true;
}
@Override
public int hashCode() {
int hash = 0;
for (int i = 0; i < rawSize(); i++) {
if (!isRemoved(i)) {
hash += Objects.hashCode(getKeyAt(i)) ^ Objects.hashCode(getValueAt(i));
}
}
return hash;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder().append('{');
boolean first = true;
for (int i = 0; i < rawSize(); i++) {
if (isRemoved(i)) {
continue;
}
if (!first) {
sb.append(", ");
}
first = false;
sb.append(getKeyAt(i)).append('=').append(getValueAt(i));
}
return sb.append('}').toString();
}
private class ValuesView extends IndexedCollectionBase<V> implements IndexedCollectionBase.NoAdditiveChange<V> {
@Override
public V getEntryAt(int index) {
return getValueAt(index);
}
@Override
public void clear() {
IndexedMapBase.this.clear();
}
@Override
public boolean removeAt(int index) {
IndexedMapBase.this.removeAt(index);
return false;
}
@Override
public boolean isRemoved(int index) {
return IndexedMapBase.this.isRemoved(index);
}
@Override
public int rawSize() {
return IndexedMapBase.this.rawSize();
}
@Override
public int size() {
return IndexedMapBase.this.size();
}
}
private class KeysView extends IndexedSetBase<K> implements IndexedCollectionBase.NoAdditiveChange<K> {
@Override
public int getIndex(Object key) {
return IndexedMapBase.this.getIndex(key);
}
@Override
public K getEntryAt(int index) {
return getKeyAt(index);
}
@Override
public void clear() {
IndexedMapBase.this.clear();
}
@Override
public boolean removeAt(int index) {
IndexedMapBase.this.removeAt(index);
return false;
}
@Override
public boolean isRemoved(int index) {
return IndexedMapBase.this.isRemoved(index);
}
@Override
public int rawSize() {
return IndexedMapBase.this.rawSize();
}
@Override
public int size() {
return IndexedMapBase.this.size();
}
}
private class EntriesView extends IndexedSetBase<Map.Entry<K, V>> implements IndexedCollectionBase.NoAdditiveChange<Map.Entry<K, V>> {
@Override
public int getIndex(Object key) {
if (!(key instanceof Map.Entry<?, ?>)) {
return -1;
}
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) key;
int index = IndexedMapBase.this.getIndex(entry.getKey());
if (index < 0 || Objects.equals(entry.getValue(), getValueAt(index))) {
return index;
}
return -1;
}
@Override
public Entry<K, V> getEntryAt(int index) {
return IndexedMapBase.this.getEntryAt(index);
}
@Override
public void clear() {
IndexedMapBase.this.clear();
}
@Override
public boolean removeAt(int index) {
IndexedMapBase.this.removeAt(index);
return false;
}
@Override
public boolean isRemoved(int index) {
return IndexedMapBase.this.isRemoved(index);
}
@Override
public int rawSize() {
return IndexedMapBase.this.rawSize();
}
@Override
public int size() {
return IndexedMapBase.this.size();
}
}
private class IndexedEntry implements Entry<K, V> {
private final int index;
public IndexedEntry(int index) {
this.index = index;
}
@Override
public K getKey() {
return getKeyAt(index);
}
@Override
public V getValue() {
return getValueAt(index);
}
@Override
public V setValue(V value) {
return setValueAt(index, value);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Map.Entry<?, ?>)) {
return false;
}
Map.Entry<?, ?> that = (Map.Entry<?, ?>) o;
return Objects.equals(that.getKey(), getKey()) && Objects.equals(that.getValue(), getValue());
}
@Override
public int hashCode() {
return Objects.hashCode(getKeyAt(index)) ^ Objects.hashCode(getValueAt(index));
}
@Override
public String toString() {
return getKey() + "=" + getValue();
}
@Override
public int getIndex() {
return index;
}
@Override
public boolean isRemoved() {
return IndexedMapBase.this.isRemoved(index);
}
}
}

View file

@ -0,0 +1,6 @@
package org.xbib.datastructures.tiny;
import java.util.Set;
public interface IndexedSet<T> extends Set<T>, IndexedCollection<T> {
}

View file

@ -0,0 +1,34 @@
package org.xbib.datastructures.tiny;
import java.util.Objects;
import java.util.Set;
public abstract class IndexedSetBase<T> extends IndexedCollectionBase<T> implements IndexedSet<T> {
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Set<?>) || size() != ((Set<?>) o).size()) {
return false;
}
for (Object obj : ((Set<?>) o)) {
if (getIndex(obj) < 0) {
return false;
}
}
return true;
}
@Override
public int hashCode() {
int hash = 0;
for (int i = 0; i < rawSize(); i++) {
if (!isRemoved(i)) {
hash += Objects.hashCode(getEntryAt(i));
}
}
return hash;
}
}

View file

@ -0,0 +1,67 @@
package org.xbib.datastructures.tiny;
public abstract class Preconditions {
public static void checkArgument(boolean expression, Object errorMessage) {
if (!expression) {
throw new IllegalArgumentException(String.valueOf(errorMessage));
}
}
public static void checkArgument(boolean expression,
String errorMessageTemplate,
Object... errorMessageArgs) {
if (!expression) {
throw new IllegalArgumentException(format(errorMessageTemplate, errorMessageArgs));
}
}
public static void checkState(boolean expression, Object errorMessage) {
if (!expression) {
throw new IllegalStateException(String.valueOf(errorMessage));
}
}
public static void checkElementIndex(int index, int size) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException(badElementIndex(index, size));
}
}
private static String badElementIndex(int index, int size) {
if (index < 0) {
return format("index (%s) must not be negative", index);
} else if (size < 0) {
throw new IllegalArgumentException("negative size: " + size);
} else {
return format("index (%s) must be less than size (%s)", index, size);
}
}
public static String format(String template, Object... args) {
template = String.valueOf(template);
StringBuilder builder = new StringBuilder(template.length() + 16 * args.length);
int templateStart = 0;
int i = 0;
while (i < args.length) {
int placeholderStart = template.indexOf("%s", templateStart);
if (placeholderStart == -1) {
break;
}
builder.append(template, templateStart, placeholderStart);
builder.append(args[i++]);
templateStart = placeholderStart + 2;
}
builder.append(template.substring(templateStart));
if (i < args.length) {
builder.append(" [");
builder.append(args[i++]);
while (i < args.length) {
builder.append(", ");
builder.append(args[i++]);
}
builder.append(']');
}
return builder.toString();
}
}

View file

@ -0,0 +1,176 @@
package org.xbib.datastructures.tiny;
import java.util.Arrays;
public abstract class TinyMap<K, V> extends IndexedMapBase<K, V> {
private final TinySet<K> keys;
protected TinyMap(TinySet<K> keys) {
this.keys = keys;
}
public static <K, V> Builder<K, V> builder() {
return new Builder<>();
}
@Override
public K getKeyAt(int index) {
return keys.getEntryAt(index);
}
@Override
public int size() {
return keys.size();
}
@Override
public int getIndex(Object key) {
return keys.getIndex(key);
}
@Override
public TinySet<K> keySet() {
return keys;
}
public static final Object TOMBSTONE = new Object() {
@Override
public String toString() {
return "TOMBSTONE";
}
};
public static class Builder<K, V> extends IndexedMapBase<K, V> {
private final TinySet.Builder<K> keys;
private Object[] values;
private Builder() {
this(16);
}
private Builder(int expectedSize) {
values = new Object[expectedSize];
keys = new TinySet.Builder<>(expectedSize) {
@Override
public void compact() {
if (size() == rawSize()) {
return;
}
int index = 0;
int rawSize = rawSize();
for (int i = 0; i < rawSize; i++) {
if (values[i] == TOMBSTONE) {
continue;
}
values[index++] = values[i];
}
Arrays.fill(values, index, rawSize, null);
super.compact();
}
};
}
public void compact() {
keys.compact();
}
public V put(K key, V value) {
int index = keys.addOrGetIndex(key);
if (index >= 0) {
return setValueAt(index, value);
}
index = ~index;
if (index >= values.length) {
values = Arrays.copyOf(values, values.length + (values.length >> 1));
}
values[index] = value;
return null;
}
@Override
public int getIndex(Object key) {
return keys.getIndex(key);
}
@Override
public K getKeyAt(int index) {
return keys.getEntryAt(index);