views:

786

answers:

9

Say I have a java.util.List list and I want to create a new List by adding an element e to the beginning of list (i.e., I want to cons e and list). For example, if list is

[1,2,3,4]

and e is 5, then cons(e,list) will be

[5,1,2,3,4]

It's OK for the elements of list and cons(e,list) to be shared, but list should not be modified.

What is the simplest and/or most efficient way to implement cons? It's OK for the result to be unmodifiable. Use of the Google Collections Library is allowed.

What if list is a com.google.common.collect.ImmutableList?

+2  A: 

Could you use a CompositeCollection?

public Collection cons(Collection c1, Collection c2)
{
    CompositeCollection cons = new CompositeCollection();
    cons.addComposited(c1);
    cons.addComposited(c2);
    return cons;
}

This would not be affected by whether or not one of the parameters is immutable and is still backed by the original collections c1 and c2.

If you need a List I would probably do the following:

public List cons(Collection c1, Collection c2)
{
    ArrayList cons = new ArrayList(c1.size() + c2.size());
    cons.addAll(c1);
    cons.addAll(c2);
    return cons;
}
Alex B
Is the resulting Collection a List?
Chris Conway
+8  A: 
public static<T> List<T> cons(List<T> list, T t) {
    ArrayList<T> result = new ArrayList<T>(list);
    result.add(0, t);
    return result;
}

Edited in response to comments: Since the question asked for "the simplest and/or most efficient way to implement cons," I went with "simplest". I wouldn't be surprised to learn there are more efficient ways. Putting the element in before the list is another valid approach, and allocating the correct size initially could probably improve performance. Premature optimization is the root of all evil.

Carl Manaster
Does anyone know how this performs in practice? Per the Java API: 'Inserts the specified element at the specified position in this list. Shifts the element currently at that position (if any) and any subsequent elements to the right (adds one to their indices). ' Does this mean that the elements of 'list' are only iterated over once?
tehblanx
Woops. Forgot to mention that I'm quoting the documentation for the 'add(int index, Object o)' method
tehblanx
It's probably more efficient to create the ArrayList with the require capacity, add the first element and then addAll the list. But I think the original question wanted the element added to the end of the list, where it doesn't matter so much.
Tom Hawtin - tackline
(No, the original question just had the list and the element the sensible way around.)
Tom Hawtin - tackline
This will iterate over the elements of the list TWICE; once to copy everything from list to result, and again to shift the elements in add. Using a LinkedList and addFirst eliminates the second array traversal.
Adam Jaskiewicz
Wouldn't it be more efficient to use LinkedList instead of ArrayList. add(0, obj) with ArrayList is probably O(N) operation (shifting the whole array one step forward), whereas with LinkedList it would be O(1) operation?
Juha Syrjälä
@Juha, probably it would be more efficient. ArrayList is the list implementation I use until I have a reason to choose another. @Tom Hawtin's suggestion is probably more efficient still. I went with simple.
Carl Manaster
@Carl ArrayList is what I usually use, but this particular application I would immediately go for a LinkedList. Of course, I suppose without knowing how the list will be used, it's rather academic, so I've removed my -1. I'd argue that for lisp-like uses, a LinkedList is more appropriate.
Adam Jaskiewicz
+4  A: 

Clojure provides that kind of Lisp-y stuff. While most people think of using Clojure for the language (like I do), the Clojure libraries are all real Java code, and you can use the data structures from Java as just a special library if you'd like. That way you'd get the ability to do cons and such, and you get the immutability that Clojure uses. The Clojure data strcutres implement the equivalent Java types too.

Just a thought from a different direction.

MBCook
An idea whose time will certainly come. STMs may in fact be Clojure's more lasting contribution to the JVM ecosphere.
+1  A: 

I'm going to throw my 2 cents in, then see if anybody comes up with anything more elegant. In the general case:

<E> List<E> cons(E e, List<E> list) {
    List<E> res = Lists.newArrayListWithCapacity(list.size() + 1);
    res.add(e);
    res.addAll(list);
    return res;
}

