You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ant.apache.org by Kev Jackson <ke...@it.fts-vn.com> on 2005/02/28 08:53:35 UTC

Re: AW: [Patch] modifiedselector, style, remove unused code, slightly more lazy DigestAlgorithm.getValue (now with added source code -doh!)

>Why you use the for-loop instead of iterator? 
>  
>

1 iterator has to check underlying array bounds for every hasNext(), and 
every next(), checking is slow, and in this case unecessary
2 iterator call to hasNext() each time around the loop - again we can 
call size() once and cache the result, since we are never updating the 
underlying structure, this won't change and the local var read is faster 
than a method call.
3 iterator is a "large" object to create, an int is a small one 
(bytecode is probably smaller, although I haven't checked this, I can 
almost (99%) guarantee it)
4 remove calls to iterator, then we can remove the import :)

It's just an idiom I learnt from "Effective Java" or one of the books 
like that, but the reasons above mean that I use it whenever I can, as 
it does tend to speed up large loops, at the expense of only a very 
slight obfuscation of the code.  Some people use essentially the same 
loop but move the size variable above the loop.  That's ok, but it means 
that the size variable has slightly larger scope than it requires, 
although the effect is the same.

Also speaking of Iterators in general...

Iterators: Signs of Weakness in Object-Oriented Languages
http://citeseer.ist.psu.edu/55669.html (there's a PDF or PS file 
downloadable from the link at the top)

I don't pretend to understand all the Lispness of this paper, but some 
of the arguments against external iterators (the kind we have in Java) 
are interesting.  I did a little test last week comparing a  normal 
iterator/for loop, with a "functional-style" of programming, both in 
Java.  Speed was identical, but bytecode of the functional-style was 
much smaller, there were less casts and less method calls.  Anecdotal at 
best, but try it yourself:

