Java Forum / First Aid / January 2007
How do you change all elements in a Collection at the same time?
phillip.s.powell@gmail.com - 12 Jan 2007 20:04 GMT In my native language, PHP, we have a function called array_walk() http://www.php.net/manual/en/function.array-walk.php
That will walk through an array and perform change on every element in the array.
I've studied the Collections within Java so far and this seems like the right way to do it (Collections.replaceAll(List list, Object oldVal, Object newVal)): http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collections.html#replaceAll(ja va.util.List,%20java.lang.Object,%20java.lang.Object)
But looking at the API does not tell me how to do a function-based "array_walk" like we can so easily do in PHP:
<? $array = array(1, 2, 3, 4, 5); @array_walk($array, create_function('&$a', 'return ($a + 1);')); // WILL RETURN (2, 3, 4, 5, 6) ?>
So how do I do this in Java?
Thanx Phil
Paul Hamaker - 12 Jan 2007 20:21 GMT As of Java5 : for ( SomeClass c : somecoll ) { //do something with (each) c in somecoll, like : c.change(somearg); } -- http://javalessons.com Paul Hamaker, SEMM Teaching ICT since 1987
phillip.s.powell@gmail.com - 12 Jan 2007 20:41 GMT > As of Java5 : > for ( SomeClass c : somecoll ) > { //do something with (each) c in somecoll, like : > c.change(somearg); But what if you won't know what "change()" will be? That is, I could be doing any kind of uniform change to the collection; I could be adding by 1, rearranging strings, converting Objects, literally anything at all (except removing them)
Here is where I can't find the Java equivalent of "eval()", something in PHP that's normal to me.
Phil
> } > -- > http://javalessons.com Paul Hamaker, SEMM > Teaching ICT since 1987 Rogan Dawes - 12 Jan 2007 21:27 GMT >> As of Java5 : >> for ( SomeClass c : somecoll ) [quoted text clipped - 10 lines] > > Phil Java is not an interpreted language. Hence it does not have "eval", since the classes and methods may not actually be present on the classpath.
If you REALLY need "eval" functionality, and cannot achieve what you need using sub-classes and/or interfaces, you may want to take a look at BeanShell, which is an interpreted version of Java.
Regards,
Rogan
phillip.s.powell@gmail.com - 12 Jan 2007 21:37 GMT > >> As of Java5 : > >> for ( SomeClass c : somecoll ) [quoted text clipped - 17 lines] > need using sub-classes and/or interfaces, you may want to take a look at > BeanShell, which is an interpreted version of Java. Thanx I'll look into it. I don't however see how BeanShell will do what I need to do within classes for a Java application to do what PHP can do with eval() and array_walk() more easily. :(
Phil
> Regards, > > Rogan Patricia Shanahan - 12 Jan 2007 23:14 GMT >> As of Java5 : >> for ( SomeClass c : somecoll ) [quoted text clipped - 5 lines] > by 1, rearranging strings, converting Objects, literally anything at > all (except removing them) The suggested code is not an implementation of array_walk, but a replacement for it. Use it at the point in the code where you decide to do a specific thing to every element of a collection. Replace c.change(somearg) with whatever operation, or combination of operations, you want.
> Here is where I can't find the Java equivalent of "eval()", something > in PHP that's normal to me. If you consider "eval()" to be essential for whatever you are doing, you should use an interpreted language, such as PHP.
Patricia
Mark Rafn - 12 Jan 2007 23:14 GMT >> As of Java5 : >> for ( SomeClass c : somecoll ) >> { //do something with (each) c in somecoll, like : >> c.change(somearg);
>But what if you won't know what "change()" will be? That is, I could be >doing any kind of uniform change to the collection; I could be adding >by 1, rearranging strings, converting Objects, literally anything at >all (except removing them) Define an interface like public interface SomeClassProcessor { void process(SomeClass c); } and have the caller (whoever it is that DOES know what to do with each element) pass you an instance of SomeClassProcessor. Call process(c) on each element.
>Here is where I can't find the Java equivalent of "eval()", something >in PHP that's normal to me. eval() is misspelled. Change the a to an i. It completely breaks any sort of compile-time analysis, and cannot be optimized in any reasonable way. -- Mark Rafn dagon@dagon.net <http://www.dagon.net/>
Eric Sosman - 13 Jan 2007 03:03 GMT >> As of Java5 : >> for ( SomeClass c : somecoll ) >> { //do something with (each) c in somecoll, like : >> c.change(somearg); > > But what if you won't know what "change()" will be? [...] Then you don't know whether the "change" will preserve the collection's invariants. Imagine traversing a HashSet and applying a transformation that changed the hashCodes(), or a TreeSet() and changing the outcome of compareTo() ...
Unrestricted change on a structured collection is not a Good Thing. If you want mapcar, you know where to find it.
 Signature Eric Sosman esosman@acm-dot-org.invalid
phillip.s.powell@gmail.com - 14 Jan 2007 08:50 GMT > >> As of Java5 : > >> for ( SomeClass c : somecoll ) [quoted text clipped - 10 lines] > Unrestricted change on a structured collection is not > a Good Thing. If you want mapcar, you know where to find it. But what does a Lisp function have to do with Java?
Hendrik Maryns - 15 Jan 2007 11:11 GMT phillip.s.powell@gmail.com schreef:
>>>> As of Java5 : >>>> for ( SomeClass c : somecoll ) [quoted text clipped - 10 lines] > > But what does a Lisp function have to do with Java? Nothing. Read the above comment as: if you want Lisp functionality, use Lisp.
H. - -- Hendrik Maryns http://tcl.sfs.uni-tuebingen.de/~hendrik/ ================== http://aouw.org Ask smart questions, get good answers: http://www.catb.org/~esr/faqs/smart-questions.html
John Ersatznom - 15 Jan 2007 16:36 GMT > Nothing. Read the above comment as: if you want Lisp functionality, use > Lisp. Lisp functionality? Lisp functionality is to tell you you have unbalanced parentheses and leave you to find a needle in a haystack. What he was looking for is myCollection collect: [:foo | foo + 1] which is in a far nicer looking language. (But also a far slower one than Java, unsuited for performing any serious work, last time I checked.)
Eric Sosman - 15 Jan 2007 18:49 GMT >> Nothing. Read the above comment as: if you want Lisp functionality, use >> Lisp. [quoted text clipped - 4 lines] > is in a far nicer looking language. (But also a far slower one than > Java, unsuited for performing any serious work, last time I checked.) "The above comment" (snipped; it was "If you want mapcar, you know where to find it") was about the nature of collections. Java offers several kinds, some of which rely on characteristics of the objects they contain: invariant hashCode(), consistent behavior of compareTo() and so on. These structured collections cannot tolerate arbitrary and uncontrolled changes to their contained objects (the O.P. asked "what if you won't know what `change()' will be?").
Lisp's mapcar operator applies to a List, a kind of collection whose structure does not rely on the nature of its contents but only on their order of appearance. By bringing it up, I intended two things: to appear clever (lost cause) by paraphrasing a well- known flippancy, but also to provoke the O.P. to think about what makes a Lisp List different from, say, a Java SortedSet. Java's List is a reasonably close (albeit imperfect) analogue of Lisp's, and a method that applies an arbitrary transformation to the contents might make sense. (Note that Java does not prevent the programmer from extending List's supplied implementations, nor from rolling his own.) However, the O.P. asked for a much more general operation that would apply to every kind of Collection -- and it was this goal I wanted to call into question.
 Signature Eric Sosman esosman@acm-dot-org.invalid
John Ersatznom - 16 Jan 2007 15:10 GMT > However, the O.P. asked for a much more general operation > that would apply to every kind of Collection -- and it was this goal > I wanted to call into question. Yes. Even aCollection collect: aBlock creates a new one rather than doing in-place modification. In Java you want to use ListIterator for this as was already pointed out in greater detail elsewhere.
If you want an unordered collection you can either use a list (and not care about the ordering) or use a map, iterate over the keys, and change the values (or iterate over the entry objects and change the values). Map doesn't care if values mutate or are replaced.
phillip.s.powell@gmail.com - 16 Jan 2007 22:34 GMT I'm sorry but all of you completely lost me. :(
Phil
> > However, the O.P. asked for a much more general operation > > that would apply to every kind of Collection -- and it was this goal [quoted text clipped - 8 lines] > the values (or iterate over the entry objects and change the values). > Map doesn't care if values mutate or are replaced. Lew - 16 Jan 2007 23:15 GMT > I'm sorry but all of you completely lost me. :( Rather than bore you here, I will send you to Sun for boredom.
Er, wisdom.
<http://java.sun.com/j2se/1.5.0/docs/api/java/util/Collection.html> <http://java.sun.com/j2se/1.5.0/docs/api/java/util/Iterator.html> <http://java.sun.com/j2se/1.5.0/docs/api/java/util/ListIterator.html> and related java.util interfaces and classes.
<http://java.sun.com/docs/books/tutorial/collections/index.html> for the tutorial.
Our friends had wandered down a back alley of nuances that differentiate collection types. In your case, they were saying you needed a ListIterator for various reasons that will make more sense after you have read the above links then re-read the posts in this thread.
- Lew
Tom Hawtin - 12 Jan 2007 20:58 GMT > That will walk through an array and perform change on every element in > the array.
> $array = array(1, 2, 3, 4, 5); > @array_walk($array, create_function('&$a', 'return ($a + 1);')); // > WILL RETURN (2, 3, 4, 5, 6) For a one off, you could write it explicitly as:
List<Integer> values = Arrays.asList(new Integer[] { 1, 2, 3, 4, 5 }); for ( ListIterator<Integer> iter = values.listIterator(); Integer value = iter.next(); ) { iter.set(value + 1); } System.out.println(values);
To abstract the 'array walking' takes a bit more work:
public interface Transform<T> { T transform(T value); } ... public <T> static void transform( List<T> values, Transform<T> transform ) { for ( ListIterator<T> iter = values.listIterator(); T value = iter.next(); ) { iter.set(transform.transform(value)); } } ... List<Integer> values = Arrays.asList(new Integer[] { 1, 2, 3, 4, 5 }); transform(values, new Transform<Integer>() { public Integer transform(Integer value) { return value+1; } }); System.out.println(values);
I have used List instead of Collection, because Collection doesn't provide a ListIterator and it's not an entirely sensible operation to perform on a set anyway.
As you can see, abstracting loops is not a particularly natural thing to do in Java. After splitting the method, my example is only one line shorter and indents three levels instead of one. The inner class can only reference final variables of the enclosing method. So it isn't done very often in Java, at the moment.
There are various proposals to make things simpler in Java 7. These will allow something like:
// BGGA transform(values) { Integer value => value+1 }
transform(values, { Integer value => value+1 });
// CICE (possibly without the <Integer>). transform(values, Transform<Integer>(Integer value) { return value+1; });
// My favourite: transform(values, new()(Integer value) { return value+1; });
Tom Hawtin
Daniel Pitts - 13 Jan 2007 01:28 GMT > In my native language, PHP, we have a function called array_walk() > http://www.php.net/manual/en/function.array-walk.php [quoted text clipped - 20 lines] > Thanx > Phil Java doesn't exactly have closures, so its hard to do stuff like what your asking.. However, what you really want is:
Replacing "doChange" with whatever is appropriate.
You COULD make a utility class that does this for you:
public interface Transformer { Object transform(Object o); }
public class ListWalker { public static void walk(List myList, Transformer transformer) { ListIterator iterator = myList.listIterator(); while (iterator.hasNext()) { iterator.set( transformer.transform(iterator.next()) ); } }
Now you can call: ListWalker.walk(myList, new Transformer() { public Object transform(Object o) { return new Integer(((Integer)o).intValue() + 1); } });
Ick... Did I just write that? Well, you can't do little "tricks" with Java like you can in PHP, but don't worry about that too much.
RedGrittyBrick - 13 Jan 2007 12:51 GMT >> In my native language, PHP, we have a function called array_walk() >> That will walk through an array and perform change on every element in [quoted text clipped - 19 lines] > > Ick... Did I just write that? It might be less icky using generics?
interface Transformer<T> { T transform(T o); }
ListWalker.walk(myList, new Transformer<Integer>() { public Integer transform(Integer i) { return i+1; } });
Lew - 13 Jan 2007 17:15 GMT As so many on this thread have pointed out, Java can do what closures do, you just have to think in terms of object-orientation and polymorphism instead of interpreted, loosey-goosey self-rewriting code.
Many have argued for closures in Java. I am against them for the moment, since there are already idioms that accomplish the same thing without the conceptual mindtwist. Once again, that would be polymorphism, possibly with inner or anonymous classes, combined with generics to provide compile-time safety.
Java is a professional language, not a script-kiddie hack language. Things like program invariants, compile-time safety and maintainability carry weight here.
- Lew
Arne Vajhøj - 14 Jan 2007 01:46 GMT > As so many on this thread have pointed out, Java can do what closures > do, you just have to think in terms of object-orientation and [quoted text clipped - 9 lines] > Things like program invariants, compile-time safety and maintainability > carry weight here. I do not think it is fair to call closures a "script-kiddie hack language" feature.
It is a language feature with some merits.
Arne
PS: I am also against adding closures to Java, but that is strictly to keep the size of the Java language down.
Lew - 14 Jan 2007 02:58 GMT (followup set to clj.programmer, as we proceed into a little more theoretical domain.)
> I do not think it is fair to call closures a "script-kiddie > hack language" feature. > > It is a language feature with some merits. You are right. However, when something like "eval" (or "evil", per Mark Rafn) combines with dynamic typing and a few other things that interpreted languages sport, you start to see some tangled code.
Anyhow, my invective aside, you can accomplish the same effect, more or less, as closures using polymorphism over an interface, as suggested by Tom Hawtin for example. I believe the formal term is a "functor" class.
- Lew
Daniel Pitts - 14 Jan 2007 17:37 GMT > As so many on this thread have pointed out, Java can do what closures do, you > just have to think in terms of object-orientation and polymorphism instead of [quoted text clipped - 10 lines] > > - Lew True, but what would be the harm in having an interface:
public interface Closure<R, P> { R execute(P); }
and a new language feature:
public <T> void methodWhichTakesClosure(Closure<T, T> closure); ... methodWhichTakesClosure(closure<Integer, Integer>{return arg+1; }); which would be equivalent to: methodWhichTakesClosure(new Closure<Integer, Integer>() { Integer execute(Integer arg) { return arg + 1; } });
Anything that makes the intention of the calling code clearer is a Good Thing(TM) in my opinion, and its just as type safe.
While we're adding keywords and language features, maybe a shortcut to "Runnable" or "Callable" would be a useful idiom as well.
SwingUtilities.invokeLater(runnable{updateGuiWith(results);});
Lew - 14 Jan 2007 19:45 GMT > True, but what would be the harm in having an interface: > > public interface Closure<R, P> { > R execute(P); > } This already exists.
> and a new language feature: > [quoted text clipped - 7 lines] > } > }); This just looks like syntactic sugar, much like the enhanced for-loop. If you think
closure<Integer, Integer> { return arg+1; }
which to my eye lacks any resemblance to a method signature that can be enforce, is clearer than
new Closure<Integer, Integer>() { Integer execute(Integer arg) { return arg + 1; } }
than I see why you favor it. To me, the latter form is much more explicit and plenty easy to understand, and leaves less to guess than the first format.
> Anything that makes the intention of the calling code clearer is a Good > Thing(TM) in my opinion, and its [sic] just as type safe. I agree that clearer is better, but not that your closure form is clearer.
> While we're adding keywords and language features, maybe a shortcut to > "Runnable" or "Callable" would be a useful idiom as well. > > SwingUtilities.invokeLater(runnable{updateGuiWith(results);}); I have the same objections. I cannot tell how the syntax here tells me what exactly is happening, whereas the "new Runnable() { public void run(){...} }" syntax reveals all and hides nothing. To me, the existing Java syntax is clearer, and therefore by our common principle of "clearer is a Good Thing" we should stick with what we've got.
- Lew
Arne Vajhøj - 14 Jan 2007 20:31 GMT >> and a new language feature:
>> public <T> void methodWhichTakesClosure(Closure<T, T> closure); >> ... [quoted text clipped - 7 lines] > > This just looks like syntactic sugar, much like the enhanced for-loop. Ofcourse.
In the end the entire Java language is syntactic sugar over the instruction set on the system you run your app on.
The question is: does the code become sufficient easier to write & read and will it be used frequently enough to warrant an extension to the language.
It is not free to make he language bigger and more complex. We know how PL/I and Ada ended up.
I doubth it is worth it for closures.
Arne
Lasse Reichstein Nielsen - 14 Jan 2007 23:54 GMT > As so many on this thread have pointed out, Java can do what closures > do, you just have to think in terms of object-orientation and > polymorphism instead of interpreted, loosey-goosey self-rewriting code. Having closures in a language is completely unrelated to being interpreted, loosely typed, or otherwise "unproffessionel". Take a language like Haskel, which has a very string, but also powerful, type system. It's functional though, which is where closures typically occour.
> Many have argued for closures in Java. I am against them for the > moment, since there are already idioms that accomplish the same thing > without the conceptual mindtwist. For someone used to programming in functional languages, the conceptual mindtwist comes when you try to express a perfectly simple anonymous function value as an object of some unnecessary class or interface. It's just syntactic wrapping necessary to live in a language where only objects (and primitives) are expressible and denotable objects.
The argument for closures isn't really about functionality, which is there already, but about syntax. Just as enums were added to provide simpler syntax for a commonly occuring programming construct, simpler syntax could be provided for function values.
Simpler syntax, like for enums, provides nothing but readability. It makes it easier to express the programmers intent and easier to recognize it for a reader. The more unnecessary syntax is wrapped around the essential idea, the less accessible it is.
What is easier to read:
myCollection.foreach(new Callable<Integer>() { void call(Integer v) { sum += v; } }); or
myCollection.foreach(fn(Integer v) { sum += v; });
> Once again, that would be polymorphism, possibly with inner or > anonymous classes, combined with generics to provide compile-time > safety. It's quite possible. It's just so ... wordy.
> Java is a professional language, not a script-kiddie hack > language. Things like program invariants, compile-time safety and > maintainability carry weight here. Which is why generics was a good addition (collections were untyped before). And why syntactic sugar making often used idioms were made easier to express: enums, iterations over iterables and arrays, boxing and unboxing. I happen to think that easier syntax for delegates, which is essentially what is needed, would be great. Real closures capturing local variables might not be necessary, but it would be nice.
/L
 Signature Lasse Reichstein Nielsen - lrn@hotpop.com DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleDOM.html> 'Faith without judgement merely degrades the spirit divine.'
gafter - 15 Jan 2007 01:12 GMT > As so many on this thread have pointed out, Java can do what closures do... > > Many have argued for closures in Java. I am against them for the moment, since > there are already idioms that accomplish the same thing without the conceptual > mindtwist... Actually, you can't do without closures what you can do with them; they actualy reduce the conceptual mindtwist. Admittedly, you may have to learn something.
See http://www.bejug.org/confluenceBeJUG/display/PARLEYS/Closures+for+Java
pkriens - 15 Jan 2007 06:51 GMT Right! programming should be hard so only real men can do it! All this sissy stuff of auto-boxing and foreach loops.
Kind regards,
Peter Kriens
> As so many on this thread have pointed out, Java can do what closures do, you > just have to think in terms of object-orientation and polymorphism instead of [quoted text clipped - 10 lines] > > - Lew Kai Schwebke - 14 Jan 2007 14:06 GMT phillip.s.powell@gmail.com schrieb:
> <? > $array = array(1, 2, 3, 4, 5); [quoted text clipped - 3 lines] > > So how do I do this in Java? As some have already pointed out this could be achieved (quite verbose) using a callback-like anonymous class implementing some interface.
Another option would be heading towards Groovy, a dynamic language which runs on and interfaces well with the Java platform.
Here comes a Groovy implementation of your PHP code: -------------- array = [1, 2, 3, 4, 5]
array.collect { it = it+1 } // returns [2, 3, 4, 5] --------------
Or even shorter using a range: -------------- (1..5).collect { it = it+1 } // returns [2, 3, 4, 5] --------------
Kai
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|