当前位置:  开发笔记 > 编程语言 > 正文

应该尝试......赶上环路内外?

如何解决《应该尝试赶上环路内外?》经验,为你挑选了6个好方法。

我有一个看起来像这样的循环:

for (int i = 0; i < max; i++) {
    String myString = ...;
    float myNum = Float.parseFloat(myString);
    myFloats[i] = myNum;
}

这是方法的主要内容,其唯一目的是返回浮点数组.我希望这个方法null在出现错误时返回,所以我将循环放在一个try...catch块中,如下所示:

try {
    for (int i = 0; i < max; i++) {
        String myString = ...;
        float myNum = Float.parseFloat(myString);
        myFloats[i] = myNum;
    }
} catch (NumberFormatException ex) {
    return null;
}

但后来我还想把try...catch块放在循环中,如下所示:

for (int i = 0; i < max; i++) {
    String myString = ...;
    try {
        float myNum = Float.parseFloat(myString);
    } catch (NumberFormatException ex) {
        return null;
    }
    myFloats[i] = myNum;
}

是否有任何理由,表现或其他方式比较喜欢一个?


编辑:共识似乎是将循环置于try/catch中更清晰,可能在其自己的方法中.但是,关于哪个更快,仍然存在争议.有人可以对此进行测试,并以统一的答案回来吗?



1> Jeffrey L Wh..:

性能:

try/catch结构的放置位置绝对没有性能差异.在内部,它们被实现为在调用方法时创建的结构中的代码范围表.当该方法正在执行时,try/catch结构完全不在图片中,除非发生抛出,然后将错误的位置与表进行比较.

以下是参考资料:http://www.javaworld.com/javaworld/jw-01-1997/jw-01-hood.html

该表描述了一半.


谢谢.我想自1997年以来情况可能已经发生了变化,但它们的效率却很低*.

2> Ray Hayes..:

表现:正如杰弗里在回答中所说,在Java中它并没有太大的区别.

通常,为了代码的可读性,您选择捕获异常的位置取决于您是否希望循环继续处理.

在您的示例中,您在捕获异常时返回.在那种情况下,我将try/catch放在循环中.如果你只是想抓住一个坏的值但继续处理,就把它放进去.

第三种方式:您总是可以编写自己的静态ParseFloat方法,并在该方法中处理异常处理而不是循环.使异常处理与循环本身隔离!

class Parsing
{
    public static Float MyParseFloat(string inputValue)
    {
        try
        {
            return Float.parseFloat(inputValue);
        }
        catch ( NumberFormatException e )
        {
            return null;
        }
    }

    // ....  your code
    for(int i = 0; i < max; i++) 
    {
        String myString = ...;
        Float myNum = Parsing.MyParseFloat(myString);
        if ( myNum == null ) return;
        myFloats[i] = (float) myNum;
    }
}


我知道,这就是为什么我在他的情况下说,因为当他遇到问题时他会回来,然后我会使用外部捕获.为清楚起见,这是我使用的规则......
其他人建议使用TryParse,它在C#/ .net中允许您在不处理异常的情况下进行安全解析,现在已经复制并且该方法可以重用.至于最初的问题,我更喜欢捕获内部的循环,即使没有性能问题,它看起来也更干净.

3> Michael Myer..:

好吧,在Jeffrey L Whitledge说没有性能差异之后(截至1997年),我去测试了它.我跑了这个小基准:

public class Main {

    private static final int NUM_TESTS = 100;
    private static int ITERATIONS = 1000000;
    // time counters
    private static long inTime = 0L;
    private static long aroundTime = 0L;

    public static void main(String[] args) {
        for (int i = 0; i < NUM_TESTS; i++) {
            test();
            ITERATIONS += 1; // so the tests don't always return the same number
        }
        System.out.println("Inside loop: " + (inTime/1000000.0) + " ms.");
        System.out.println("Around loop: " + (aroundTime/1000000.0) + " ms.");
    }
    public static void test() {
        aroundTime += testAround();
        inTime += testIn();
    }
    public static long testIn() {
        long start = System.nanoTime();
        Integer i = tryInLoop();
        long ret = System.nanoTime() - start;
        System.out.println(i); // don't optimize it away
        return ret;
    }
    public static long testAround() {
        long start = System.nanoTime();
        Integer i = tryAroundLoop();
        long ret = System.nanoTime() - start;
        System.out.println(i); // don't optimize it away
        return ret;
    }
    public static Integer tryInLoop() {
        int count = 0;
        for (int i = 0; i < ITERATIONS; i++) {
            try {
                count = Integer.parseInt(Integer.toString(count)) + 1;
            } catch (NumberFormatException ex) {
                return null;
            }
        }
        return count;
    }
    public static Integer tryAroundLoop() {
        int count = 0;
        try {
            for (int i = 0; i < ITERATIONS; i++) {
                count = Integer.parseInt(Integer.toString(count)) + 1;
            }
            return count;
        } catch (NumberFormatException ex) {
            return null;
        }
    }
}

