-1

I've got a graph. This graph consists of a node class that has the following:

public abstract class AbstractNode{

    public Collection<AbstractNode> predecessors;
    public Collection<AbstractNode> successors;

    //accept methods for visitors
}

I recently added a component that allows you to import configuration from a YAML file. This component reads the YAML file and generates a graph using this node class. This code is fairly slow and makes lots of operations on the graph, including for-eaching through some predecessors, analyzing various nodes, and making decisions about how other nodes successors and/or predecessors should be updated.

Our existing code has a service that polls this same graph once every 100ms to generate a list of errors in our program, using a visitor. A visitor is effectively a class of objects that's going to for-each through every single list of successors and predecessors once.

This is causing concurrent modification exceptions. :(

My choices appear to be:

  • update the polling service to be event driven.
    • We switched from an event driven strategy to a polling one because the complexity of the events was getting very high. There is a lot of subtle timing issues involved in this.
    • Best for 'purity' of model.
  • update the problem polling service and my new yaml-importer to both synchronize on a thread work-queue (the obvious choice being the UI thread since they've already got queue-based implementations)
    • more dependency on UI framework, if the UI framework isn't available (and our application does operate in a headless command-line mode), then what?
  • Implement collections that use closeable-iterables that lock their source until either a close() call is made or hasNext() returns false. This would allow us to either block the writer until the readers complete.
    • clooge on iterables. Probably fairly safe since nobody actually uses iterators anymore, but still it is an odd use of the contract.
  • update the AbstractNode to use CopyOnWriteArrayCollection as its backing sets/lists of predecessors and successors
    • replaces on-boarding friendly POJO List<String> predecessors = new ArrayList<>() code with List<String> predecessors = new CopyOnWriteArrayList<>()
    • requires additional serializer configuration and/or produces nasty XML/JSON
  • Separate the read from the write model by pushing the graph-mutation operations onto the AbstractNode class, and replace the getSuccesors/Precessors to return a defensive copy and/or an immutable collection (such as those in PCollections)
    • requires a great deal of code to solve what is conceptually a very small problem
    • requires updating a huge number of places that are consuming this graph expecting standard-mutable-collections to instead use the methods on the AbstractNode

What I really want is for a collections framework that is built around composition rather than around inheritance.

To turn this into a rant for a second, and with the benefit of (lots) of hindsight, its pretty well recognized that when faced with the choice of creating abstract classes and multiple implementations of them, or one class that delegates to dependencies (read: is composed of its dependencies), the latter is universally preferable.

Is there no library to allow me to do something like

private final Collection<AbstractNode> predecessors = Collections.create(
    DuplicationPolicies.ForbidDuplicates,
    Orders.InsertionOrder,
    Concurrency.ThrowOnConcurrentModification
);

for a LinkedHashSet<> and

private final Collection<AbstractNode> predecessors = Collections.create(
    DuplicationPolicies.AllowDuplicates,
    Orders.InsertionOrder,
    Concurrency.CopyOnWrite
);

for a CopyOnWriteArrayList<>?

With such a system, it would (presumably) be reasonably simple for me to switch from a concurrency-averse implementation (such as an ArrayList) to a concurrency-tolerant one (such as a CopyOnWrite list or a Shared/Exclusive-locking list).

Groostav
  • 3,170
  • 1
  • 23
  • 27
  • 2
    *"I was very disappointed in java in trying to build solutions to this problem."* ... I'm sure I don't know what you're asking us. Java is a fine platform for concurrent development as any number of successfully developed highly concurrent projects can attest. What you need to do is develop sensible multithreading policies for the classes that you intend to use to solve your problem ... and you haven't told us anything about what those are. That the Collections Framework classes are, themselves, not thread safe is relatively untimportant. – scottb Jul 28 '15 at 05:32
  • Adding to what @scottb said, there is no algorithm that you can express in some other language, but not express in Java. You might have to write more lines of Java than you would have to write in some other language, but if it can be implemented in any language, it can be implemented in Java. It all gets compiled down to the same machine code in the end. – Solomon Slow Jul 28 '15 at 12:59
  • I'm asking 2 things: are the strategies I'm proposing really all there is? Is there some fundamental class that I'm missing? Secondly: is there some library (perhalps a more recent version of jakarta or more complete google-guava-collections) that would allow me to compose collection behaviour? – Groostav Jul 28 '15 at 22:53
  • -1 for ranting. But it sounds like what you're asking for is an implementation of [persistent data structures](http://en.wikipedia.org/wiki/Persistent_data_structure) in Java? In that case, see this question: [what's a good persistent collections framework for use in java?](http://stackoverflow.com/questions/8575723/whats-a-good-persistent-collections-framework-for-use-in-java) – Daniel Pryden Jul 28 '15 at 23:02
  • and regarding Java being turing complete @jameslarge, I know, but the same can be said of assembly language. It is not so much a question of feasibility, but rather expressiveness and comprehension. Whatever solution I implement I want to be obvious, not something that somebody would come along and think 'why are we using an XYZ here when we use ABC everywhere else?' – Groostav Jul 28 '15 at 23:05
  • @DanielPryden thank you for that, that's something I should've thought about from the start. I'm a big proponent of immutable collections elsewhere, I should've simply been one here. Unfortunately this code has several hundred consumers that all expect to have a getter that returns a mutable collection. Modifying it to use immutable collections would be quite an exercise. – Groostav Jul 28 '15 at 23:27

1 Answers1

1

There is no already made composite collections in java, but you can easily implement them by hands using standard java collections and some additional objects as members of your class.

As you always use Orders.InsertionOrder policy, list-like base collections are most suitable for you. You can use CopyOnWriteArrayList as base collection for implement Concurrency.CopyOnWrite policy, and simple ArrayList for implement Concurrency.ThrowOnConcurrentModification.

For DuplicationPolicies additional object with single bool canAddElem(collection, elem) method is sufficient.

Something like that:

public class CompositeCollection extends Collection<AbstractNode>
{
    private Collection<AbstractNode> baseCollection;
    private filter;

    public CompositCollection(DuplicationPolicies dPolicy, Concurrency cPolicy)
    {
        if(dPolicy == DuplicationPolicies.AllowDuplicates)
        {
            filter = new AllowDuplicatesFilter();
        }
        else
        {
            filter = new ForbidDuplicatesFilter();
        }
        if(cPolicy == Concurrency.Concurrency.CopyOnWrite)
        {
            baseCollection = new CopyOnWriteArrayList<AbstractNode>();
        }
        else
        {
            baseCollection = new ArrayList<AbstractNode>();
        }
    }

    public bool add(AbstractNode node)
    {
        if(!filter->canAddElem(baseCollection, node)) {return false; }

        return baseCollection->add(node);
    }

    ...
};
Tsyvarev
  • 60,011
  • 17
  • 110
  • 153
  • I also had this thought (what self-respecting programmer doesnt have the 'well I'll do it myself!' thought at first), but I think you're underestimating the amount of work that needs to be done. Firstly I very much suspect that some very serious big-design work would need to be done to determine exactly what the interfaces and enums should be. Ideally I would have one `Collection` class that has a list of handlers/listeners for each event, rather than having to switch on enum objects. – Groostav Jul 29 '15 at 19:32
  • It is always hard to make design well-suited for a great amount of users. Actually, I doesn't see necessity of composite collections here: you have all properties of collection already implemented, and want collection only to be thread safe. There is no standard collection, suited for you needs, so you need to implement it by yourself. No needs for composite pattern in case of single additional property. – Tsyvarev Jul 29 '15 at 22:17