With an ImmutableList (don't know how efficient this is):

<E> ImmutableList<E> cons(E e, ImmutableList<E> list) {
    return ImmutableList.<E>builder()
                        .add(e)
                        .addAll(list)
                        .build();
}
Chris Conway
Is `Lists.newArrayListWithExpectedSize` really easier to type than `new ArrayList<E>`?
Michael Myers
I guess, it's about avoiding possible resize operations. But you could use http://java.sun.com/javase/6/docs/api/java/util/ArrayList.html#ArrayList(int). The above code, however, is dealing with 'expected size'. Don't know what's the difference to instantiating with a 'known' capacity.
msiemeri
@mmyers: In this case, no, but I maintain the convention of using static methods in Lists to instantiate my lists whenever possible. It's usually a win.
Chris Conway
@msiemeri: Good catch. "Expected size" is different from "capacity", and I wanted the latter.
Chris Conway
@mmyers: `Lists.new*` saves you from writing the generic parameter twice when declaring/initializing a variable.
Willi
@Willi: Yes, but at the cost of more typing, it seems.
Michael Myers
What about: `List<SomeGenericFancyLongTypeName<WithTypeParameter>> list = new ArrayList<SomeGenericFancyLongTypeName<WithTypeParameter>>(100);` compared to `List<SomeGenericFancyLongTypeName<WithTypeParameter>> list = newArrayListWithExpectedSize(100);`?
Willi
+1  A: 

Surely a LinkedList would be the most efficient way of inserting an item at the head of a list?

Just use the LinkedList class that comes with Java

Fortyrunner
You're assuming that the cost of copying the original list into a LinkedList is negligible.
Chris Conway
You didn't specify the List implementation. I have used LinkedList on some occasions where the initial cost of creation wasn't an issue but subsequent inserts *were* an issue.
Fortyrunner
+1  A: 

Because you mention the cons function, I will assume that you're approaching this problem with the conceptual model of linked lists composed of cons cells. Specifically, I assume that you're thinking of each list having a car (the first element) and a cdr (the sublist containing all following elements).

Java supports linked lists as java.util.LinkedList. These are good for linear traversal and can have elements inserted very efficiently. These are most similar to the linked lists I mentioned above.

Java also offers java.util.ArrayList. Lists of this kind are good for random access, but can be slow when inserting elements. In fact, they are slowest when inserting an element at the beginning of the list. Because ArrayLists are implemented as arrays behind the scenes, every element must be copied one position forward in the list to make room for the new first element. Now, if you also happen to need a bigger array, the ArrayList will allocate a new array, copy all the elements over, and so on.

(Google's ImmutableList is cited as "random-access", so it is likely more similar to the latter.)

If you plan to use your cons method often, I recommend using it with linked lists. A cons operation is essentially adding an element at the beginning of a list. This makes little sense with linear structures such as arrays. I recommend against using array lists for this reason: that they're conceptually wrong for the job.

For the very picky: because a new list is returned each time cons is called, copying must occur whether the list is a LinkedList or an ArrayList. However, the entire principal of a cons operation is that it is operating on a linked list.

public <E> LinkedList<E> cons(E car, List<E> cdr) {
    LinkedList<E> destination = new LinkedList<E>(cdr);
    destination.addFirst(car);
    return destination;
}

Note that the above code was written after reading the above answers, so I apologize for any accidental plagiarism. Let me know if you see it and I'll properly acknowledge.

Assuming you're happy with returning a LinkedList, you can use ImmutableList as the cdr in this example.

Wesley
Use destination.addFirst(car) instead of destination.add(0, car). It won't make a huge difference in performance (likely some, though) but I think it's a lot clearer.
Adam Jaskiewicz
That's much better to look at - thanks! I had been looking at the java.util.List interface while writing that snippet, so I missed it.
Wesley
A: 

Another variation if you have only Iterable.

public static <E> List<E> cons(E e, Iterable<E> iter) {
   List<E> list = new ArrayList<E>();
   list.add(e);
   for(E e2: iter) list.add(e2);
   return list;
}

public static <E> List<E> cons(Iterable<E>... iters) {
   List<E> list = new ArrayList<E>();
   for(Iterable<E> iter: iters) for(E e1: iter1) list.add(e1);
   return list;
}
Peter Lawrey
+3  A: 

I believe the answer you're really looking for is this:

http://functionaljava.googlecode.com/svn/artifacts/2.20/javadoc/fj/data/List.html

The method is even called cons.

I have no experience with this library. Just heard of it the other day. I hope it's good!

Kevin Bourrillion
Functional Java looks pretty cool, but I'm already relying pretty heavily on Google Collections and am not in the market for a new library dependency :-(
Chris Conway
Awwww... ditch that Google crap then.
Kevin Bourrillion
+1  A: 

An efficient solution would be to create your own List implementation, lazily delegating. If unmodifiable, it wouldn't be too much trouble. If created through a factory method, then you could even use one of two different underlying implementations depending on whether the argument List implements RandomAccess or not.

For the simplest case (haven't checked to see whether this compiles, no sanity checks, etc.):

class ConsList<E> extends AbstractList<E>
{
    private final E first;
    private final List<E> rest;

    ConsList( E first, List<E> rest )
    {
        this.first = first;
        this.rest = rest;
    }

    public int get( int index )
    {
        return (index == 0) ? first : rest.get( index - 1 );
    }

    public int size()
    {
        return rest.size() + 1;
    }
}

I'm sure a few of the other methods could be made more efficient, and the sequential case would be better served by extending AbstractSequentialList instead.

Ray Conner