在Java中将字符串转换为整数的最有效方法是什么?

29

有许多方法可以将字符串转换为整数对象。在以下方法中,哪个是最有效的:

Integer.valueOf()
Integer.parseInt()
org.apache.commons.beanutils.converters.IntegerConverter

我的使用情况需要创建包装整数对象...也就是没有原始的int...并且转换后的数据用于只读。


我进行了一些小测试,惊讶地发现Integer.valueOf花费了很长时间...有什么想法吗...[code] for (int i = 1; i <= Integer.MAX_VALUE / 100; i++) conversion [code]valueOf=0:00:18.094 parseInt=0:00:17.656 IntegerConverter=0:00:13.594 NumberUtils.toInt=0:00:13.734 - Aravind Yarram
请查看此链接:https://dev59.com/THRB5IYBdhLWcg3wyqOo - MBH
11个回答

30

如果你关注效率,那么创建一个Integer对象要比解析它昂贵得多。如果你必须创建一个Integer对象,我不会太担心它是如何解析的。

注意:Java 6u14允许您使用命令行选项-Djava.lang.Integer.IntegerCache.high=1024(例如)来增加您的整数池的大小。

注意2:如果您正在读取原始数据(例如从文件或网络中读取字节),将这些字节转换为字符串也相对昂贵。如果您要编写自定义解析器,我建议您跳过转换为字符串的步骤,直接解析原始源数据。

注意3:如果您要创建一个Integer以便将其放入集合中,您可以使用GNU Trove(trove4j),它允许您在集合中存储基本类型,从而无需创建Integer对象。

理想情况下,为了获得最佳性能,您应该尽量避免创建任何对象。


2
+1 for -Djava.lang.Integer.IntegerCache.high。Long 也有开关吗? - Thilo

24

您的最佳选择是使用Integer.parseInt。这将返回一个int,但可以自动装箱为Integer。当数字在-128到127之间时,它将使用整数缓存而不是创建新对象,因此略微快于valueOf方法。最慢的是Apache方法。

private String data = "99";

public void testParseInt() throws Exception {
    long start = System.currentTimeMillis();
    long count = 0;
    for (int i = 0; i < 100000000; i++) {
        Integer o = Integer.parseInt(data);
        count += o.hashCode();
    }
    long diff = System.currentTimeMillis() - start;
    System.out.println("parseInt completed in " + diff + "ms");
    assert 9900000000L == count;
}

public void testValueOf() throws Exception {
    long start = System.currentTimeMillis();
    long count = 0;
    for (int i = 0; i < 100000000; i++) {
        Integer o = Integer.valueOf(data);
        count += o.hashCode();
    }
    long diff = System.currentTimeMillis() - start;
    System.out.println("valueOf completed in " + diff + "ms");
    assert 9900000000L == count;
}


public void testIntegerConverter() throws Exception {
    long start = System.currentTimeMillis();
    IntegerConverter c = new IntegerConverter();
    long count = 0;
    for (int i = 0; i < 100000000; i++) {
        Integer o = (Integer) c.convert(Integer.class, data);
        count += o.hashCode();
    }
    long diff = System.currentTimeMillis() - start;
    System.out.println("IntegerConverter completed in " + diff + "ms");
    assert 9900000000L == count;
}

parseInt completed in 5906ms
valueOf completed in 7047ms
IntegerConverter completed in 7906ms

3
+1 表示实际测试了!顺便说一下,我认为如果你不将结果装箱,你可以从 Integer.parseInt() 中获得更快的结果... 在我的机器上这值得额外的 10% 左右。 - mikera
请问您能提供基准测试吗?确切地说出您尝试过的内容以得出结论是有帮助的,但我认为将您实际的结果与用于得出此结论的实际代码相结合会更有意义。例如,像这样的内容:http://nadeausoftware.com/articles/2009/08/java_tip_how_parse_integers_quickly - searchengine27

11

我总是惊讶于这里的许多人如此迅速地对一些性能问题的调查置之不理。在许多程序中,将int解析为10进制是一项非常常见的任务。加快此过程可以在许多环境中产生明显的积极影响。

由于解析int实际上是一项相当琐碎的任务,所以我尝试实现比JDK实现更直接的方法,该方法具有可变基数。结果它的速度超过两倍,并且应该与Integer.parseInt()完全相同。

public static int intValueOf( String str )
{
    int ival = 0, idx = 0, end;
    boolean sign = false;
    char ch;

    if( str == null || ( end = str.length() ) == 0 ||
       ( ( ch = str.charAt( 0 ) ) < '0' || ch > '9' )
          && ( !( sign = ch == '-' ) || ++idx == end || ( ( ch = str.charAt( idx ) ) < '0' || ch > '9' ) ) )
        throw new NumberFormatException( str );

    for(;; ival *= 10 )
    {
        ival += '0'- ch;
        if( ++idx == end )
            return sign ? ival : -ival;
        if( ( ch = str.charAt( idx ) ) < '0' || ch > '9' )
            throw new NumberFormatException( str );
    }
}
要获取一个整数对象,可以使用自动装箱或显式地调用 Interger.valueOf(intValueOf(str)) 方法。

3
人们忽视它的原因是因为在执行时间的100万分之一内获得50%的改进是浪费。对于任何非微不足道的程序,特别是涉及数据库的程序而言,字符串-整数转换所占的执行时间应远远低于100万分之一。 - kdgregory
7
在今天普遍存在的基于文本格式,如XML、JSON和HTTP的应用环境中,将数字从字符串中解析出来实际上是许多复杂程序中非常常见的任务。我更愿意说,如果你的程序在这种代码中花费的时间仍然很少,那么它们可能在其他地方非常低效。 - x4u
2
如果你想提高程序的整体吞吐量,自从我25年前开始工作以来,规则并没有改变:你需要创建一个真实的工作负载,使用该工作负载对应用程序进行分析,并优化实际消耗大量时间的部分。其他任何事情都是徒劳无功。 - kdgregory
1
我完全同意你的观点。分析是了解哪些部分值得优化的唯一途径。但是,如果这表明您的请求处理比处理请求需要的时间长1000倍,那么您要么在那里做一些严肃的工作,要么在这段代码中浪费时间。即,在请求处理程序中使用未缓存的数据库访问是我认为在大多数情况下在高负载服务器上效率低下不可接受的事情。 - x4u
我让它在jdk-1.5/1.6模式下的客户端/服务器通过基准测试,使用了100,000和3,000,000个案例。机器使用的是中央处理器为centrino single 1.6Ghz的Linux系统,sun-sdk/vm软件。结果:对于100,000个案例没有明显差异,客户端模式约为0.1秒,服务器模式各为0.3秒。对于3M个案例:cm:1.4秒x4u代码/1.8秒jvm代码,sm:1秒/1秒,简言之:高数字的服务器模式无区别,低数字亦然。 - user unknown
显示剩余4条评论

10

我知道这不在你上面提到的选项中。IntegerConverter还可以,但您需要创建它的实例。看一下Commons Lang中的NumberUtils:

Commons Lang NumberUtils

它提供了方法toInt:

static int toInt(java.lang.String str, int defaultValue) 

这使您能够在发生故障的情况下指定默认值。

NumberUtils.toInt("1", 0)  = 1

这是我目前找到的最佳解决方案。


6

这是一篇好的文章,比较了不同的整数解析方法的性能

以下是使用带有溢出/下溢检查的代码。

public static int parseInt( final String s )
{
    if ( string == null )
        throw new NumberFormatException( "Null string" );

    // Check for a sign.
    int num  = 0;
    int sign = -1;
    final int len  = s.length( );
    final char ch  = s.charAt( 0 );
    if ( ch == '-' )
    {
        if ( len == 1 )
            throw new NumberFormatException( "Missing digits:  " + s );
        sign = 1;
    }
    else
    {
        final int d = ch - '0';
        if ( d < 0 || d > 9 )
            throw new NumberFormatException( "Malformed:  " + s );
        num = -d;
    }

    // Build the number.
    final int max = (sign == -1) ?
        -Integer.MAX_VALUE : Integer.MIN_VALUE;
    final int multmax = max / 10;
    int i = 1;
    while ( i < len )
    {
        int d = s.charAt(i++) - '0';
        if ( d < 0 || d > 9 )
            throw new NumberFormatException( "Malformed:  " + s );
        if ( num < multmax )
            throw new NumberFormatException( "Over/underflow:  " + s );
        num *= 10;
        if ( num < (max+d) )
            throw new NumberFormatException( "Over/underflow:  " + s );
        num -= d;
    }

    return sign * num;
}

甚至更快的实现,无需溢出/下溢检查。

public static int parseInt( final String s )
{
    // Check for a sign.
    int num  = 0;
    int sign = -1;
    final int len  = s.length( );
    final char ch  = s.charAt( 0 );
    if ( ch == '-' )
        sign = 1;
    else
        num = '0' - ch;

    // Build the number.
    int i = 1;
    while ( i < len )
        num = num*10 + '0' - s.charAt( i++ );

    return sign * num;
} 

1
我需要将任意长度的String数字数组转换为int,因此效率非常重要。我使用了您更快的版本,但删除了符号检查以消除额外的if,因为我没有负数。 - lenooh

5

如果您关心效率,请使用int:它比Integer要快得多

否则,Integer类至少提供了几种明确、清晰的方法:

Integer myInteger = new Integer(someString);
Integer anotherInteger = Integer.valueOf(someOtherString);

我同意。使用Integer类似乎与效率有关。除非效率只涉及字符串转换,而不是整数存储或操作。 - aberrant80
使用Integer.valueOf(someOtherString),它更加智能,可能会使用缓存的Integer实例。 - deterb

5