我使用javap检查了生成的字节码,以确保没有任何内联.

结果表明,假设JIT优化不明显,Jeffrey是正确的 ; 也绝对在Java 6没有性能上的差异,阳光客户端虚拟机(我没有获得其它版本).在整个测试中,总时间差大约为几毫秒.

因此,唯一的考虑因素是看起来最干净.我发现第二种方式是丑陋的,所以我会坚持第一种方式或Ray Hayes的方式.



4> seBaka28..:

虽然性能可能相同,"看起来"更好是非常主观的,但功能上仍然存在很大差异.请看以下示例:

Integer j = 0;
    try {
        while (true) {
            ++j;

            if (j == 20) { throw new Exception(); }
            if (j%4 == 0) { System.out.println(j); }
            if (j == 40) { break; }
        }
    } catch (Exception e) {
        System.out.println("in catch block");
    }

while循环在try catch块内,变量'j'递增直到它达到40,当j mod 4为零时打印出来,当j到达20时抛出异常.

在任何细节之前,这里是另一个例子:

Integer i = 0;
    while (true) {
        try {
            ++i;

            if (i == 20) { throw new Exception(); }
            if (i%4 == 0) { System.out.println(i); }
            if (i == 40) { break; }

        } catch (Exception e) { System.out.println("in catch block"); }
    }

与上面相同的逻辑,唯一的区别是try/catch块现在在while循环中.

这是输出(在try/catch中):

4
8
12 
16
in catch block

而另一个输出(尝试/捕获时):

4
8
12
16
in catch block
24
28
32
36
40

你有很大的不同:

而在try/catch中突破循环

try/catch in while保持循环活动



5> Matt N..:

我同意所有的性能和可读性帖子.但是,有些情况确实很重要.其他几个人提到了这一点,但通过示例可能更容易看到.

考虑这个略微修改的例子:

public static void main(String[] args) {
    String[] myNumberStrings = new String[] {"1.2345", "asdf", "2.3456"};
    ArrayList asNumbers = parseAll(myNumberStrings);
}

public static ArrayList parseAll(String[] numberStrings){
    ArrayList myFloats = new ArrayList();

    for(int i = 0; i < numberStrings.length; i++){
        myFloats.add(new Float(numberStrings[i]));
    }
    return myFloats;
}

如果你希望parseAll()方法在有任何错误时返回null(比如原始示例),你可以将try/catch放在外面,如下所示:

public static ArrayList parseAll1(String[] numberStrings){
    ArrayList myFloats = new ArrayList();
    try{
        for(int i = 0; i < numberStrings.length; i++){
            myFloats.add(new Float(numberStrings[i]));
        }
    } catch (NumberFormatException nfe){
        //fail on any error
        return null;
    }
    return myFloats;
}

实际上,你应该在这里返回一个错误而不是null,通常我不喜欢多次返回,但你明白了.

另一方面,如果你希望它只是忽略这些问题,并解析它可以使用的任何字符串,你可以将try/catch放在循环内部,如下所示:

public static ArrayList parseAll2(String[] numberStrings){
    ArrayList myFloats = new ArrayList();

    for(int i = 0; i < numberStrings.length; i++){
        try{
            myFloats.add(new Float(numberStrings[i]));
        } catch (NumberFormatException nfe){
            //don't add just this one
        }
    }

    return myFloats;
}


这就是为什么我说"如果你只想抓住一个不好的价值但继续加工,就把它放进去."

6> 小智..:

如前所述,性能是一样的.但是,用户体验不一定相同.在第一种情况下,您将快速失败(即在第一次错误之后),但是如果将try/catch块放在循环中,则可以捕获为给定的方法调用创建的所有错误.在从期望出现格式错误的字符串解析值的数组时,肯定会有这样的情况:您希望能够将所有错误呈现给用户,这样他们就不需要逐个尝试修复它们.

推荐阅读
mobiledu2402851373
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有