我现在用java做ACM上的题,但是做了几个简单的题后我发现很多题我提交都会超时,代码在我的机器上完全可以运行,结果应该正确,但是超时我受不了,也不是java语言不适合做ACM上.我现在刚开始学数据结构和算法(java版),看了几个例子,也上百度找了一些帖子,知道自己以前的代码有很多地方有问题,比如我以前的代码根据帖子上说的两个语句能在内存中创造8个对象,但是换一种写法就可以只在内存中创建4个对象,这样时间和空间上都能有很好的优化.现在改了一些,但是还是有程序超时.在此我想让高手说一下java代码优化上的经常要注意的地方.如果有什么好的java代码优化方面的书也希望大家能告诉我.

解决方案 »

  1.   

    作ACM题目的关键不是优化java,而是优化算法.
    去看很多经典的问题,那些出彩的答案都是几行代码搞定的 .
      

  2.   

    1. 如何使用Exception 
    Exception降低性能。一个异常抛出首先需要创建一个新的对象。Throwable接口中的构造器调用名为fillInStackTrace()的本地方法。这个方法负责巡检栈的整个框架来收集跟踪信息。这样无论何时有异常抛出,它要求虚拟机装载调用栈,因为一个新的对象在中部被创建。 
    异常应当仅用于有错误发生时,而不要控制流。 
    2. 不要两次初始化变量 
    Java通过调用独特的类构造器默认地初始化变量为一个已知的值。所有的对象被设置成null,integers (byte, short, int, long)被设置成0,float和double设置成0.0,Boolean变量设置成false。这对那些扩展自其它类的类尤其重要,这跟使用一个新的关键词创建一个对象时所有一连串的构造器被自动调用一样。 
    3. 在任何可能的地方让类为Final 
    标记为final的类不能被扩展。在《核心Java API》中有大量这个技术的例子,诸如java.lang.String。将String类标记为final阻止了开发者创建他们自己实现的长度方法。 
    更深入点说,如果类是final的,所有类的方法也是final的。Java编译器可能会内联所有的方法(这依赖于编译器的实现)。在我的测试里,我已经看到性能平均增加了50%。 
    4. 在任何可能的地方使用局部变量 
    属于方法调用部分的自变量和声明为此调用一部分的临时变量存储在栈中,这比较快。诸如static,实例(instance)变量和新的对象创建在堆中,这比较慢。局部变量的更深入优化依赖于你正在使用的编译器或虚拟机。 
    5. 停止小聪明 
    很多开发人员在脑子中编写可复用和灵活的代码,而有时候在他们的程序中就产生额外的开销。曾经或者另外的时候他们编写了类似这样的代码: 
    public void doSomething(File file) { 
    FileInputStream fileIn = new FileInputStream(file); 
    // do something 
    他够灵活,但是同时他们也产生了更多的开销。这个主意背后做的事情是操纵一个InputStream,而不是一个文件,因此它应该重写如下: 
    public void doSomething(InputStream inputStream){ 
    // do something 
    6. 乘法和除法 
    我有太多的东东适用于摩尔法则——它声明CPU功率每年成倍增长。“摩尔法则”表明每年由开发者所写的差劲的代码数量三倍增加,划去了摩尔法则的任何好处。 
    考虑下面的代码: 
    for (val = 0; val < 100000; val +=5) { shiftX = val 8; myRaise = val 2; } 
    如果我们狡猾的利用位移(bit),性能将会六倍增加。这是重写的代码: 
    for (val = 0; val < 100000; val += 5) { shiftX = val << 3; myRaise = val << 1; } 
    代替了乘以8,我们使用同等效果的左移3位。每一个移动相当于乘以2,变量myRaise对此做了证明。同样向右移位相当于除以2,当然这会使执行速度加快,但可能会使你的东东以后难于理解;所以这只是个建议 
    7. 用代码有效处理内存溢出 
    OutOfMemoryError是由于内存不够后普遍会遇到的问题,下面一段代码能有效判断内存溢出错误,并在内存溢出发生时有效回收内存 
    通过该方法可以联想到有效管理连接池溢出,道理等同。 
    import java.util.*; 
    public class DataServer 
    { private Hashtable data = new Hashtable(); 
    public Object get (String key) 
    { Object obj = data.get (key); 
    if (obj == null) 
    { System.out.print (key + “ ”); 
    try 
    { // simulate getting lots of data 
    obj = new Double[1000000]; 
    data.put (key, obj); 
    } catch (OutOfMemoryError e) 
    { System.out.print (“\No Memory! ”); 
    flushCache(); 
    obj = get (key);// try again 
    } } 
    return (obj); 
    } public void flushCache() 
    { System.out.println (“Clearing cache”); 
    data.clear(); 
    } public static void main (String[] args) 
    { DataServer ds = new DataServer(); 
    int count = 0; 
    while (true) // infinite loop for test 
    ds.get (“” count+); 
    } } 
    8. Lazy Loading (Lazy evaluation)在需要装入的时候才装入 
    static public long 
    factorial( int n ) throws IllegalArgumentException 
    { IllegalArgumentException illegalArgumentException = 
    new IllegalArgumentException( “must be >= 0” ); 
    if( n < 0 ) { 
    throw illegalArgumentException ; 
    } else if( ( n 0 ) || ( n 1 ) ) { 
    return( 1 ); 
    } else ( 
    return( n * factorial( n – 1 ) ) ; 
    } 优化后代码 
    static public long 
    factorial( int n ) throws IllegalArgumentException 
    { if( n < 0 ) { 
    throw new IllegalArgumentException( “must be >= 0” ); 
    } else if( ( n 0 ) || ( n 1 ) ) { 
    return( 1 ); 
    } else ( 
    return( n * factorial( n – 1 ) ) ; 
    } 9. 异常在需要抛出的地方抛出,try catch能整合就整合 
    try { 
    some.method1(); // Difficult for javac 
    } catch( method1Exception e ) { // and the JVM runtime 
    // Handle exception 1 // to optimize this 
    } // code 
    try { 
    some.method2(); 
    } catch( method2Exception e ) { 
    // Handle exception 2 
    } try { 
    some.method3(); 
    } catch( method3Exception e ) { 
    // Handle exception 3 
    } 已下代码 更容易被编译器优化 
    try { 
    some.method1(); // Easier to optimize 
    some.method2(); 
    some.method3(); 
    } catch( method1Exception e ) { 
    // Handle exception 1 
    } catch( method2Exception e ) { 
    // Handle exception 2 
    } catch( method3Exception e ) { 
    // Handle exception 3 
    } 10. For循环的优化 
    Replace… 
    for( int i = 0; i < collection.size(); i++ ) { 
    ... 
    } with… 
    for( int i = 0, n = collection.size(); i < n; i++ ) { 
    ... 
    } 11. 字符串操作优化 
    在对字符串实行+操作时,最好用一条语句 
    // Your source code looks like… 
    String str = “profit = revenue( ” revenue 
    “ – cost( ” cost ““; // 编译方法 
    String str = new StringBuffer( ).append( “profit = revenue( “ ). 
    append( revenue ).append( “ – cost( “ ). 
    append( cost ).append( ““ ).toString( ); 
    在循环中对字符串操作时改用StringBuffer.append()方法 
    String sentence = “”; 
    for( int i = 0; i < wordArray.length; i++ ) { 
    sentence += wordArray[ i ]; 
    } 优化为 
    StringBuffer buffer = new StringBuffer( 500 ); 
    for( int i = 0; i < wordArray.length; i++ ) { 
    buffer.append( wordArray[ i ] ); 
    } String sentence = buffer.toString( ); 
    12. 对象重用(特别对于大对象来说) 
    public 
    class Point 
    { public int x; 
    public int y; 
    public Point( ) 
    { this( 0, 0 ); 
    } } 
    优化为: 
    public class Component 
    { private int x; 
    private int y; 
    public Point getPosition( ) 
    { Point rv = new Point( ); // Create a new Point 
    rv.x = x; // Update its state 
    rv.y = y; 
    return rv; 
    } } 
    // Process an array of Component positions… 
    for( int i = 0; i < componentArray.length; i++ ) { 
    Point position = componentArray[i].getPosition( ); 
    // Process position value… 
    // Note: A Point object is created for each iteration 
    // of the loop… 
    } 可再次优化,仅使用一个类对象:) 
    public 
    class Component 
    { private int x; 
    private int y; 
    public Point getPosition( Point rv ) 
    { if( rv == null) rv = new Point( ); 
    rv.x = x; // Update its state 
    rv.y = y; 
    return rv; 
    } // Create a single point object and reuse it… 
    Point p = new Point( ); 
    for( int i = 0; i < componentArray.length; i++ ) { 
    Point position = componentArray[i].getPosition( p ); 
    // Process position value… 
    // Note: Only one Point object is ever created. 
    } 13. j2ee相关 
    a) 尽量不要将大对象放到HttpSession或其他须序列化的对象中,并注意及时清空Session 
    b) 使用预编译语句prepareStatement代替createStatement 
    c) 尽可能使用连接池 
    d) 能使用Cache就使用Cache,具体实现可参考jive(Cache\Cacheable\CacheObject\CacheSizes\DefaultCache\LinkdList\LinkdListNode)或ofbiz(org.ofbiz.core.util. UtilCache.java) 
      

  3.   

    作ACM题目一般用不到楼上这些东西的.
      

  4.   

    3楼的那些东西我找到过,但是太多了,一时适应不了.现在在学算法,刚学了几个排序的算法,对于ACM上的题是不是算法越精巧越好?那我现在主要的方向就不是对着刚写完的代码想哪里写得不好了,而是多看一下算法的书,看高手写的代码,之后写实现,了解后再来ACM上复仇啊?
      

  5.   

      对于ACM上的题面向对象的思想是不是用得就少了?
      

  6.   

      那哪里能找到一些用java做的ACM上的题的例子呢,我大多看到都 是C C++的,我想找一些 java 的
      

  7.   

    语言差别也不是很大,将就着看吧.
    很多书上还用pascal呢.
      

  8.   

    用 Java 做 ACM 的题本身就是耗时的,ACM 上也提到的,这与 Java 的运行方式有关。
    对于 Java 来说,在空的方法里啥都不做执行一下用的时间都要比其他语言做了事情的时间
    花得长。
      

  9.   

    我也做acm的,呵呵,你用scanner读入的吧,那个很容易超时的,我的经验是用这个import java.io.*;public class Main
    {
        public static void main(String[] args) throws IOException
        {
            StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
            PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
            int a, b;
            while(in.nextToken() != StreamTokenizer.TT_EOF)
            {
                a = (int)in.nval;
                in.nextToken();
                b = (int)in.nval;
                out.println(a + b);
            }
            out.flush();
        }
    }
    看看这个:  http://acm.hdu.edu.cn/faq.php?topic=java
      

  10.   

    呵呵,我还真的是用Scanner读入的,看来真得改改了.