是否有更有效的方法在ArrayList和Array之间进行转换

时间:2011-10-30 15:01:17

标签: java arrays performance arraylist

使用Java,我有一个类,它将网页检索为字节数组。然后我需要删除一些内容(如果存在)。 (该应用程序监视网页的变化,但需要从PHP创建的html中删除会话ID,并且意味着每次访问该页面时都会检测到更改。)

一些生成的字节数组可能是1000个字节长的10。它们不是这样存储的 - 存储了16字节的MD5页面。但是,它是需要处理的原始全尺寸字节数组。

更新 - 代码不起作用。请参阅以下A.H.的评论) 测试显示我的代码:

public void testSessionIDGetsRemovedFromData() throws IOException
    {

        byte[] forumContent = "<li class=\"icon-logout\"><a href=\"./ucp.php?mode=logout&amp;sid=3a4043284674572e35881e022c68fcd8\" title=\"Logout [ barry ]\" accesskey=\"x\">Logout [ barry ]</a></li>".getBytes();

        byte[] sidPattern = "&amp;sid=".getBytes();
        int sidIndex = ArrayCleaner.getPatternIndex(forumContent, sidPattern);
        assertEquals(54, sidIndex);

        // start of cleaning code
        ArrayList<Byte> forumContentList = new ArrayList<Byte>();
        forumContentList.addAll(forumContent);
        forumContentList.removeAll(Arrays.asList(sidPattern));

        byte[] forumContentCleaned = new byte[forumContentList.size()];
        for (int i = 0; i < forumContentCleaned.length; i++)
        {
            forumContentCleaned[i] = (byte)forumContentList.get(i);
        }
        //end of cleaning code

        sidIndex = ArrayCleaner.getPatternIndex(forumContentCleaned, sidPattern);
        assertEquals(-1, sidIndex);
    }

这一切都很好,但我担心清洁部分的效率。我原本希望只在数组上运行,但ArrayList有很好的内置函数来从ArrayList中删除一个集合,这正是我所需要的。所以我不得不创建一个Byte的ArrayList,因为我不能拥有原始字节的ArrayList(任何人都可以告诉我为什么?),将模式转换为删除到另一个ArrayList(我想这可能是一个ArrayList一直)传递给removeAll()。然后我需要创建另一个byte []并将Bytes的ArrayList的每个元素转换为一个字节并将其添加到byte []。

有没有更有效的方法来做这一切? 可以使用数组执行吗?

更新 的 这与使用字符串的功能相同:

    public void testSessionIDGetsRemovedFromDataUsingStrings() throws IOException
{       
    String forumContent = "<li class=\"icon-logout\"><a href=\"./ucp.php?mode=logout&amp;sid=3a4043284674572e35881e022c68fcd8\" title=\"Logout [ barry ]\" accesskey=\"x\">Logout [ barry ]</a></li>";
    String sidPattern = "&amp;sid=";

    int sidIndex = forumContent.indexOf(sidPattern);
    assertEquals(54, sidIndex);

    forumContent = forumContent.replaceAll(sidPattern, "");
    sidIndex = forumContent.indexOf(sidPattern);
    assertEquals(-1, sidIndex);
}

这是否与array / arrayList方法一样有效?

谢谢, 百里

3 个答案:

答案 0 :(得分:5)

您可以使用List#toArray()将任何列表转换为数组。

在这个特定用例中,事情有点复杂,因为在转换列表时没有优雅的方式来自动解包(从Bytebyte)。好的'Java泛型。这是一个很好的细节...

  

所以我不得不创建一个Byte的ArrayList,因为我不能拥有原始字节的ArrayList(有人可以告诉我为什么吗?)

因为在Java中,泛型类型参数不能是原语。见Why can Java Collections not directly store Primitives types?


旁注:作为一种风格问题,您几乎应该始终将ArrayList类型声明为List

List<Byte> forumContentList = new ArrayList<Byte>();

请参阅Java - declaring from Interface type instead of ClassType List vs type ArrayList in Java

答案 1 :(得分:3)

  

一切正常,我担心清洁部分的效率......

真的?你检查了结果“字符串”了吗?在我的机器上,forumContentCleaned中的数据仍包含&amp;sid=...数据。

那是因为

forumContentList.removeAll(Arrays.asList(sidPattern));

尝试从List<byte[]>中删除List<Byte>。这无济于事。即使您将removeAll的参数替换为包含List<Byte>字节的真实"&amp;sid=",您也会删除每个a所有次出现},每个m,每个p等等。结果数据如下所示:

<l cl"con-logout">< href"./uc.h?oelogout34043284674572e35881e022c68fc8" ttle....

嗯,严格来说,&amp;sid=部分已经消失,但我很确定这不是你想要的。

因此,退一步思考:你在这里进行字符串操作,所以使用StringBuilder,用String(forumContent)提供它并在那里进行操作。

修改

查看给定的示例输入字符串,我想,还应删除sid,而不仅仅是键。此代码应该在没有常规表达的情况下有效地执行:

String removeSecrets(String input){
    StringBuilder sb = new StringBuilder(input);

    String sidStart = "&amp;sid=";
    String sidEnd = "\"";

    int posStart = 0;
    while ((posStart = sb.indexOf(sidStart, posStart)) >= 0) {
        int posEnd = sb.indexOf(sidEnd, posStart);
        if (posEnd < 0)     // delete as far as possible - YMMV
            posEnd = sb.length();
        sb.delete(posStart, posEnd);
    }

    return sb.toString();
}

修改2

以下是StringBuilderString.replaceAll之间的一个小基准:

public class ReplaceAllBenchmark {
    public static void main(String[] args) throws Throwable {
        final int N = 1000000;
        String input = "<li class=\"icon-logout\"><a href=\"./ucp.php?mode=logout&amp;sid=3a4043284674572e35881e022c68fcd8\" title=\"Logout [ barry ]\" accesskey=\"x\">Logout [ barry ]</a>&amp;sid=3a4043284674572e35881e022c68fcd8\"</li>";

        stringBuilderBench(input, N);
        regularExpressionBench(input, N);
    }

    static void stringBuilderBench(String input, final int N) throws Throwable{
        for(int run=0; run<5; ++run){
            long t1 = System.nanoTime();
            for(int i=0; i<N; ++i)
                removeSecrets(input);
            long t2 = System.nanoTime();
            System.out.println("sb: "+(t2-t1)+"ns, "+(t2-t1)/N+"ns/call");
            Thread.sleep(1000);
        }
    }

    static void regularExpressionBench(String input, final int N) throws Throwable{
        for(int run=0; run<5; ++run){
            long t1 = System.nanoTime();
            for(int i=0; i<N; ++i)
                removeSecrets2(input);
            long t2 = System.nanoTime();
            System.out.println("regexp: "+(t2-t1)+"ns, "+(t2-t1)/N+"ns/call");
            Thread.sleep(1000);
        }
    }

    static String removeSecrets2(String input){
        return input.replaceAll("&amp;sid=[^\"]*\"", "\"");
    }
}

结果:

java version "1.6.0_20"
OpenJDK Runtime Environment (IcedTea6 1.9.9) (6b20-1.9.9-0ubuntu1~10.04.2)
OpenJDK 64-Bit Server VM (build 19.0-b09, mixed mode)

sb: 538735438ns, 538ns/call
sb: 457107726ns, 457ns/call
sb: 443282145ns, 443ns/call
sb: 453978805ns, 453ns/call
sb: 458895308ns, 458ns/call
regexp: 2404818405ns, 2404ns/call
regexp: 2196834572ns, 2196ns/call
regexp: 2239056178ns, 2239ns/call
regexp: 2164337638ns, 2164ns/call
regexp: 2177091893ns, 2177ns/call

答案 2 :(得分:1)

我不认为两个代码具有相同的功能。

第一个代码从forumContent中删除sidPattern中的所有字符。 第二个代码从forumContnt中删除sidPattern字符串,可能不起作用,导致replaceAll()接受该参数作为正则表达式模式。

你确定要删除“&amp; sid =”而不是“&amp; sid = 3a4043284674572e35881e022c68fcd8”吗?

无论如何,我认为String很好,List有点重。