[I decided to call these things Closures, but they aren't quite, rather 
more like Blocks, correct me if I'm getting my terms wrong]

public interface Closure {
    Object execute(Object o);
}

import java.util.ArrayList;
import java.util.List;

public class ClosureUtils {

    /**
     * This method maps a "function" to a list of data
     * and returns a new modified list.
     * @param list the source list of data
     * @param c the Closure to apply to the list
     * @return the modified list
     */
    public static List map(final List list, final Closure c) {
        final int size = list.size();
        List result = new ArrayList(size);
        for (int i=0; i<size; i++) {
            result.add(c.execute(list.get(i)));
        }
        return result;
    }
}

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import junit.framework.TestCase;


public class ClosureUtilsTest extends TestCase {
   
    List testList = Arrays.asList(new String[]{"This", "That", "Other"});
   
    /*
     * @see TestCase#setUp()
     */
    protected void setUp() throws Exception {
        super.setUp();
    }
   
    /*
     * @see TestCase#tearDown()
     */
    protected void tearDown() throws Exception {
        super.tearDown();
    }
   
    public void testJ5() {
        try {
            Date start = new Date();
            System.out.println("Start J5");
            for (int i=0; i<100000; i++) {
                List newList = new ArrayList();
               
                for (Object o : testList) {
                    newList.add(((String)o).toUpperCase());
                }
               
                assertEquals(((String)newList.get(0)), 
((String)testList.get(0)).toUpperCase());
            }
            System.out.println("End elapsed : [ "+(new 
Date().getTime()-start.getTime()));
        } catch (Exception e) {
            e.printStackTrace();
            fail();
        }
    }
   
    public void testOld() {
        try {
            Date start = new Date();
            System.out.println("Start old");
            for (int i=0; i<100000; i++) {
                List newList = new ArrayList();
               
                for (Iterator it = testList.iterator(); it.hasNext();) {
                    newList.add(((String)it.next()).toUpperCase());
                }
               
                assertEquals(((String)newList.get(0)), 
((String)testList.get(0)).toUpperCase());
            }
            System.out.println("End elapsed : [ "+(new 
Date().getTime()-start.getTime()));
        } catch (Exception e) {
            e.printStackTrace();
            fail();
        }
    }
   
    public void testMap() {
        try {
            Date start = new Date();
            System.out.println("Start closure");
            for (int i=0; i<100000; i++) {
                List newList = ClosureUtils.map(testList, new Closure() {
                    public Object execute(Object o) {
                        return ((String)o).toUpperCase();
                    }
                }
                );
                assertEquals(((String)newList.get(0)), 
((String)testList.get(0)).toUpperCase());
            }
            System.out.println("End elapsed : [ "+(new 
Date().getTime()-start.getTime()));
        } catch (Exception e) {
            e.printStackTrace();
            fail();
        }
    }
}

bytecode (note this is from tests without the 100000 iterations, or the 
timing code, both compiled on jdk1.5.0-b64/winXP, output from javap):

testJ5/testOld produced identical bytecode, showing that the new 
sugar-coated for loop is simply an old loop underneath

public void testMap();

  Code:
   0:   aload_0
   1:   getfield        #27; //Field testList:Ljava/util/List;
   4:   new     #43; //class com/ftsvn/tools/test/ClosureUtilsTest$1
   7:   dup
   8:   aload_0
   9:   invokespecial   #46; //Method 
com/ftsvn/tools/test/ClosureUtilsTest$1."<
init>":(Lcom/ftsvn/tools/test/ClosureUtilsTest;)V
   12:  invokestatic    #52; //Method 
com/ftsvn/tools/ClosureUtils.map:(Ljava/ut
il/List;Lcom/ftsvn/tools/Closure;)Ljava/util/List;
   15:  astore_1
   16:  aload_1
   17:  iconst_0
   18:  invokeinterface #58,  2; //InterfaceMethod 
java/util/List.get:(I)Ljava/l
ang/Object;
   23:  checkcast       #13; //class java/lang/String
   26:  checkcast       #13; //class java/lang/String
   29:  aload_0
   30:  getfield        #27; //Field testList:Ljava/util/List;
   33:  iconst_0
   34:  invokeinterface #58,  2; //InterfaceMethod 
java/util/List.get:(I)Ljava/l
ang/Object;
   39:  checkcast       #13; //class java/lang/String
   42:  checkcast       #13; //class java/lang/String
   45:  invokevirtual   #62; //Method 
java/lang/String.toUpperCase:()Ljava/lang/
String;
   48:  invokestatic    #68; //Method 
junit/framework/Assert.assertEquals:(Ljava
/lang/String;Ljava/lang/String;)V
   51:  goto    62
   54:  astore_1
   55:  aload_1
   56:  invokevirtual   #71; //Method 
java/lang/Exception.printStackTrace:()V
   59:  invokestatic    #74; //Method junit/framework/Assert.fail:()V
   62:  return
  Exception table:
   from   to  target type
     0    54    54   Class java/lang/Exception

public void testOld();
  Code:
   0:   new     #80; //class java/util/ArrayList
   3:   dup
   4:   invokespecial   #81; //Method java/util/ArrayList."<init>":()V
   7:   astore_1
   8:   aload_0
   9:   getfield        #27; //Field testList:Ljava/util/List;
   12:  invokeinterface #85,  1; //InterfaceMethod 
java/util/List.iterator:()Lja
va/util/Iterator;
   17:  astore_2
   18:  goto    43
   21:  aload_1
   22:  aload_2
   23:  invokeinterface #91,  1; //InterfaceMethod 
java/util/Iterator.next:()Lja
va/lang/Object;
   28:  checkcast       #13; //class java/lang/String
   31:  checkcast       #13; //class java/lang/String
   34:  invokevirtual   #62; //Method 
java/lang/String.toUpperCase:()Ljava/lang/
String;
   37:  invokeinterface #95,  2; //InterfaceMethod 
java/util/List.add:(Ljava/lan
g/Object;)Z
   42:  pop
   43:  aload_2
   44:  invokeinterface #99,  1; //InterfaceMethod 
java/util/Iterator.hasNext:()
Z
   49:  ifne    21
   52:  aload_1
   53:  iconst_0
   54:  invokeinterface #58,  2; //InterfaceMethod 
java/util/List.get:(I)Ljava/l
ang/Object;
   59:  checkcast       #13; //class java/lang/String
   62:  checkcast       #13; //class java/lang/String
   65:  aload_0
   66:  getfield        #27; //Field testList:Ljava/util/List;
   69:  iconst_0
   70:  invokeinterface #58,  2; //InterfaceMethod 
java/util/List.get:(I)Ljava/l
ang/Object;
   75:  checkcast       #13; //class java/lang/String
   78:  checkcast       #13; //class java/lang/String
   81:  invokevirtual   #62; //Method 
java/lang/String.toUpperCase:()Ljava/lang/
String;
   84:  invokestatic    #68; //Method 
junit/framework/Assert.assertEquals:(Ljava
/lang/String;Ljava/lang/String;)V
   87:  goto    98
   90:  astore_1
   91:  aload_1
   92:  invokevirtual   #71; //Method 
java/lang/Exception.printStackTrace:()V
   95:  invokestatic    #74; //Method junit/framework/Assert.fail:()V
   98:  return
  Exception table:
   from   to  target type
     0    90    90   Class java/lang/Exception

 From this I was fairly impressed on the code-size reduction alone, but 
the fact that you don't trade-off speed is excellent.  It behaves 
similarly to the Visitor pattern I think, but instead of an accept/visit 
method you have an execute.  I like the style, but as I mentioned 
earlier I'm not convinced I should name them Closures.  Anyway I'm rambling.

>>      //
>>       // -----  Instantiate the interfaces  -----
>>       //
>>       String className = null;
>>       String pkg = 
>>"org.apache.tools.ant.types.selectors.modifiedselector";
>>
>>what do these lines do in ModifiedSelector.configure?  
>>Eclipse says that 
>>they're never read, and as they're method variables, not class or 
>>instance variables (ie not public), I was very tempted to 
>>delete them, 
>>but I thought I'd better ask first in case they're present to support 
>>future functionality
>>    
>>
>
>That came more from "past functionality" than for future one :)
>While developing the modified selector I started with reflection. And in
>earlier steps
>I had hardcoded classnames for the three interfaces as default. And so I
>could simply shorten
>the text to write.
>My ascii editor I used there couldn�t check for unneeded variables :)
>
>  
>
Ah, ok, I've removed them, here's the "new" modified selector

Kev