org.helenus.commons.collections.graph.ConcurrentHashDirectedGraph Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of helenus-commons Show documentation
Show all versions of helenus-commons Show documentation
JPA-like syntax for annotating POJO classes for persistence via Cassandra's Java driver - Common Utilities
/*
* Copyright (C) 2015-2015 The Helenus Driver Project Authors.
*
* 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 org.helenus.commons.collections.graph;
import java.io.Serializable;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.Spliterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.helenus.commons.collections.DirectedGraph;
/**
* The ConcurrentHashDirectedGraph
class provides an implementation
* of the {@link DirectedGraph} interface that uses a {@link ConcurrentHashMap}
* to keep track of the graph.
*
* @copyright 2015-2015 The Helenus Driver Project Authors
*
* @author The Helenus Driver Project Authors
* @version 1 - Jan 15, 2015 - paouelle - Creation
*
* @param the type of elements in this graph
*
* @since 2.0
*/
public class ConcurrentHashDirectedGraph
implements DirectedGraph, Cloneable, Serializable {
/**
* Holds the serialVersionUID.
*
* @author paouelle
*/
private static final long serialVersionUID = -8430194904167408569L;
/**
* The HashNode
class provides an implementation for a graph node.
*
* @copyright 2015-2015 The Helenus Driver Project Authors
*
* @author The Helenus Driver Project Authors
* @version 1 - Jan 15, 2015 - paouelle - Creation
*
* @since 2.0
*/
private class HashNode implements Node, Cloneable, Serializable {
/**
* Holds the serialVersionUID.
*
* @author paouelle
*/
private static final long serialVersionUID = 2377326682367937940L;
/**
* Holds the value for this node.
*
* @author paouelle
*/
private final T value;
/**
* Holds the edges from this node.
*
* @author paouelle
*/
@SuppressWarnings("synthetic-access")
private final HashSet edges
= new HashSet<>(Math.min(initialCapacity / 16, 8), loadFactor);
/**
* Instantiates a new HashNode
object.
*
* @author paouelle
*
* @param value the value for this node.
*/
HashNode(T value) {
this.value = value;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see org.helenus.commons.collections.DirectedGraph.Node#getValue()
*/
@Override
public T getValue() {
return value;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see org.helenus.commons.collections.DirectedGraph.Node#edgeExists(java.lang.Object)
*/
@Override
public boolean edgeExists(T dest) {
@SuppressWarnings("synthetic-access")
final HashNode node = graph.get(dest);
if (node == null) {
throw new NoSuchElementException("unknown destination node");
}
return edges.contains(node);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see org.helenus.commons.collections.DirectedGraph.Node#getEdges()
*/
@Override
public Set> getEdges() {
return Collections.unmodifiableSet(edges);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see org.helenus.commons.collections.DirectedGraph.Node#edges()
*/
@Override
public Stream> edges() {
return edges.stream().map(n -> n);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see org.helenus.commons.collections.DirectedGraph.Node#add(java.lang.Object)
*/
@Override
public void add(T dest) {
HashNode node = new HashNode(dest);
@SuppressWarnings("synthetic-access")
final HashNode old = graph.putIfAbsent(dest, node);
if (old != null) { // we already had one so continue with it
node = old;
}
edges.add(node);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see org.helenus.commons.collections.DirectedGraph.Node#add(java.util.stream.Stream)
*/
@Override
public void add(Stream dests) {
dests.forEach(d -> add(d));
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see org.helenus.commons.collections.DirectedGraph.Node#addEdge(java.lang.Object)
*/
@Override
public void addEdge(T dest) {
@SuppressWarnings("synthetic-access")
final HashNode node = graph.get(dest);
if (node == null) {
throw new NoSuchElementException("unknown destination node");
}
edges.add(node);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see org.helenus.commons.collections.DirectedGraph.Node#removeEdge(java.lang.Object)
*/
@Override
public boolean removeEdge(T dest) {
@SuppressWarnings("synthetic-access")
final HashNode node = graph.get(dest);
if (node == null) {
throw new NoSuchElementException("unknown destination node");
}
return edges.remove(node);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return Objects.hashCode(value);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
return obj == this;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return edges.stream()
.map(
e -> String.valueOf(e.getValue())
)
.collect(Collectors.joining("-", value + "=", ""));
}
}
/**
* Holds the load factor for this graph.
*
* @author paouelle
*/
private final float loadFactor;
/**
* Holds the initial capacity for this graph.
*
* @author paouelle
*/
private final int initialCapacity;
/**
* Holds the graph.
*
* @author paouelle
*/
private final Map graph;
/**
* Holds the node set view of this graph.
*
* @author paouelle
*/
private transient Set> nodeSet = null;
/**
* Instantiates a new ConcurrentHashDirectedGraph
object with
* the default initial capacity (16) and the default load factor (0.75).
*
* @author paouelle
*/
public ConcurrentHashDirectedGraph() {
this(16, 0.75f);
}
/**
* Instantiates a new ConcurrentHashDirectedGraph
object with the
* specified initial capacity and the default load factor (0.75).
*
* @param initialCapacity the initial capacity
* @throws IllegalArgumentException if the initial capacity is negative
*/
public ConcurrentHashDirectedGraph(int initialCapacity) {
this(initialCapacity, 0.75f);
}
/**
* Instantiates a new ConcurrentHashDirectedGraph
object with the
* specified initial capacity and load factor.
*
* @param initialCapacity the initial capacity
* @param loadFactor he load factor
* @throws IllegalArgumentException if the initial capacity is negative
* or the load factor is non-positive
*/
public ConcurrentHashDirectedGraph(int initialCapacity, float loadFactor) {
this.loadFactor = loadFactor;
this.initialCapacity = initialCapacity;
this.graph = new ConcurrentHashMap<>(initialCapacity, loadFactor);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see java.util.Set#size()
*/
@Override
public int size() {
return graph.size();
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see java.util.Set#isEmpty()
*/
@Override
public boolean isEmpty() {
return graph.isEmpty();
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see java.util.Set#contains(java.lang.Object)
*/
@Override
public boolean contains(Object o) {
return graph.containsKey(o);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see java.util.Set#iterator()
*/
@Override
public Iterator iterator() {
final Iterator> i = graph.entrySet().iterator();
return new Iterator() {
Map.Entry current = null;
@Override
public boolean hasNext() {
return i.hasNext();
}
@Override
public T next() {
this.current = i.next();
return current.getKey();
}
@Override
@SuppressWarnings("synthetic-access")
public void remove() {
org.apache.commons.lang3.Validate.validState(current != null);
i.remove();
// we need to remove all edges to the removed node
graph.values().stream()
.forEach(
n -> n.edges.remove(current)
);
this.current = null;
}
};
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see java.util.Collection#stream()
*/
@Override
public Stream stream() {
return graph.keySet().stream();
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see java.util.Collection#parallelStream()
*/
@Override
public Stream parallelStream() {
return graph.keySet().parallelStream();
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see java.lang.Iterable#forEach(java.util.function.Consumer)
*/
@Override
public void forEach(Consumer super T> action) {
graph.keySet().forEach(action);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see java.util.Set#toArray()
*/
@Override
public Object[] toArray() {
return graph.keySet().toArray();
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see java.util.Set#toArray(java.lang.Object[])
*/
@Override
public A[] toArray(A[] a) {
return graph.keySet().toArray(a);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see org.helenus.commons.collections.DirectedGraph#get(java.lang.Object)
*/
@Override
public Node get(T val) {
return graph.get(val);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see java.util.Set#add(java.lang.Object)
*/
@Override
public boolean add(T e) {
return graph.putIfAbsent(e, new HashNode(e)) == null;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see java.util.Set#remove(java.lang.Object)
*/
@Override
@SuppressWarnings("synthetic-access")
public boolean remove(Object o) {
final HashNode node = graph.remove(o);
if (node != null) {
// we need to remove all edges to the removed node
graph.values().stream()
.forEach(
n -> n.edges.remove(node)
);
return true;
}
return false;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see java.util.Set#containsAll(java.util.Collection)
*/
@Override
public boolean containsAll(Collection> c) {
return graph.keySet().containsAll(c);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see java.util.Set#addAll(java.util.Collection)
*/
@Override
public boolean addAll(Collection extends T> c) {
boolean added = false;
for (final T e: c) {
if (add(e)) {
added = true;
}
}
return added;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see java.util.Set#retainAll(java.util.Collection)
*/
@Override
public boolean retainAll(Collection> c) {
boolean removed = false;
for (final Iterator i = iterator(); i.hasNext(); ) {
if (!c.contains(i.next())) {
i.remove();
removed = true;
}
}
return removed;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see java.util.Set#removeAll(java.util.Collection)
*/
@Override
@SuppressWarnings("synthetic-access")
public boolean removeAll(Collection> c) {
boolean removed = false;
for (final Iterator> i = graph.entrySet().iterator(); i.hasNext(); ) {
final Map.Entry e = i.next();
if (c.contains(e.getKey())) {
i.remove(); // remove the whole node
removed = true;
} else { // remove all edges to elements of c
for (final Iterator j = e.getValue().edges.iterator(); j.hasNext(); ) {
if (c.contains(j.next().getValue())) { // remove the edge
j.remove();
removed = true;
}
}
}
}
return removed;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see java.util.Set#clear()
*/
@Override
public void clear() {
graph.clear();
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see org.helenus.commons.collections.DirectedGraph#nodeSet()
*/
@Override
@SuppressWarnings("synthetic-access")
public Set> nodeSet() {
final Set> ns;
return (ns = nodeSet) == null ? (this.nodeSet = new NodeSet()) : ns;
}
@SuppressWarnings("javadoc")
private final class NodeSet extends AbstractSet> {
@Override
@SuppressWarnings("synthetic-access")
public final int size() {
return graph.size();
}
@Override
public final void clear() {
ConcurrentHashDirectedGraph.this.clear();
}
@Override
public final Iterator> iterator() {
@SuppressWarnings("synthetic-access")
final Iterator> i = graph.entrySet().iterator();
return new Iterator>() {
Map.Entry current = null;
@Override
public boolean hasNext() {
return i.hasNext();
}
@Override
public Node next() {
this.current = i.next();
return current.getValue();
}
@Override
@SuppressWarnings("synthetic-access")
public void remove() {
org.apache.commons.lang3.Validate.validState(current != null);
i.remove();
// we need to remove all edges to the removed node
graph.values().stream()
.forEach(
n -> n.edges.remove(current)
);
this.current = null;
}
};
}
@Override
public final boolean contains(Object o) {
if (!(o instanceof Node)) {
return false;
}
return contains(((Node>)o).getValue());
}
@Override
public final boolean remove(Object o) {
if (o instanceof Node) {
return remove(((Node>)o).getValue());
}
return false;
}
@Override
@SuppressWarnings({"rawtypes", "cast", "unchecked", "synthetic-access"})
public final Spliterator> spliterator() {
return (Spliterator>)(Spliterator)graph.values().spliterator();
}
@Override
@SuppressWarnings("synthetic-access")
public final void forEach(Consumer super Node> action) {
graph.values().forEach(action);
}
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see org.helenus.commons.collections.DirectedGraph#add(java.lang.Object, java.lang.Object)
*/
@Override
public void add(T start, T dest) {
HashNode sn = new HashNode(start);
final HashNode old = graph.putIfAbsent(start, sn);
if (old != null) { // we already had one so continue with it
sn = old;
}
sn.add(dest);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see org.helenus.commons.collections.DirectedGraph#add(java.lang.Object, java.util.stream.Stream)
*/
@Override
public void add(T start, Stream dests) {
HashNode sn = new HashNode(start);
final HashNode old = graph.putIfAbsent(start, sn);
if (old != null) { // we already had one so continue with it
sn = old;
}
sn.add(dests);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see org.helenus.commons.collections.DirectedGraph#addEdge(java.lang.Object, java.lang.Object)
*/
@Override
public void addEdge(T start, T dest) {
final HashNode sn = graph.get(start);
if (sn == null) {
throw new NoSuchElementException("unknown starting node");
}
sn.addEdge(dest);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see org.helenus.commons.collections.DirectedGraph#removeEdge(java.lang.Object, java.lang.Object)
*/
@Override
public boolean removeEdge(T start, T dest) {
final HashNode sn = graph.get(start);
if (sn == null) {
throw new NoSuchElementException("unknown starting node");
}
return sn.removeEdge(dest);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see org.helenus.commons.collections.DirectedGraph#edgeExists(java.lang.Object, java.lang.Object)
*/
@Override
public boolean edgeExists(T start, T dest) {
final HashNode sn = graph.get(start);
if (sn == null) {
throw new NoSuchElementException("unknown starting node");
}
return sn.edgeExists(dest);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see org.helenus.commons.collections.DirectedGraph#edgesFrom(java.lang.Object)
*/
@Override
public Stream edgesFrom(T node) {
final HashNode n = graph.get(node);
if (n == null) {
throw new NoSuchElementException("unknown node");
}
return n.edges()
.map(e -> e.getValue());
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see org.helenus.commons.collections.DirectedGraph#getEdgesFrom(java.lang.Object)
*/
@Override
public Set getEdgesFrom(T node) {
final HashNode n = graph.get(node);
if (n == null) {
throw new NoSuchElementException("unknown node");
}
return Collections.unmodifiableSet(
n.getEdges().stream()
.map(e -> e.getValue())
.collect(Collectors.toSet())
);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return super.hashCode();
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
/**
* Returns a shallow copy of this ConcurrentHashDirectedGraph
* instance: the values themselves are not cloned.
*
* @return a shallow copy of this graph
*/
@Override
public Object clone() {
final ConcurrentHashDirectedGraph clone
= new ConcurrentHashDirectedGraph<>(initialCapacity, loadFactor);
clone.addAll(graph.keySet());
clone.nodeSet().forEach(
n -> graph.get(n.getValue()).edges()
.forEach(
e -> n.addEdge(e.getValue())
)
);
return clone;
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return graph.values()
.stream()
.map(
n -> n.edges()
.map(
e -> (e.getValue() == this) ? "(this graph)" : String.valueOf(e.getValue())
)
.collect(Collectors.joining(
"-", (n.getValue() == this) ? "(this graph)" : String.valueOf(n.getValue()) + '=', "")
)
)
.collect(Collectors.joining(", ", "{", "}"));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy