0

I need to pre-populate a List with a large number of integer values.

Is there are faster way to do this other than iteration?

Current Code:

class VlanManager {
    Queue<Integer> queue = Lists.newLinkedList();

    public VlanManager(){
    for (int i = 1; i < 4094; i++) {
        queue.add(i);
    }
}

This code is in the constructor of a class that is created pretty frequently so I'd like this to be as efficient (read:performance not lines of code) as possible

Dave Tucker
  • 181
  • 1
  • 7
  • I don't think there is any other way – Kyle Emmanuel Jul 19 '14 at 23:04
  • 1
    You could try creating an immediate array and using asList, but I'd profile it under the actual circumstances you need. I'd generate the array code programmatically, unless you really like typing. – Dave Newton Jul 19 '14 at 23:05
  • 9
    4094? Don't worry, you will be fine ... – Jean Logeart Jul 19 '14 at 23:05
  • Use an `ArrayList` instead – But I'm Not A Wrapper Class Jul 19 '14 at 23:07
  • `ArrayList` isn't an option as I need to use the Queue interface. – Dave Tucker Jul 19 '14 at 23:10
  • perhaps an [`ArrayDeque`](http://docs.oracle.com/javase/7/docs/api/java/util/ArrayDeque.html) then? – vandale Jul 19 '14 at 23:13
  • @DaveTucker is `4094` the actual number of elements you want to initialize the quoe with? If it is, you shouldn't worry that much about performance. – Christian Tapia Jul 19 '14 at 23:14
  • Yes. Seems the current consensus is that 4094 is not a big deal I'm happy with that... This is in the constructor of a class that is created pretty frequently so I'm trying to avoid bottlenecks – Dave Tucker Jul 19 '14 at 23:18
  • What do you want to do with this queue? – thertweck Jul 19 '14 at 23:23
  • Is it *always* these values? t – ChiefTwoPencils Jul 19 '14 at 23:26
  • This queue is used to represent VLAN assignments... when something needs a VLAN a poll of a Queue yields the first free value. A subsequent call to remove is issued when the VLAN is no longer required. Yes it's always between 1 and 4094 – Dave Tucker Jul 19 '14 at 23:37
  • "This is in the constructor of a class that is created pretty frequently" That was the most important information that should have been presented originally. It is never worth worrying about the performance of code elements that execute thousands or millions of times, because the time is barely even measurable. But it is always worth worrying about the performance of those code elements that execute billions, trillions, or quadrillions of times. So if you're calling this loop of thousands of iterations millions of times, then yeah, you have an issue; otherwise, no. – Boann Jul 19 '14 at 23:48
  • 1
    You should desribe more clearly what you are going to do with these numbers, and what the (potentially) performance-critical part is. – Marco13 Jul 20 '14 at 01:18

3 Answers3

4

4094 isnt to many items to loop but if it is getting called very frequently you might look at doing something with a static variable.

private static Integer[] theList;

static {
    theList = new Integer[4094];
    for (int i = 1; i < 4094; i++) {
        theList[i-1] = i;
    }
}

then make that list a List

Queue<Integer> intQue = new LinkedList(Arrays.asList(theList));

There is a danger of using this method if you have a list of mutable objects. Heres an example of what can happen. Integers are immutable so this doesnt actually apply to your question as it stands

class MyMutableObject {
    public int theValue;
}

class Test {

    private static MyMutableObject[] theList;

    static {
        theList = new MyMutableObject[4094];
        for (int i = 1; i <= 4094; i++) {
            theList[i-1] = new MyMutableObject();
            theList[i-1].theValue = i;
        }
    }

    public static void main(String [] args) {
        Queue<MyMutableObject> que = new LinkedList(Arrays.asList(theList));
        System.out.println(que.peek().theValue); // 1
        // your actually modifing the same object as the one in your static list
        que.peek().theValue = -100; 
        Queue<MyMutableObject> que2 = new LinkedList(Arrays.asList(theList));
        System.out.println(que2.peek().theValue); // -100
    }
}

@Bohemian Has some good points on using a static List instead of an array, while the performance gains are very small they are none the less performance gains. Also because the 'array' is actually only ever being used as a List not an array it should be declared as such.

private static List<Integer> theList;

static {
    theList = new ArrayList(4094);
    for (Integer i = 0; i < 4094; i++) {
        theList.add(i+1);
    }
}
ug_
  • 11,267
  • 2
  • 35
  • 52
  • That's what I was just about to do. – ChiefTwoPencils Jul 19 '14 at 23:30
  • That's better than what I had for sure! But I need a mutable object so I don't think this will work for my use case. – Dave Tucker Jul 19 '14 at 23:58
  • @DaveTucker So your not using integers? – ug_ Jul 20 '14 at 00:24
  • @DaveTucker It's ambiguous but by "immutable objects" I believe ug_ means the ones in the list (the `Integer`s), not the object that contains the list/queue/thing, which can of course be mutable. So this code will work fine. – Boann Jul 20 '14 at 09:52
  • @Boann your right my mention of mutable objects in my awnser was a vague, updated. – ug_ Jul 20 '14 at 10:20
4

The fastest way would be to create a reference list (initialized using an instance block - neatly wrapping it all up in one statement):

private static final List<Integer> LIST = new ArrayList<Integer>(4094) {{
    for (int i = 1; i < 4094; i++)
       LIST.add(i);
}};

Then in your constructor, initialize the queue using the copy constructor:

Queue<Integer> queue;

public VlanManager(){
    queue = new LinkedList<Integer>(LIST);
}

You will not write a faster implementation than what's in the JDK.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • A nice advantage here is avoiding changing `int` to `Integer` for all of the 4094 values each time an object is created. – Brett Okken Jul 20 '14 at 02:49
  • This is basically the same answer that @ug_ gave, except you changed the simple static block to the double-brace idiom. I'm not really impressed. – Boann Jul 20 '14 at 09:44
  • @Boann actually no. The two answers have some similarities, but the big difference is in the initialuzation of the queue (the main topic of the question): the other answer takes an array, creates (and initialized) a List, then uses that List to initialize the queue. My answer skips the intermediate object and goes straight to the JDK's (fast) List copy constructor. Another important difference is my reference list contains Integer objects, which don't need converting, but the other answer initialized he reference list with inf primitives that must be wrapped as Integers every time. – Bohemian Jul 20 '14 at 10:16
  • @Bohemian "the other answer takes an array, creates (and initialized) a List, then uses that List to initialize the queue" The creation of the LinkedList already creates 4094 Node objects every time. The creation of 1 tiny additional asList wrapper object is negligible compared to that. "Another important difference is my reference list contains Integer objects, which don't need converting, but the other answer initialized he reference list with inf primitives that must be wrapped as Integers every time" That's not true. They are Integers. – Boann Jul 20 '14 at 10:57
1

I realize this question has already been answered. But I think one important answer is missing: The fastest way to initialize a LinkedList with the values 0..4093 is .. DON'T DO IT AT ALL. Especially if speed is an issue.

What you basically are doing is creating a structure consisting of 4093 Node elements each consiting of two pointers to prev/next element and one pointer to an Integer object. Each of this Nodes must be created (and free). In addition nearly each contained Integer must be created (and freed). 'Nearly' because Java uses a cache for Integer but normally (you can change this with system properties) in the range of -127..127.

This is a lot to do in order to get a simple list of integer and if used intensively gives the GC a lot to do afterwards.

That being said there are numerous possible ways of doing this in a more efficient way. But they depend on what your concrete usage pattern is. Just to name a few:

  1. Use an Array: boolean [] inUse' and set the taken vlan-id totrue` if it's taken
  2. Even better use a BitSet instead of the array
  3. Don't store which vlan is free, but which vlan is taken. I think they tend to be free and so there are much more free as there are taken ones. (this means much less to keep track of).
  4. If you insist on using a LinkedList don't initialize it with your class but have it already initialized. This depends on how much of them you would need. You could keep a pool of them. Or perhaps your codes allows reusage of old lists. (yes, you could sort them after usage.)

Surely there are more...

All of this methods require you to build your own 'Queue' interface. But perhaps this has not to be as rich as Java's. And it really isn't that difficult. If you really use this intensively you could reach perfomance improvement factor 10x-1000x++.

A possible implementation using BitSet with an instantiation cost of nearly nothing could be:

import java.util.BitSet;

import org.testng.annotations.Test;


public class BitSetQueue {

    // Represents the values 0..size-1
    private final BitSet bitset;
    private final int size;
    private int current = 0;
    private int taken = 0;

    public BitSetQueue( int size ){

        this.bitset = new BitSet( size );
        this.size = size;
        this.current = size-1;
    }

    public int poll(){

        // prevent endless loop
        if( taken == size ) return -1;

        // seek for next free value.
        // can be changed according to policy
        while( true ){

            current = (current+1)%size;

            if( ! bitset.get( current ) ){
                bitset.set( current );
                taken++;
                return current;
            }
        }
    }

    public boolean free( int num ){

        if( bitset.get( num ) ){
            bitset.clear( num );
            taken--;
            return true;
        }
        return false;
    }


    @Test
    public static void usage(){

        BitSetQueue q = new BitSetQueue( 4094 );

        for( int i = 0; i < 4094; i++ ){
            assertEquals( q.poll(), i );
        }
        assertEquals( q.poll(), -1 ); // No more available

        assertTrue( q.free( 20 ) );
        assertTrue( q.free( 51 ) );

        assertEquals( q.poll(), 20 );
        assertEquals( q.poll(), 51 );
    }

}
Scheintod
  • 7,953
  • 9
  • 42
  • 61
  • I agree with your point about the Node objects, but the simplest solution to that is to use an `ArrayDeque` in place of the `LinkedList`. – Boann Jul 20 '14 at 11:01
  • Admittedly the `ArrayDeque` will avoid the creation of the Nodes. But it still uses an backing long[4094] array (which can be allocated in one piece) and about 4000 newly to create (and free) integer objects. But all we need are 4094 bits (if at all) (This could be improved by increasing Javas integer cache or by keeping one's own) but most importantly it is missing an `ArrayDeque( Object[] )` constructor which could at least use System.arrayCopy or something to speed things up. – Scheintod Jul 20 '14 at 11:22