System.currentTimeMillis()与new Date()与Calendar.getInstance().getTime()之间的区别是什么?

263

在Java中,使用什么会对性能和资源产生影响?

System.currentTimeMillis() 

对比。

new Date() 

对比。

Calendar.getInstance().getTime()

据我所了解,System.currentTimeMillis() 是最有效的方法。然而,在大多数应用程序中,需要将这个长整型值转换为日期或某些类似的对象,以便对人类有意义。

8个回答

268

System.currentTimeMillis() 显然是最高效的,因为它甚至不创建对象,但 new Date() 只是一个对 long 做了简单封装的薄膜,所以它的效率也很高。另一方面,Calendar 相对较慢且非常复杂,因为它必须处理与日期和时间相关的所有复杂性和怪异问题(闰年、夏令时、时区等)。

通常建议在应用程序中仅处理 long 类型的时间戳或 Date 对象,并且只在实际需要执行日期/时间计算或格式化日期以将其显示给用户时使用 Calendar。如果你需要频繁进行此类操作,那么使用 Joda Time 可能是个好主意,因为它具有更清晰的接口和更好的性能。


3
时间戳(timestamp)和currentMillis之间有什么区别? - pinkpanther
3
“时间戳”通常用来描述一个整数/长整数,表示自“纪元开始”以来的秒数或毫秒数,从而表示某个时间点。换句话说,currentTimeMillis()返回一个时间戳。 - Michael Borgwardt

46

查看JDK,Calendar.getInstance()的最内层构造函数如下:

public GregorianCalendar(TimeZone zone, Locale aLocale) {
    super(zone, aLocale);
    gdate = (BaseCalendar.Date) gcal.newCalendarDate(zone);
    setTimeInMillis(System.currentTimeMillis());
}

所以它已经自动执行了你的建议。Date的默认构造函数包含了这个:

public Date() {
    this(System.currentTimeMillis());
}

所以,除非你在创建日历/日期对象之前需要进行一些数学计算,否则真的不需要特别获取系统时间。此外,如果你的目的是频繁进行日期计算,我必须推荐使用joda-time作为Java自带的日历/日期类的替代。


23
如果你正在使用日期,我强烈建议你使用Joda Time http://joda-time.sourceforge.net/。对于那些代表日期的字段来说,使用System.currentTimeMillis()听起来是一个非常糟糕的想法,因为你最终会得到很多无用的代码。
日期和日历都存在问题,而且Calendar绝对是它们中最糟糕的表现者。
当你实际上要操作毫秒时,我建议你使用System.currentTimeMillis(),例如像这样:
 long start = System.currentTimeMillis();
    .... do something ...
 long elapsed = System.currentTimeMillis() -start;

31
我想对这个进行评论,因为你的例子正是你不应该使用System.currentTimeMillis()的原因之一; 它不是单调时钟源,所以你不能可靠地用它来计算经过的时间。 如果在执行你计时的代码期间更改了系统时钟,你会得到奇怪(例如负数)的结果。 相反,请使用System.nanoTime(),如果底层系统支持这样的时钟源,它就是单调的(请参见http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6458294)。 - rem
System.currentTimeMillis本身不受时区影响。更改系统时间将以与System.currentTimeMillis相同的方式影响System.nanotime。 - Viktor
1
@Viktor,那是不正确的,nanoTime() 是一个始终递增的计数器,它永远不会返回比在此 JVM 实例中上次调用的时间更短的值。currentTimeMillis() 可能会返回比上次调用的时间更短的值,因为系统时间被改变了,例如 NTP 时钟同步。 - griffinjm

15

在我的机器上,我尝试进行了检查。我的结果:

Calendar.getInstance().getTime() (*1000000 次) = 402ms
new Date().getTime(); (*1000000 次) = 18ms
System.currentTimeMillis() (*1000000 次) = 16ms

不要忘记GC(如果您使用 Calendar.getInstance()new Date()


2
不知道如果许多线程在其他处理之间调用相同的内容是否会有任何差异。 - tgkprog

12

我更喜欢使用System.currentTimeMillis()返回的值进行所有类型的计算,仅在需要真正显示人类可读取的值时才使用CalendarDate。这还将防止99%的夏令时错误。:)


7

根据您的应用程序,您可能希望考虑使用System.nanoTime()


为什么?他的问题是关于资源和性能的,nanoTime() 使用了更多的资源。 - WolfmanDragon
我提到这个选项是因为没有其他人提出过。海报没有指定平台。SDN bug 6876279表明,在某些JDK版本中,currentTimeMillis()和nanoTime()的执行时间大致相同。海报也没有指定他们的精度需求,原始列表可能不足够满足要求。 - MykennaC
4
虽然有些晚了,但问题中的所有示例都具有绝对时间概念,例如自Unix纪元开始以来的1,348,770,313,071毫秒。nanoTime返回的时间是相对的(通常是相对于程序的启动时间),如果您尝试将其转换为日期,则会变成无意义的东西。 - Dunes
是的,但你可以在程序启动时将currentTimeMillis和nanoTime存储在一些静态代码中,然后使用它们作为偏移量来使用双变量得到准确的纳秒时间:currentTimeStart - nanoTimeStart + nanoTimeNow。 - tgkprog

3
我尝试了这个:
        long now = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            new Date().getTime();
        }
        long result = System.currentTimeMillis() - now;

        System.out.println("Date(): " + result);

        now = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            System.currentTimeMillis();
        }
        result = System.currentTimeMillis() - now;

        System.out.println("currentTimeMillis(): " + result);

结果如下:

Date():199

currentTimeMillis():3


4
这是一个微基准测试,你需要小心信任所得到的结果。请查看https://dev59.com/hHRB5IYBdhLWcg3wz6UK。 - Axel
1
这个基准测试结果并不具有显著性,或者更确切地说,它表明在Java下操作系统确实很重要。我在循环中运行了相同的基准测试十几次(将“now”和“result”的初始化移出循环),每次运行都有微小的差异:Date():322到330;currentTimeMillis():319到322。在其他一些运行中,我得到了Date():312到318;currentTimeMillis():324到335。因此,在实际情况下它们是相当等价的(也可以查看Date的源代码)。供您参考,我在Ubuntu上使用的是Java7。 - Sampisa

0

System.currentTimeMillis() 显然是最快的,因为它只需要一个方法调用,而且不需要垃圾回收器。


16
你的回答没有价值,因为它只是先前批准的回答的一部分。你应该避免这种回答方式,尤其是考虑到这是一个非常老的问题,已经有一个质量上乘的答案存在。 - Nicklas Gnejs Eriksson
1
其次,对于伊甸园空间中的短期对象,垃圾收集器并不是必需的。 - Niels Bech Nielsen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接