Update HashAssociation.java, HashMultiAssociation.java, and 2 more files...
This commit is contained in:
parent
bd80576306
commit
9d30be133e
@ -136,7 +136,9 @@ public class HashAssociation<T, U> implements Association<T, U> {
|
||||
@Override
|
||||
public Set<U> getLinks(T src) {
|
||||
Objects.requireNonNull(src);
|
||||
return associations.get(src).clone();
|
||||
var dests = associations.get(src);
|
||||
if (dests == null) return Set.of();
|
||||
return dests.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,185 @@
|
||||
package org.warp.commonutils.type;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class HashMultiAssociation<T, U> implements MultiAssociation<T, U> {
|
||||
|
||||
private final Object2ObjectOpenHashMap<T, ObjectOpenHashSet<U>> associations;
|
||||
private final Object2ObjectOpenHashMap<U, ObjectOpenHashSet<T>> inverseAssociations;
|
||||
|
||||
public HashMultiAssociation() {
|
||||
this.associations = new Object2ObjectOpenHashMap<>();
|
||||
this.inverseAssociations = new Object2ObjectOpenHashMap<>();
|
||||
}
|
||||
|
||||
private HashMultiAssociation(Object2ObjectOpenHashMap<T, ObjectOpenHashSet<U>> associations,
|
||||
Object2ObjectOpenHashMap<U, ObjectOpenHashSet<T>> inverseAssociations) {
|
||||
this.associations = associations;
|
||||
this.inverseAssociations = inverseAssociations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the source from this destination
|
||||
*/
|
||||
@Nullable
|
||||
private boolean removeSourceFromDest(@NotNull U dest, @NotNull T src) {
|
||||
var sources = inverseAssociations.get(dest);
|
||||
if (sources != null) {
|
||||
sources.remove(src);
|
||||
|
||||
// Keep the map clean
|
||||
if (sources.isEmpty()) {
|
||||
inverseAssociations.remove(dest);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the destination from this source
|
||||
*/
|
||||
@Nullable
|
||||
private boolean removeDestFromSource(@NotNull T src, @NotNull U dest) {
|
||||
var dests = associations.get(src);
|
||||
if (dests != null) {
|
||||
dests.remove(dest);
|
||||
|
||||
// Keep the map clean
|
||||
if (dests.isEmpty()) {
|
||||
associations.remove(src);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean link(T src, U dest) {
|
||||
return associations.computeIfAbsent(src, s -> new ObjectOpenHashSet<>()).add(dest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unlink(T src, U dest) {
|
||||
var removed = removeDestFromSource(src, dest);
|
||||
|
||||
if (removed) {
|
||||
removeSourceFromDest(dest, src);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<U> unlink(T src) {
|
||||
var dests = associations.remove(src);
|
||||
|
||||
if (dests == null) {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
for (U dest : dests) {
|
||||
removeSourceFromDest(dest, src);
|
||||
}
|
||||
return dests;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<T> unlinkFromSource(U dest) {
|
||||
var sources = inverseAssociations.remove(dest);
|
||||
|
||||
if (sources == null) {
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
for (T source : sources) {
|
||||
removeDestFromSource(source, dest);
|
||||
}
|
||||
return sources;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyLink(T src) {
|
||||
var dests = associations.get(src);
|
||||
if (dests == null) {
|
||||
return false;
|
||||
}
|
||||
return !dests.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyLinkSource(U dest) {
|
||||
var sources = inverseAssociations.get(dest);
|
||||
if (sources == null) {
|
||||
return false;
|
||||
}
|
||||
return !sources.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLink(T src, U dest) {
|
||||
var dests = associations.get(src);
|
||||
if (dests == null) {
|
||||
return false;
|
||||
}
|
||||
return dests.contains(dest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<U> getLinks(T src) {
|
||||
Objects.requireNonNull(src);
|
||||
var dests = associations.get(src);
|
||||
if (dests == null) return Set.of();
|
||||
return dests.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<T> getLinkSources(U dest) {
|
||||
Objects.requireNonNull(dest);
|
||||
var sources = inverseAssociations.get(dest);
|
||||
if (sources == null) return Set.of();
|
||||
return sources.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
associations.clear();
|
||||
inverseAssociations.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return Math.max(associations.size(), inverseAssociations.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectSet<T> getSources() {
|
||||
return associations.clone().keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectSet<U> getDestinations() {
|
||||
return inverseAssociations.clone().keySet();
|
||||
}
|
||||
|
||||
@SuppressWarnings("MethodDoesntCallSuperMethod")
|
||||
@Override
|
||||
public HashMultiAssociation<T, U> clone() {
|
||||
var associationsClone = associations.clone();
|
||||
associationsClone.replaceAll((item, destinations) -> destinations.clone());
|
||||
|
||||
var inverseAssociationsClone = inverseAssociations.clone();
|
||||
inverseAssociationsClone.replaceAll((item, sources) -> sources.clone());
|
||||
|
||||
return new HashMultiAssociation<>(associationsClone, inverseAssociationsClone);
|
||||
}
|
||||
}
|
123
src/main/java/org/warp/commonutils/type/MultiAssociation.java
Normal file
123
src/main/java/org/warp/commonutils/type/MultiAssociation.java
Normal file
@ -0,0 +1,123 @@
|
||||
package org.warp.commonutils.type;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* One to many relationship
|
||||
*
|
||||
* o ------- o
|
||||
* +--- o
|
||||
* o ---+--- o
|
||||
* +-|- o
|
||||
* o -----|- o
|
||||
* o ------- o
|
||||
* o ---+--- o
|
||||
* +--- o
|
||||
*
|
||||
* @param <T> Source type
|
||||
* @param <U> Destination type
|
||||
*/
|
||||
public interface MultiAssociation<T, U> {
|
||||
|
||||
/**
|
||||
* Link source to dest
|
||||
* @param src Source
|
||||
* @param dest Destination
|
||||
* @return true if linked, false if it was already linked with that destination
|
||||
*/
|
||||
boolean link(T src, U dest);
|
||||
|
||||
/**
|
||||
* Unlink only if src is linked with dest
|
||||
* @param src Source
|
||||
* @param dest Destination
|
||||
* @return true if unlinked, false if not present
|
||||
*/
|
||||
boolean unlink(T src, U dest);
|
||||
|
||||
/**
|
||||
* Unlink
|
||||
* @param src Source
|
||||
* @return previous linked destinations
|
||||
*/
|
||||
Set<U> unlink(T src);
|
||||
|
||||
/**
|
||||
* Unlink
|
||||
* @param dest Destination
|
||||
* @return previous linked source
|
||||
*/
|
||||
Set<T> unlinkFromSource(U dest);
|
||||
|
||||
/**
|
||||
* Check if link exists
|
||||
* @param src Source
|
||||
* @return true if source is linked with at least 1 destination
|
||||
*/
|
||||
default boolean hasAnyLink(T src) {
|
||||
return !getLinks(src).isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if link exists
|
||||
* @param dest Destination
|
||||
* @return true if destination is linked with at least 1 destination
|
||||
*/
|
||||
default boolean hasAnyLinkSource(U dest) {
|
||||
return !getLinkSources(dest).isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if link exists
|
||||
* @param src Source
|
||||
* @param dest Destination
|
||||
* @return true if source and destination are linked together
|
||||
*/
|
||||
boolean hasLink(T src, U dest);
|
||||
|
||||
/**
|
||||
* Get a link destination
|
||||
* @param src Source
|
||||
* @return Existing linked destinations
|
||||
*/
|
||||
Set<U> getLinks(T src);
|
||||
|
||||
/**
|
||||
* Get a link source
|
||||
* @param dest Source
|
||||
* @return Existing linked sources
|
||||
*/
|
||||
Set<T> getLinkSources(U dest);
|
||||
|
||||
/**
|
||||
* Delete all links
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Get the count of existing links
|
||||
* @return size
|
||||
*/
|
||||
int size();
|
||||
|
||||
/**
|
||||
* Get all the sources
|
||||
* @return Set of sources
|
||||
*/
|
||||
ObjectSet<T> getSources();
|
||||
|
||||
/**
|
||||
* Get all the destinations
|
||||
* @return Set of destinations
|
||||
*/
|
||||
ObjectSet<U> getDestinations();
|
||||
|
||||
static <T, U> MultiAssociation<T, U> synchronize(MultiAssociation<T, U> association) {
|
||||
return new SynchronizedMultiAssociation<>(association);
|
||||
}
|
||||
|
||||
static <T, U> MultiAssociation<T, U> synchronize(MultiAssociation<T, U> association, Object lock) {
|
||||
return new SynchronizedMultiAssociation<>(association, lock);
|
||||
}
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
package org.warp.commonutils.type;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class SynchronizedMultiAssociation<T, U> implements MultiAssociation<T, U> {
|
||||
|
||||
private final MultiAssociation<T, U> association;
|
||||
private final Object lock;
|
||||
|
||||
SynchronizedMultiAssociation(MultiAssociation<T, U> association) {
|
||||
this.association = association;
|
||||
this.lock = new Object();
|
||||
}
|
||||
|
||||
SynchronizedMultiAssociation(MultiAssociation<T, U> association, Object lock) {
|
||||
this.association = association;
|
||||
this.lock = lock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean link(T src, U dest) {
|
||||
synchronized (lock) {
|
||||
return association.link(src, dest);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unlink(T src, U dest) {
|
||||
synchronized (lock) {
|
||||
return association.unlink(src, dest);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<U> unlink(T src) {
|
||||
synchronized (lock) {
|
||||
return association.unlink(src);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<T> unlinkFromSource(U dest) {
|
||||
synchronized (lock) {
|
||||
return association.unlinkFromSource(dest);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyLink(T src) {
|
||||
synchronized (lock) {
|
||||
return association.hasAnyLink(src);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnyLinkSource(U dest) {
|
||||
synchronized (lock) {
|
||||
return association.hasAnyLinkSource(dest);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLink(T src, U dest) {
|
||||
synchronized (lock) {
|
||||
return association.hasLink(src, dest);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<U> getLinks(T src) {
|
||||
synchronized (lock) {
|
||||
return association.getLinks(src);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<T> getLinkSources(U dest) {
|
||||
synchronized (lock) {
|
||||
return association.getLinkSources(dest);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
synchronized (lock) {
|
||||
association.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
synchronized (lock) {
|
||||
return association.size();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectSet<T> getSources() {
|
||||
synchronized (lock) {
|
||||
return association.getSources();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectSet<U> getDestinations() {
|
||||
synchronized (lock) {
|
||||
return association.getDestinations();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user