![JAR search and dependency download from the Maven repository](/logo.png)
com.github.protobufel.multikeymap.BaseMultiKeyMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of multikeymapjava Show documentation
Show all versions of multikeymapjava Show documentation
Java 8 implementation of the multi-key map. It behaves like a regular generic Map with the additional ability of getting its values by any combination of partial keys.
The newest version!
/*
* Copyright 2017 David Tesler
*
* 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.
*
*/
package com.github.protobufel.multikeymap;
import java.io.IOException;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Stream;
import static com.github.protobufel.multikeymap.Collectors.intersectSets;
class BaseMultiKeyMap, V> implements MultiKeyMap, Serializable {
private static final long serialVersionUID = 995884597801625434L;
static boolean enableParallelStreaming = false;
/**
* The base map holding all the Map data
*
* @serial
*/
private Map fullMap;
private transient LiteSetMultimap partMap;
private transient Set keySet;
private transient Collection values;
private transient Set> entrySet;
BaseMultiKeyMap() {
this(new HashMap<>(), LiteSetMultimap.newInstance());
}
BaseMultiKeyMap(final Map sourceMap) {
this(new HashMap<>(Objects.requireNonNull(sourceMap)), LiteSetMultimap.newInstance());
}
BaseMultiKeyMap(final Map fullMap, final LiteSetMultimap partMap) {
super();
this.fullMap = Objects.requireNonNull(fullMap);
this.partMap = Objects.requireNonNull(partMap);
}
private void writeObject(java.io.ObjectOutputStream out)
throws IOException {
out.writeObject(fullMap);
out.writeBoolean(partMap.isConcurrent());
}
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject();
final boolean concurrent = in.readBoolean();
partMap = LiteSetMultimap.newInstance(concurrent);
fullMap.forEach((k, v) -> putPartial(k));
}
private void readObjectNoData()
throws ObjectStreamException {
fullMap = new HashMap<>();
partMap = LiteSetMultimap.newInstance();
}
static boolean isEnableParallelStreaming() {
return enableParallelStreaming;
}
static void setEnableParallelStreaming(final boolean enableParallelStreaming) {
BaseMultiKeyMap.enableParallelStreaming = enableParallelStreaming;
}
@Override
public String toString() {
return fullMap.toString();
}
// TODO remove or re-enable after comparing the performance w/ the Java 8 based
// implementation
// @Override
// public Optional> getFullKeysByPartialKey(final Iterable extends T> partialKey) {
// Objects.requireNonNull(partialKey);
//
// if (partMap.isEmpty()) {
// return Optional.empty();
// }
//
// final List> subResults = new ArrayList<>();
// int minSize = Integer.MAX_VALUE;
// int minPos = -1;
//
// for (final T subKey : partialKey) {
// final Set subResult = partMap.get(Objects.requireNonNull(subKey));
//
// if (subResult.size() == 0) {
// return Optional.empty();
// } else if (subResult.size() < minSize) {
// minSize = subResult.size();
// minPos = subResults.size();
// }
//
// subResults.add(subResult);
// }
//
// if (subResults.isEmpty()) {
// return Optional.empty();
// }
//
// final Set result = new HashSet<>(subResults.get(minPos));
//
// if (subResults.size() == 1) {
// return Optional.of(result.stream());
// }
//
// for (int i = 0; i < subResults.size(); i++) {
// if (i != minPos) {
// if (result.retainAll(subResults.get(i)) && result.isEmpty()) {
// return Optional.empty();
// }
// }
// }
//
// return Optional.of(result.stream());
// }
@Override
public Stream getFullKeysByPartialKey(final Iterable extends T> partialKey) {
Objects.requireNonNull(partialKey);
if (partMap.isEmpty()) {
return Stream.empty();
}
if (!(partialKey instanceof Set)) {
return getFullKeysByPartialKey(partialKey, Collections.emptyList());
}
// Java 8 doesn't allow to break the processing and also discourages stateful functions
// untill Java 9 takeWhile!
// final Set> sets = ((Set extends T>) partialKey).stream().unordered()
// .map(subKey -> partMap.get(Objects.requireNonNull(subKey))).collect(toSet());
final List> sets = new ArrayList<>();
for (final T subKey : partialKey) {
final Set set = partMap.get(Objects.requireNonNull(subKey));
if (set == null) {
return Stream.empty();
}
sets.add(set);
}
if (sets.isEmpty()) {
return Stream.empty();
}
final Set result = intersectSets(sets, isEnableParallelStreaming());
return result.isEmpty() ? Stream.empty() : result.stream();
}
@Override
public int size() {
return fullMap.size();
}
@Override
public boolean isEmpty() {
return fullMap.isEmpty();
}
@Override
public boolean containsKey(final Object key) {
return fullMap.containsKey(key);
}
@Override
public boolean containsValue(final Object value) {
return fullMap.containsValue(value);
}
@Override
public V get(final Object key) {
return fullMap.get(Objects.requireNonNull(key));
}
@Override
public V put(final K key, final V value) {
Objects.requireNonNull(value);
final Object[] oldValue = {null};
fullMap.compute(key, (k, v) -> {
if (v == null) {
putPartial(k);
} else {
oldValue[0] = v;
}
return value;
});
@SuppressWarnings("unchecked") final V oldV = (V) oldValue[0];
return oldV;
}
@Override
public V remove(final Object key) {
@SuppressWarnings("unchecked") final K fullKey = (K) key;
final Object[] oldValue = {null};
fullMap.computeIfPresent(fullKey, (k, v) -> {
deletePartial(k);
oldValue[0] = v;
return null;
});
@SuppressWarnings("unchecked") final V oldV = (V) oldValue[0];
return oldV;
}
@Override
public void putAll(final Map extends K, ? extends V> m) {
for (final Map.Entry extends K, ? extends V> entry : Objects.requireNonNull(m).entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
@Override
public void clear() {
fullMap.clear();
partMap.clear();
}
@Override
public boolean equals(final Object o) {
if (o == this) {
return true;
}
if (!(o instanceof Map)) {
return false;
}
return fullMap.equals(o);
}
@Override
public int hashCode() {
return fullMap.hashCode();
}
private void putPartial(final K key) {
for (final T subKey : key) {
partMap.put(subKey, key);
}
}
private void deletePartial(final K key) {
for (final T subKey : key) {
partMap.remove(subKey, key);
}
}
@Override
public Collection values() {
if (values == null) {
values = new Values();
}
return values;
}
@Override
public Set keySet() {
if (keySet == null) {
keySet = new KeySet();
}
return keySet;
}
@Override
public Set> entrySet() {
if (entrySet == null) {
entrySet = new EntrySet();
}
return entrySet;
}
final class Values extends AbstractCollection {
@Override
public Iterator iterator() {
return new ValueIterator(fullMap.entrySet().iterator());
}
@Override
public int size() {
return fullMap.size();
}
@Override
public Spliterator spliterator() {
return fullMap.values().spliterator();
}
@Override
public void forEach(final Consumer super V> action) {
fullMap.values().forEach(action);
}
@Override
public boolean contains(final Object o) {
return fullMap.values().contains(o);
}
@Override
public void clear() {
BaseMultiKeyMap.this.clear();
}
}
final class ValueIterator implements Iterator {
private final Iterator> it;
private Entry current;
public ValueIterator(final Iterator> it) {
super();
this.it = it;
}
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public V next() {
current = it.next();
return current.getValue();
}
@Override
public void remove() {
it.remove();
deletePartial(current.getKey());
}
}
final class KeySet extends AbstractSet {
@Override
public Iterator iterator() {
return new KeySetIterator(fullMap.keySet().iterator());
}
@Override
public int size() {
return fullMap.size();
}
@Override
public Spliterator spliterator() {
return fullMap.keySet().spliterator();
}
@Override
public void forEach(final Consumer super K> action) {
fullMap.keySet().forEach(action);
}
@Override
public boolean contains(final Object o) {
return fullMap.keySet().contains(o);
}
@Override
public boolean remove(final Object o) {
if (fullMap.keySet().remove(o)) {
@SuppressWarnings("unchecked") final K key = (K) o;
deletePartial(key);
return true;
}
return false;
}
@Override
public void clear() {
BaseMultiKeyMap.this.clear();
}
}
final class KeySetIterator implements Iterator {
private final Iterator it;
private K current;
public KeySetIterator(final Iterator it) {
super();
this.it = it;
}
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public K next() {
current = it.next();
return current;
}
@Override
public void remove() {
it.remove();
deletePartial(current);
}
}
final class EntrySet extends AbstractSet> {
@Override
public Iterator> iterator() {
return new EntrySetIterator(fullMap.entrySet().iterator());
}
@Override
public int size() {
return fullMap.size();
}
@Override
public Spliterator> spliterator() {
return fullMap.entrySet().spliterator();
}
@Override
public void forEach(final Consumer super Entry> action) {
fullMap.entrySet().forEach(action);
}
@Override
public boolean contains(final Object o) {
return fullMap.entrySet().contains(o);
}
@Override
public boolean remove(final Object o) {
if (fullMap.entrySet().remove(o)) {
@SuppressWarnings("unchecked") final Entry entry = (Entry) o;
deletePartial(entry.getKey());
return true;
}
return false;
}
@Override
public void clear() {
BaseMultiKeyMap.this.clear();
}
}
final class EntrySetIterator implements Iterator> {
private final Iterator> it;
private Entry current;
public EntrySetIterator(final Iterator> it) {
super();
this.it = it;
}
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public Entry next() {
current = it.next();
return current;
}
@Override
public void remove() {
it.remove();
deletePartial(current.getKey());
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy