简体   繁体   中英

Optimization: replace for loop with ListIterator

It's my first working on a quite big project, and I've been asked to obtain the best performances.

So I've thouhgt to replace my for loops with a ListIterator , because I've got around 180 loops which call list.get(i) on lists with about 5000 elements.

So I've got two questions.

1) Are those 2 snippets equal? I mean, do them produce the same output? If no, how can I correct the ListIterator thing?

ListIterator<Corsa> ridesIterator = rides.listIterator();
    while (ridesIterator.hasNext()) {
        ridesIterator.next();
        Corsa previous = ridesIterator.previous(); //rides.get(i-1)
        Corsa current = ridesIterator.next(); //rides.get(i)
        if (current.getOP() < d.getFFP() && previous.getOA() > d.getIP() && current.wait(previous) > DP) {
            doSomething();
            break;
        }
    }

__

for (int i = 1; i < rides.size(); i++) {
    if (rides.get(i).getOP() < d.getFP() && rides.get(i - 1).getOA() > d.getIP() && rides.get(i).getOP() - rides.get(i - 1).getOA() > DP) {
        doSomething();
            break;
        }
    }

2) How will it be the first snippet if I've got something like this? (changed i and its exit condition)

for (int i = 0; i < rides.size() - 1; i++) {
    if (rides.get(i).getOP() < d.getFP() && rides.get(i + 1).getOA() > d.getIP() && rides.get(i).getOP() - rides.get(i + 1).getOA() > DP) {
        doSomething();
            break;
        }
    }

I'm asking because it's the first time that I'm using a ListIterator and I can't try it now!

EDIT: I'm not using an ArrayList, it's a custom List based on a LinkedList

EDIT 2 : I'm adding some more infos. I can't use a caching system because my data is changing on evry iteration and managing the cache would be hard as I'd have to deal with inconsistent data. I can't even merge some of this loops into one big loop, as I've got them on different methods because they need to do a lot of different things.

So, sticking on this particular case, what do you think is the best pratice? Is ListIterator the best way to deal with my case? And how can I use the ListIterator if my for loop works between 0 and size-1 ?

If you know the maximum size, you will get the best performance if you resign from collections such as ArrayList replacing them with simple arrays.

So instead creating ArrayList<Corsa> with 5000 elements, do Corsa[] rides = new Corsa[5000] . Instead of hard-coding 5000 use it as final static int MAX_RIDES = 5000 for example, to avoid magic number in the code. Then iterate with normal for, referring to rides[i] .

Generally if you look for performance, you should code in Java, as if it was C/C++ (of course where you can). The code is not so object-oriented and beautiful, but it's fast. Remember to do optimization always in the end, when you are sure, you have found a bottleneck. Otherwise, your efforts are futile, only making the code less readable and maintainable. Also use a profiler , to make sure your changes are in fact upgrades, not downgrades.

Another downside of using ListIterator is that it internally allocates memory. So GC (Garbage Collector) will awake more often, which also can have impact on the overall performance.

  1. No they do not do the same.

     while (ridesIterator.hasNext()) { ridesIterator.next(); Corsa previous = ridesIterator.previous(); //rides.get(i-1) Corsa current = ridesIterator.next(); //rides.get(i) 

    The variables previous and current would contain the same "Corsa" value, see the ListIterator documentation for details (iterators are "in between" positions).

    The correct code would look as follows:

     while (ridesIterator.hasNext()) { Corsa previous = ridesIterator.next(); //rides.get(i-1) if(!ridesIterator.hasNext()) break; // We are already at the last element Corsa current = ridesIterator.next(); //rides.get(i) ridesIterator.previous(); // going back 1, to start correctly next time 
  2. The code would actually look exactly the same, only the interpretation (as shown in the comments) would be different:

     while (ridesIterator.hasNext()) { Corsa previous = ridesIterator.next(); //rides.get(i) if(!ridesIterator.hasNext()) break; // We are already at the last element Corsa current = ridesIterator.next(); //rides.get(i+1) ridesIterator.previous(); // going back 1, to start correctly next time 

From a (premature?) optimization viewpoint the ListIterator implementation is better.

  • LinkedList is a doubly-linked list which means that each element links to both its predecessor (previous) as well as its successor (next). So it does 3 referals per loop. => 3*N
  • Each get(i) needs to go through all previous elements to get to the i index position. So on average N/4 referals per loop. (You'd think N/2, but LinkedList starts from the beginning or the end of the list.) => 2 * N * N/4 == N^2 /2

Here are some suggestions, hopefully one or two will be applicable to your situation.

  1. Try to do only one rides.get(x) per loop.
  2. Cache method results in local variables as appropriate for your code.

In some cases the compiler can optimize multiple calls to the same thing doing it just once instead, but not always for many subtle reasons. As a programmer, if you know for a fact that these should deliver the same values, then cache them in local variables.

For example,

int sz = rides.size ();
float dFP = d.getFP ();  // wasn't sure of the type, so just called if float..
float dIP = d.getIP ();
Corsa lastRide = rides.get ( 0 );
for ( int i = 1; i < sz; i++ ) {
    Corsa = rides.get ( i );
    float rOP = r.getOP ();
    if ( rOP < dFP ) {
        float lastRideOA = lastRide.getOA (); // only get OA if rOP < dFP
        if ( lastRideOA > dIP && rOP - lastRideOA > DP ) {
            doSomething ();
            // maybe break;
        }
    }
    lastRide = r;
}

These are optimizations that may not work in all cases. For example, if your doSomething expands the list, then you need to recompute sz, or maybe go back to doing rides.size() each iteration. These optimizations also assumes that the list is stable in that the elements don't change during the get..() 's. If doSomething makes changes to the list, then you'd need to cache less. Hopefully you get the idea. You can apply some of these techniques to the iterator form of the loop as well.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM