有效的Java-尽管创建了多个实例,但方法调用时间相同 [英] Effective Java - Same method invocation time despite creating multiple instances

查看:60
本文介绍了有效的Java-尽管创建了多个实例,但方法调用时间相同的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在研究有效的Java,在本书的第5项中,约书亚·布洛赫(Joshua Bloch)谈到了避免创建不必要的对象.一个示例演示了可变的Date对象,这些对象一旦计算出其值就永远不会被修改.

I'm studying Effective Java and in Item 5 of the Book, Joshua Bloch talks about the avoidance of creating unnecessary objects. An example demonstrates mutable Date objects that are never modified once their values have been computed.

这是不好的做法":

public Person(Date birthDate) {
    this.birthDate = new Date(birthDate.getTime());
}

// DON'T DO THIS!
public boolean isBabyBoomer() {
    // Unnecessary allocation of expensive object
    Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
    gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
    Date boomStart = gmtCal.getTime();
    gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
    Date boomEnd = gmtCal.getTime();
    return birthDate.compareTo(boomStart) >= 0
            && birthDate.compareTo(boomEnd) < 0;
}

isBabyBoomer方法每次调用时都不必要地创建一个新的Calendar,TimeZone和两个Date实例-这对我来说显然很有意义.

The isBabyBoomer method unnecessarily creates a new Calendar, TimeZone, and two Date instances each time it is invoked - and that clearly makes sense to me.

下面是改进的代码:

public Person(Date birthDate) {
    this.birthDate = new Date(birthDate.getTime());
}

/**
 * The starting and ending dates of the baby boom.
 */
private static final Date BOOM_START;
private static final Date BOOM_END;

static {
    Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
    gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
    BOOM_START = gmtCal.getTime();
    gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
    BOOM_END = gmtCal.getTime();
}

public boolean isBabyBoomer() {
    return birthDate.compareTo(BOOM_START) >= 0
            && birthDate.compareTo(BOOM_END) < 0;
}

日历,TimeZone和Date实例在初始化时仅创建一次.Bloch解释说,如果频繁调用 isBabyBoomer()方法,这将显着提高性能.

Calendar, TimeZone, and Date instances are created only once, when it is initialized. Bloch explains, that this results in significant performance gains if the method isBabyBoomer()is invoked frequently.

在他的机器上:
错误版本:1000万次调用需要32,000 ms
改进的版本:130毫秒,可进行1000万次调用

On his machine:
Bad Version: 32,000 ms for 10 million invocations
Improved Version: 130ms for 10 million invocations

但是当我在系统上运行示例时,性能完全相同(14毫秒).是实例仅创建一次的编译器功能吗?

But when I run the examples on my System the performance is exactly the same (14ms). Is that a compiler feature that the instances are only created once ?


这是我的基准:


Here is my benchmark:

    public static void main(String[] args) {
    Calendar cal = Calendar.getInstance();
    cal.set(1960, Calendar.JANUARY, 1, 1, 1, 0);
    Person p = new Person(cal.getTime());
    long startTime = System.nanoTime();
    for (int i = 0; i < 10000000; i++) {
        p.isBabyBoomer();
    }
    long stopTime = System.nanoTime();
    long elapsedTime = stopTime - startTime;
    double mseconds = (double) elapsedTime / 1000000.0;
    System.out.println(mseconds);
}

干杯,马库斯

推荐答案

您的基准测试是错误的.使用最新的Java 7和适当的预热,两种方法之间的区别非常明显:

Your benchmark is wrong. With the newest Java 7 and a proper warmup I get a dramatic difference between the two methods:

Person::main: estimatedSeconds 1 = '8,42'
Person::main: estimatedSeconds 2 = '0,01'

这是完整的可运行代码:

Here is the full runnable code:

import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

public class Person {
    private Date birthDate;
    static Date BOOM_START;
    static Date BOOM_END;

    public Person(Date birthDate) {
        this.birthDate = new Date(birthDate.getTime());
    }

    static {
        Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
        gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
        BOOM_START = gmtCal.getTime();
        gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
        BOOM_END = gmtCal.getTime();
    }

    public boolean isBabyBoomerWrong() {
        // Unnecessary allocation of expensive object
        Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
        gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
        Date boomStart = gmtCal.getTime();
        gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
        Date boomEnd = gmtCal.getTime();
        return birthDate.compareTo(boomStart) >= 0
                && birthDate.compareTo(boomEnd) < 0;
    }

    public boolean isBabyBoomer() {
        return birthDate.compareTo(BOOM_START) >= 0
                && birthDate.compareTo(BOOM_END) < 0;
    }

    public static void main(String[] args) {
        Person p = new Person(new Date());

        for (int i = 0; i < 10_000_000; i++) {
            p.isBabyBoomerWrong();
            p.isBabyBoomer();
        }

        long startTime = System.nanoTime();

        for (int i = 0; i < 10_000_000; i++) {
            p.isBabyBoomerWrong();
        }

        double estimatedSeconds = (System.nanoTime() - startTime) / 1000000000.0;
        System.out.println(String.format("Person::main: estimatedSeconds 1 = '%.2f'", estimatedSeconds));

        startTime = System.nanoTime();

        for (int i = 0; i < 10_000_000; i++) {
            p.isBabyBoomer();
        }

        estimatedSeconds = (System.nanoTime() - startTime) / 1000000000.0;
        System.out.println(String.format("Person::main: estimatedSeconds 2 = '%.2f'", estimatedSeconds));

    }
}

这篇关于有效的Java-尽管创建了多个实例,但方法调用时间相同的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