簡體   English   中英

為什么在 Java 中將變量重新分配給新字符串需要這么長時間?

[英]Why does reassigning a variable to a new String take so much time in Java?

幾天前,我意識到我從來沒有做過任何測量程序運行時間的實驗。 為了好玩,我決定使用System.currentTimeMillis()方法測試一些隨機的代碼行。 我決定嘗試一些簡單的事情,比如僅僅不斷地重新分配一個變量。 所以,我運行了以下代碼:

public class Timer{
    public static void main(String[] args) {
        String s = null;

        final long startTime = System.currentTimeMillis(); // Start the timer

        for (int i = 1; i <= 999999999; i++) { 
            s = i + "Hello";  // This takes around 36 seconds!
        }

        System.out.println(s);

        final long endTime = System.currentTimeMillis(); // End the timer

        System.out.println("Total execution time: " + (endTime - startTime)); // Report total time elapsed
    }
}

我很驚訝地看到,僅僅不斷地重新分配一個String變量會花費這么長時間。 我知道我要迭代的數字本身就非常大,但是當我運行其他仍然涉及到那個巨大數字的 for 循環的代碼時,程序運行所需的時間顯着降低. 例如:

import java.util.ArrayList;

public class TimerArrayList {
    public static void main(String[] args) {

        ArrayList<Integer> list = new ArrayList<Integer>();

        final long startTime = System.currentTimeMillis(); // Start the timer

        // Adding elements to an ArrayList is generally quick (around 4 seconds)
        for (int i = 1; i <= 999999999; i++) {
            list.add(i);
            if (list.size() == 50000)
                list.clear(); // To prevent OutOfMemoryError
        }
        final long endTime = System.currentTimeMillis(); // End the timer
        System.out.println("Total execution time: " + (endTime - startTime)); // Report total time elapsed
    }
}

將元素添加到 ArrayList 僅需大約 4 秒。 甚至更快,但只是在數數。 例如,這段代碼平均需要 1.5 毫秒來運行:

public class TimerCounting{
    public static void main(String[] args) {
        final long startTime = System.currentTimeMillis(); // Start the timer

        // Just counting up is super quick (around 1 millisecond)
        int counter = 0;
        for (int i = 1; i <= 999999999; i++) {
            counter = i;
        }

        final long endTime = System.currentTimeMillis(); // End the timer

        System.out.println("Total execution time: " + (endTime - startTime)); // Report total time elapsed
    }
}

所以,我的問題是:為什么重新分配一個String變量需要這么長時間?

重新分配字符串是一項代價高昂的操作,每次我們重新分配一個字符串時,由於字符串的不可變特性,實際上會在后台發生一些操作。

這就是為什么多次串聯字符串是一個壞主意的原因。 每次連接時,您的應用程序都會隱式創建一個新字符串,為此我們有 StringBuffer/StringBuilder

情況1:

for (int i = 1; i <= 999999999; i++) {
            counter = i;
}

您沒有將任何新的 memory 分配給變量counter 因此,在每次迭代中運行counter = i只需要 1 個 cpu 周期。

案例二:

for (int i = 1; i <= 999999999; i++) {
    list.add(i);
    if (list.size() == 50000)
       list.clear(); // To prevent OutOfMemoryError
}

在列表中添加一個元素通常需要超過 1 個 cpu 周期。 它首先在 memory 中搜索空閑空間,為列表中的新元素分配空間,然后執行一些代碼將新元素實際添加到鏈表的末尾。

實際上,整體耗時應該超過4 秒 但是當您批量執行此操作時(允許在第 50000 個元素之后對舊的 memory 進行垃圾收集),由於頁面交換較少,這將減少為新元素尋找新空間的時間。

案例3:


 for (int i = 1; i <= 999999999; i++) { 
   s = i + "Hello";  // This takes around 36 seconds!
 }

這會從堆中分配 memory,因為每次都會創建具有不同 i 值的新字符串。

可能在您的情況下,它會在創建新字符串文字時不斷增加堆空間,直到下一個垃圾收集器周期運行。 (與鏈表的情況不同,其中 memory 在遇到第 50000 個元素時被清除。)

希望這在某種程度上清除!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM