views:

653

answers:

12

I am currently rediscovering Java (working with Ruby a lot recently), and I love the compilation-time checking of everything. It makes refactoring so easy. However, I miss playing fast-and-loose with types to do an each loop. This is my worst code.

Is this as short as it can be? I have a collection called looperTracks, which has instances that implement Looper. I don't want to modify that collection, but I want to iterate through its members PLUS the this (which also implements Looper).

List<Looper> allLoopers = new ArrayList<Looper>(looperTracks.length + 1);
for (LooperTrack track : looperTracks) {
    allLoopers.add(track);
}
allLoopers.add(this);

for (Looper looper : allLoopers) {
    // Finally! I have a looper

I'm particularly concerned about any features that are new to Java from 1.5 on that I may have missed. For this question I am not asking about JRuby nor Groovy, though I know that they would work for this.

Edit: Sorry (too much Ruby!)... looperTracks is of type LooperTrack[] and LooperTrack implements Looper.

+6  A: 

There are at least two possible built-in ways to shorten your code:

You could use Collection.addAll(Collection) that appends each element in the collection passed as parameter to the end of the collection.:

List<Looper> allLoopers = new ArrayList<Looper>();

...

allLoopers.addAll(looperTracks);
allLoopers.add(this);

for(Looper looper : allLoopers) {
  ...
}

or you can use a constructor that takes a collection as a parameter:

List<Looper> allLoopers = new ArrayList<Looper>(looperTracks);

Due to the change of question: All arrays can easily be converted to collections using java.util.Arrays e.g.

List<Looper> someLooperTracks = Arrays.asList(looperTracks). 

This will wrap the array in a fixed-size list.

Aleksi
I changed the question due to a mistake on my part, but the first answer will definitely work.
Yar
Cool, thanks for picking up the change in the question. I had just realized it, too: `List<Looper> allLoopers = new ArrayList<Looper>(Arrays.asList(looperTracks));`
Yar
and you can use the nCopies() method (see my answer below)
lorenzog
If you don't need to modify the list (and just want a view on the data), you should definitely use Arrays.asList over the other methods.
Stefan Kendall
+8  A: 

You could at least use the fact that you can construct one collection using another as the base values. According to the docs:

Constructs a list containing the elements of the specified collection, in the order they are returned by the collection's iterator. The ArrayList instance has an initial capacity of 110% the size of the specified collection.

Which means that there is probably going to be room forthis without having to do any resizing.

List<Looper> allLoopers = new ArrayList<Looper>(looperTracks);
allLoopers.add(this);

for (Looper looper : allLoopers) {
    // Finally! I have a looper
Yacoby
+1, I'm not sure how many seconds faster than me you were, but good job!
ninesided
Sorry sorry I messed up the question, looperTracks is of type LooperTrack[] and LooperTrack implements Looper.
Yar
+1  A: 

Why can't you just add them all as part of the constructor call?

List<Looper> allLoopers = new ArrayList<Looper>(looperTracks);

allLoopers.add(this);

for(Looper looper : allLoopers) {
...
}
ninesided
Yeah, that worked with a bit of modification. Thanks!
Yar
+1  A: 

Depending on typing, you could use:

List<Looper> allLoopers = new ArrayList<Looper>(looperTracks);
allLoopers.add(this);

or:

List<Looper> allLoopers = new ArrayList<Looper>(looperTracks.length + 1);
allLoopers.addAll(looperTracks);
allLoopers.add(this);
rsp
+1  A: 

I don't think you can make it shorter than this...

for (Looper looper : new ArrayList<Looper>(){{ addAll(looperTracks); add(EnclosingClass.this); }}) {
    // Finally! I have all loopers
}
Viktor Klang
-1 Every time someone posts this hack, I feel obligated to call it out as the terrible, ugly hack that it is. And you've managed to work in *two* of the most obscure language features -- instance initializers, and qualified references to `this`! Good job confusing all your coworkers. Plus, how many readers, even once you understand this, can tell us off the top of your head whether `this` will be processed first or last? I had a guess, but I was wrong, despite my 12 years of full-time Java experience.I'm sorry, but terrible idea.
Kevin Bourrillion
(Besides which, you *can* make it much shorter than that, if you're willing to use libraries.)
Kevin Bourrillion
@Kevin Bourrillion, just know that to the dynamic languages folk, if you can manage to get it all on one line, you're like the cool guy in the office. But true about this code.
Yar
the cool guy when they see it at that moment, not the cool guy a couple months later.
Carl
The guy asked how he could make it shorter, not how he could make it more readable. Also, I was assuming that you couldn't use something that wasn't in the J6SE, and you were doing Java. Else one could easily just write an API that does the entire thing and just call that method, which would kind of miss the point totally.So, Kevin, please educate me on as to make it shorter in J6SE.
Viktor Klang
@ViktorKlang, I'm not sure about this debate, so plus one and free beer for everybody. Hope you guys sort it out.
Yar
Why would anyone assume that they should ignore all the free, open-source, peer-reviewed, heavily-tested libraries out there available to you?And saying "he asked for shorter, not better" is just willful tunnel vision. That's the kind of question-answering a robot can do.
Kevin Bourrillion
@Kevin, you know, I think it wouldn't hurt you to actually _read_ the questions before commenting. The title happens to be: "Language Tricks to Shorten My Java Code?" and not "Can a library make this code my readable?" Also, I'm eagerly awaiting your shorter language tricks solution to this. I'll buy some popcorn and wait for the show.
Viktor Klang
A: 

You can try using an immutable list by using the nCopies() method. Quoting from the API reference,

public static <T> List<T> nCopies(int n, T o)

Returns an immutable list consisting of n copies of the specified object. The newly allocated data object is tiny (it contains a single reference to the data object). This method is useful in combination with the List.addAll method to grow lists. The returned list is serializable.

this will avoid the first foreach iteration.

lorenzog
Fascinating. I have to check this out.
Yar
I don't understand how this helps.
Kevin Bourrillion
@Kevin Bourrillion it does not.
Yar
+1  A: 

In case you don't want to use Groovy or JRuby because of their dynamic nature, you should consider using Scala. Scala is statically typed but more concise than Java.

deamon
+1 because it's interesting to know. Not relevant for my purposes, but interesting to know.
Yar
+2  A: 

I've just implemented an Iterator of *Iterable*s (it is implemented in a more robust/tested/reviewed way by Guava Iterables.concat(Iterable...)):

// make a concatenation of iterables
public static <T> Iterable<T> $(final Iterable<T> first, final Iterable<T>... iterables) {
    List<Iterator<T>> allIterables = new LinkedList<Iterator<T>>();
    allIterables.add(first.iterator());

    for (Iterable<T> iterable : iterables) {
        allIterables.add(iterable.iterator());
    }

    final Iterator<Iterator<T>> iterators = allIterables.iterator();

    return new Iterable<T>() {

        @Override
        public Iterator<T> iterator() {
            return new Iterator<T>() {

                private Iterator<T> current = iterators.next();

                @Override
                public boolean hasNext() {
                    if (current.hasNext()) {
                        return true;
                    } else {
                        if (iterators.hasNext()) {
                            current = iterators.next();
                            return current.hasNext();
                        } else {
                            return false;
                        }
                    }
                }

                @Override
                public T next() {
                    return current.next();
                }

                @Override
                public void remove() {
                }
            };
        }
    };
}

using it your code becomes:

for (Looper looper : $($(looperTracks), this)) {

}

in the case you care the implementation is being part of my Dollar library (relesead as LGPL3).

dfa
Interesting. I didn't know you could use dollar in method names. Thanks, this is kind of what I'd like to know about. Dynamic-ish libs to use Java.
Yar
Is LGPL3 "infectious" meaning that all code using it automatically becomes GPL?
Yar
Also, I edited the question. Will it work if looperTracks is an array of type LooperTrack[] (implements Looper)?
Yar
What a cool library!
Eyvind
@yar: absolutely *NOT*, it is used by libraries such as Hibernate
dfa
@yar: unfortunately Java array are not assignable to Iterable, but Dollar wrap an array in a way that you can write: int[] array = ...; Iterable<Integer> iterable = $(array);
dfa
So the code would be `$($(looperTracks), this))`? After the colon, I mean.
Yar
@yar: of course :)
dfa
Okay, and congrats/good luck on the lib!
Yar
This is also in Guava, as `Iterables.concat(Iterable...)`.
finnw
Very cool library.
RHSeeger
+1  A: 

Is it a requirement that you assemble everything into one list? If not, what's wrong with this?

for (Looper looper : looperTracks) {
    doSomething(looper);
}
doSomething(this);
Alan Moore
Point taken, but the problem is that then I would need to pass any other variables in the method to doSomething as well. It's definitely an option (because there are no other variables) :)... Thanks and +1
Yar
+1  A: 

Try

Looper[] loop = new Looper[looperTracks.length + 1];
System.arraycopy(looperTracks, 0, loop, 0, looperTracks.length);
loop[looperTracks.length] = this;
for (Looper l : loop) { ... }

but honestly, why not just loop through the existing array, and then do whatever you want to do with the loop to this afterwards?

The List-version of the above looks like:

List<Looper> loop = new ArrayList<Looper>(Arrays.asList(looperTracks));
loop.add(this);
for (Looper l : loop) { ... }
Carl
The reason you don't want to run through the same code twice -- loop and then do whatever -- is to avoid running the same twice. Making it a method is another way to go, as @Alan More points out (above)
Yar
one way or the other, you are doing the same thing - encapsulating that in a method or whathaveyou is a question of readability/maintainability (indeed this whole exercise of getting it into one loop is), not function. I doubt very much the CPU sees something different either way you slice it.
Carl
@yar: Actually, my suggestion was exactly the same as @Carl's: iterate through the collection, then deal with `this` separately (I failed to read that line before posting my reply). My `doSomething()` method was just a placeholder for whatever *you're* doing with all those `Looper` objects.
Alan Moore
@Carl, I don't believe in early optimization much at all in these cases, so that wasn't what I was going for. But the problem was avoiding code repitition... any of these solutions will work. I just thought that, if I had had an ELEGANT way to get all the things in a loop.... well you get the point. Plus one for everybody, beer is on me (but you have to come to Venice)!
Yar
+4  A: 

Guava makes this pretty easy:

for (Looper looper : ObjectArrays.concat(looperTracks, this)) {}

Here is the documentation for the method ObjectArrays#concat.

Kevin Bourrillion
Yeah, didn't think of looking for libs to do this kind of thing. Great stuff!
Yar
And until Guava releases anything, you can use Google Collections which is a subset of what Guava will become, and of which there is already a 1.0 release: http://code.google.com/p/google-collections/
Joachim Sauer
A: 

I'm just going to throw this out there but don't try and make your code shorter at the expense of making it readable.

Readable code is always my aim when I start writing code, simply because I know that at some point in the future either myself or someone else is going to look at it and have to understand it.

James Raybould
This is true, but at the same time, you cannot ignore the short-is-better trend (hence Groovy, Ruby, Python). I loathe shortness for shortness -- especially the everything-on-one-line idea -- but in this case shorter IS more readable.
Yar