6

I am searching for a list data structure in Java which allows cheap appending of long lists. I tried LinkedList but I found in the documentation of addAll, that an iterator is used to append the two lists. This means that the list, which gets appended, gets cloned during the operation. The iterator walks though the whole list returning every single element. Is there any collection available which omits the iteration while appending two lists?

ceving
  • 21,900
  • 13
  • 104
  • 178
  • You mean you want structure sharing between the lists? – Fred Foo Oct 10 '12 at 12:02
  • I'm no expert in Java, but I don't think that fits the Java collections API, so you'll want to look for third-party libraries (or roll your own). – Fred Foo Oct 10 '12 at 12:03
  • 1
    This question is related to yours: http://stackoverflow.com/questions/2237308/merge-two-lists-in-constant-time-in-java, there is no analogue to STL's list::splice method in any of JDK collections – bobah Oct 10 '12 at 12:04
  • 1
    If the resultant list needs to be read-only, take a look at: http://stackoverflow.com/q/4896662/100836 – Zaki Oct 10 '12 at 12:07
  • @Zaki: Hmmm this is the second time I hit [Guava](http://code.google.com/p/guava-libraries/). The first time I was able to solve the problem without requiring Guava. But this time it seems to be hard. – ceving Oct 10 '12 at 12:15
  • @bobah: Oh my question seems to be a dup. – ceving Oct 10 '12 at 12:51

10 Answers10

6

You can use Guava's Iterables.concat method to create concatenated Iterable View..

Iterable<T> combined = Iterables.concat(list1, list2);
  • This does not copy elements from one list to another..
  • So, it does not change any of your lists..
  • Also, this does not create a new list (It creates an Iterable which is not a list)

Basically it creates an Iterable through which you can iterate over the two lists back to back (It iterates elements from list1 then from list2..

NOTE: - If you want a list as a concatenation of the two lists, then this might not help you much.. Because, it does not create a list, but an Iterable.. For that case, you have no other choice than Iterating over your lists and copy each of your reference..

From the docs: -

It Combines two iterables into a single iterable. The returned iterable has an iterator that traverses the elements in a, followed by the elements in b. The source iterators are not polled until necessary. The returned iterable's iterator supports remove() when the corresponding input iterator supports it.

You also have a var-args version of this method.. See Docs.. This can take any number of lists, and returns Iterables that can iterate over those lists in order.. So, you can do like this..

Iterable<T> combined = Iterables.concat(list1, list2, list3, list4, ...);

This link --> google-guava-libraries-essentials might also be of interest to you..

Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
  • indeed faster... is it O(n)? It has to find the end of one list – UmNyobe Oct 10 '12 at 12:09
  • Indeed this is even faster. It does not have to find the end.. It already has the tow lists.. Concatenation is O(1).. – Rohit Jain Oct 10 '12 at 12:14
  • You can also combine `multiple` lists using this approach.. See Var-arg version of this method: - http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Iterables.html#concat(java.lang.Iterable...) – Rohit Jain Oct 10 '12 at 12:16
  • 2
    Is it really necessary to use Guava? It sounds strange to me that the standard libraries of Java do not provide such a simple functionality. Even [Glib](http://developer.gnome.org/glib/stable/glib-Singly-Linked-Lists.html#g-slist-concat) has it. – ceving Oct 10 '12 at 12:28
  • @ceving.. Well they do provide.. They have `addAll` methods, and they have many more methods to work with multiple collections.. But jsut to make these operations more efficient, Third party libraries are created.. Else they won't even exists.. You can easily use them, and get your work done.. – Rohit Jain Oct 10 '12 at 12:30
  • @ceving.. Another example of Third-Party library is [Joda-Time](http://joda-time.sourceforge.net/), which makes your life easier like heaven when it comes t working with Date.. And it might also get integrated with Java 8.. – Rohit Jain Oct 10 '12 at 12:30
  • @ceving.. And that is something new you have given me.. `GLib`.. Wasn't aware of that.. Thanks :) – Rohit Jain Oct 10 '12 at 12:31
  • 1
    Its is worth nothing that an `Iterable` is not a "list data structure". To turn it into one you would have to copy all the elements so it doesn't really save you anything. – Peter Lawrey Oct 10 '12 at 13:54
  • @PeterLawrey.. Yeah that thing I have added as one of my 3 points in my post.. It does not create a new list.. It is just helpful, if you want to iterate over multiple lists.. – Rohit Jain Oct 10 '12 at 13:55
  • @RohitJain I use a nested loop to iterate over multiple lists, but I am not as "functional" as I could be. ;) – Peter Lawrey Oct 10 '12 at 13:59
  • @PeterLawrey.. Well, if what all one want is a new list containing all items from the two lists, then iterating over them through nested loop is the best bet we can have.. :) – Rohit Jain Oct 10 '12 at 14:03
  • @PeterLawrey.. But if number of lists increases, that will drive you Crazy.. won't it.. ;) – Rohit Jain Oct 10 '12 at 14:04
  • If your lists are not too big though, because of cache locality, you will probably be worse off than just copying. See https://www.youtube.com/watch?v=Y4XkWSAm2XU – Agoston Horvath Dec 02 '16 at 09:53
5

Not really, since all "append" operations don't make any assumptions about the underlying collections. Technically two linked lists could be appended directly, but the append has to be generic so it uses iteration.

Another good reason to not allow direct concatenation is the fact that after the append changing one list will also affect the other, which I'm sure is not a desirable property.

Tudor
  • 61,523
  • 12
  • 102
  • 142
4

addAll in ArrayList converts it to an Array and then uses a system call to copy (System.arraycopy). This should be quicker than looping through in java as it is native, I dont think there is a cheap appender.

RNJ
  • 15,272
  • 18
  • 86
  • 131
3

May be addAll of ArrayList should be quicker because it doesn't iterate , but use System.arrayCopy.The code looks like

public boolean addAll(Collection<? extends E> c) {
    Object[] a = c.toArray();
        int numNew = a.length;
    ensureCapacity(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
    return numNew != 0;
    }
sakthisundar
  • 3,278
  • 3
  • 16
  • 29
1

This means that the list, which gets appended, gets cloned during the operation.

Creating an iterator doesn't clone the collection.

The addAll method in most cases calls toArray() to ensure the data is extracted atomically which clones the elements of the collection as an array and this is likely to use an Iterator to do it.

Is there any collection available which omits the iteration while appending two lists?

No, but you can iterate the collection yourself if this is really important.


The most efficient is likely to be

List<E> list1 = ... random access list ...
List<E> list2 = ... random access list ...

for(int i = 0; i < list2.size(); i++)
    list1.add(list2.get(i));

This doesn't create any objects and has O(n) time where n is the size of list2.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • I don't want to iterate and I don't want to copy. – ceving Oct 10 '12 at 12:23
  • 1
    You have to copy the references at a minimum. You can do that with a loop. – Peter Lawrey Oct 10 '12 at 12:28
  • @ceving 'don't want *this* and don't want *that*' :) So *what* do you really want to achieve? – Durandal Oct 10 '12 at 12:42
  • @Durandal: Fast concatenation. And fast means: no copy and no loop. – ceving Oct 10 '12 at 12:47
  • The only way to do that would be to create a custom List which is a view on the two original Lists. It can be done but gets increasingly complicated with more than two lists. – Peter Lawrey Oct 10 '12 at 12:51
  • @ceving You have to chose between *no copy/loop* and *concatination*. As multiple answers already explained you can't have both with a *List*. You can wish for it, but don't expect reality to oblige :) – Durandal Oct 10 '12 at 13:02
  • Sorry this is quibble. Rohit Jain gave the correct answer: Java is not able to do it and it is necessary to use a third library like Guava. – ceving Oct 10 '12 at 13:22
  • @ceving Its worth nothing that an Iterable is not a `list data structure` and to turning it into one requires copying every reference. – Peter Lawrey Oct 10 '12 at 13:58
0

Even if you do find such a collection class you couldn't be guaranteed that it will continue to work that way in future versions of Java. That's because this is a behavior that's not specified in the Collection interface and is therefore subject to the whim of the class developer.

You could, of course, write your own collection class, but even then you'd be making assumptions about the behavior of other Collection classes

Chris Gerken
  • 16,221
  • 6
  • 44
  • 59
0

Default implementation of addAll method in AbstractCollections uses iterator to add but addAll method is overridden in sub classes which provides custom specific implementation

Like

  • ArrayList uses System.arraycopy
  • LinkedList uses a Loop to traverse through elements.
Amit Deshpande
  • 19,001
  • 4
  • 46
  • 72
0

I think there is no way with the standard API, but depending on your needs you would try to implement one by yourself. For example, you can implement one which internally has a List of collection references, added every time you call the addAll method.

0

with that behavior the list added to the first list will get consumed (i.e. modified) or results in the second list becoming a sublist of the bigger one with all kinds of gotchas (like what happens if you add a list twice...),

the addAll has an implicit const argument (meaning that the second list will remain unmodified over several calls), so this behavior won't exist unless you roll your own

ratchet freak
  • 47,288
  • 5
  • 68
  • 106
0

I would simply take ArrayList, and happily addAll() my stuff to it. It doesn't really matter how the chosen List implementation performs the addAll() operation, iteration or toArray are both relatively cheap and its very unlikely this will be a performance bottleneck (that assuming your application does useful work with the list elements and not just create lists...).

There is really no way around the iteration in addAll(), if you want an independent list that contains the elements from N source lists, the element references have to be copied somewhere (you could change the source lists after addAll(), but that must not affect the conactinated list, thus a copy is unavoidable).

If you do not really need the List semantics (independend of the sources), then make use of the already suggested Guava to create a view of multiple lists (or roll your own, its not rocket science if you don't want the dependency).

Durandal
  • 19,919
  • 4
  • 36
  • 70