我尝试使用以下程序比较了valueOf、parseInt、Ints.tryParse、NumberUtils.createInteger和NumberUtils.toInt。我使用的是jdk 1.8.0。

如预期的那样,不需要创建Integer对象的方法最快。我的结果如下:

valueOf took: 77
parseInt took: 61
Ints.tryParse took: 117
numberUtils.createInteger took: 169
numberUtils.toInt took: 63 

总结如下:

如果你可以使用int类型,那么使用Integer.parseInt。

如果你绝对需要一个Integer类型,那么使用Integer.valueOf。

如果你需要在解析时不处理异常的便利性,或者如果你不确定输入的格式(即它是一个不一定是数字的字符串),那么使用Ints.tryParse。

我使用的代码是:

public class HelloWorld {

public static int limit = 1000000;
public static String sint = "9999";

public static void main(String[] args) {

    long start = System.currentTimeMillis();
    for (int i = 0; i < limit; i++) {
       Integer integer = Integer.valueOf(sint);
    }
    long end = System.currentTimeMillis();

    System.out.println("valueOf took: " + (end - start));


    start = System.currentTimeMillis();
    for (int i = 0; i < limit; i++) {
        int integer = Integer.parseInt(sint);
    }
    end = System.currentTimeMillis();

    System.out.println("parseInt took: " + (end - start));


    start = System.currentTimeMillis();
    for (int i = 0; i < limit; i++) {
        int integer = Ints.tryParse(sint);
    }
    end = System.currentTimeMillis();

    System.out.println("Ints.tryParse took: " + (end - start));


    start = System.currentTimeMillis();
    for (int i = 0; i < limit; i++) {
        Integer integer = NumberUtils.createInteger(sint);
    }
    end = System.currentTimeMillis();

    System.out.println("numberUtils.createInteger took: " + (end - start));

    start = System.currentTimeMillis();
    for (int i = 0; i < limit; i++) {
        int integer = NumberUtils.toInt(sint);
    }
    end = System.currentTimeMillis();

    System.out.println("numberUtils.toInt took: " + (end - start));

}
}

5
不要浪费时间去考虑这个问题。只需选择一个似乎与其他代码相符的选项(其他转换是否使用.parse__()或.valueOf()方法?使用它来保持一致性)。
试图决定哪个是“最佳”的会分散你解决业务问题或实现功能的注意力。
不要让自己陷入琐碎的细节中。 :-)
顺便说一句,如果您的“用例”是为您的代码指定Java对象数据类型-您的BA需要退出您的领域。 BA需要定义“业务问题”,以及用户在解决问题时希望如何与应用程序交互。 开发人员确定如何将该功能与代码一起构建到应用程序中-包括处理数据的适当数据类型/对象。

1
太糟糕了,因为我最不喜欢那种方法,因为有“内置”的更简单的方法可以完成它。 :-) 但是,我无法想象它会引起任何性能问题-所以我会坚持“不修理破坏好事”的原则。 - Ron Savage
19
完全同意BA的观点,但总体评分为-1,因为没有回答实际问题并假设性能不重要,而问题明确是关于效率的。 - mikera
3
我同意。这不应该是被接受的答案,也不应该被投票支持。这个“答案”完全是主观和基于个人意见的,甚至没有试图回答所提出的问题。 - searchengine27
1
有时商业问题是系统不够快,因为开发人员认为细节不重要。我遇到过一些系统,我们的瓶颈是解析整数,加速处理这些可以使系统更加灵敏和可用。顺便说一下,我不确定BA与这个问题有什么关系。OP没有提到BA试图设计他们的系统。 - Jay Askren
4
这个问题是谷歌上针对需要尽快解析数十亿数字的人的头号搜索结果,但已被接受的答案毫无用处。 - Thom
显示剩余3条评论

0

ParseInt 返回一个 int,而不是 java.lang.Integer,因此如果您使用该方法,您需要执行以下操作

new Integer (Integer.parseInt(number));

我听说过很多次,为了内存原因而调用Integer.valueOf()而不是new Integer()更好(这来自于pmd)。
在JDK 1.5中,调用new Integer()会导致内存分配。Integer.valueOf()更加内存友好。

http://pmd.sourceforge.net/rules/migrating.html

此外,Integer.valueOf允许缓存,因为保证有缓存实例的值为-127到128。(自Java 1.5起)

2
Java现在支持自动装箱,即自动转换包装类型和基本类型之间的转换。因此,“Integer i = new Integer(Integer.parseInt(number));”最好改写为“Integer i = Integer.parseInt(number);”。 - Jim Ferrans

0

另一种方法是这样的:

public class stringtoInteger {

    private static int stringtoInteger(String x) {
        String value = "";
        for (int i = 0; i < x.length(); i++) {
            char character = x.charAt(i);
            if (Character.isDigit(character)) {
                value = value + character;
            }
        }
        return Integer.parseInt(value);
    }
}  

希望能帮到你!


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