public void union(AbstractBaseGraph<V, E> g2) {
    if (!(this instanceof DirectedGraph<?, ?> && g2 instanceof DirectedGraph<?, ?>)) {
      return;
    }
    Set<V> vertexSet = g2.vertexSet();
    for (V vertex : vertexSet) {
      if (!containsVertex(vertex)) addVertex(vertex);
    }

    for (V vertex : vertexSet) {
      Set<E> edgeSet = g2.specifics.outgoingEdgesOf(vertex);
      for (E edge : edgeSet) {
        V sourceVertex = getEdgeSource(edge);
        V targetVertex = getEdgeTarget(edge);
        if (!allowingMultipleEdges && containsEdge(sourceVertex, targetVertex)) {
          continue;
        }

        if (!allowingLoops && sourceVertex.equals(targetVertex)) {
          continue;
        }

        if (containsEdge(edge)) {
          continue;
        } else {
          IntrusiveEdge intrusiveEdge = createIntrusiveEdge(edge, sourceVertex, targetVertex);

          edgeMap.put(edge, intrusiveEdge);
          specifics.addEdgeToTouchingVertices(edge);
        }
      }
    }
  }
  /**
   * Returns a shallow copy of this graph instance. Neither edges nor vertices are cloned.
   *
   * @return a shallow copy of this set.
   * @throws RuntimeException
   * @see java.lang.Object#clone()
   */
  public Object clone() {
    try {
      TypeUtil<AbstractBaseGraph<V, E>> typeDecl = null;

      AbstractBaseGraph<V, E> newGraph = TypeUtil.uncheckedCast(super.clone(), typeDecl);

      newGraph.edgeMap = new LinkedHashMap<E, IntrusiveEdge>();

      newGraph.edgeFactory = this.edgeFactory;
      newGraph.unmodifiableEdgeSet = null;
      newGraph.unmodifiableVertexSet = null;

      // NOTE:  it's important for this to happen in an object
      // method so that the new inner class instance gets associated with
      // the right outer class instance
      newGraph.specifics = newGraph.createSpecifics();

      Graphs.addGraph(newGraph, this);

      return newGraph;
    } catch (CloneNotSupportedException e) {
      e.printStackTrace();
      throw new RuntimeException();
    }
  }