2012-02-03 28 views
47

我有一個char [],我想將每個索引的值設置爲相同的char值。
有明顯的方式做到這一點(迭代):設置數組的所有值的最快方法?

char f = '+'; 
    char [] c = new char [50]; 
    for(int i = 0; i < c.length; i++){ 
     c[i] = f; 
    } 

但我在想,如果有,我可以利用System.arraycopy或等價的東西會繞過需要遍歷的方式。有沒有辦法做到這一點?

編輯:Arrays.java

public static void fill(char[] a, int fromIndex, int toIndex, char val) { 
     rangeCheck(a.length, fromIndex, toIndex); 
     for (int i = fromIndex; i < toIndex; i++) 
      a[i] = val; 
    } 

這是完全一樣的過程,這表明可能沒有更好的辦法來做到這一點。
無論如何,建議每個人建議使用fill +1 - 你們都是對的,謝謝。

+0

在編的JDK代碼的版本顯示,在JDK的某些版本做了「調酒」:外部標記的地方表示數組邊界檢查應在方法繞過,然後明確在循環之外添加邊界檢查。這提供了顯着的性能提升,因爲邊界檢查本身不僅昂貴,而且使其他優化複雜化。 – 2012-02-03 17:19:02

+0

@Bombe它是一個自定義的密碼字段,所以我必須用''''''動態地替換文檔中的每個'char' - 這意味着它必須儘可能的快速響應。可能會說你爲什麼不爲每個指數設定價值?這是爲了和'drawString'一起使用,所以我可以反編譯•的文本。 「填充」似乎運作良好。 :) – rtheunissen 2012-02-04 00:28:19

+0

@ paranoid-android,所以你確實有能夠輸入每秒超過1000個字符的用戶?我很佩服。 – Bombe 2012-02-04 02:14:28

回答

72

嘗試Arrays.fill(c, f)Arrays javadoc

+10

Arrays.fill的源代碼表明它只是一個循環(預先加上範圍檢查)。應該通過基準測試來查看JVM是否做了巧妙的改變... – DNA 2012-02-03 12:40:43

+0

如果它是一個二維數組呢? – 2016-10-13 14:19:05

9

使用Arrays.fill

char f = '+'; 
    char [] c = new char [50]; 
    Arrays.fill(c, f) 
1

Arrays.fill可能會滿足您的需求

1

Arrays.fill(myArray, 'c');

Arrays.fill

儘管很可能這是在後臺執行循環,因此沒有比您擁有的更高效(除了節省代碼行)。如果你真的關心效率,儘量在比較下上面:

int size = 50; 
char[] array = new char[size]; 
for (int i=0; i<size; i++){ 
    array[i] = 'c'; 
} 

注意,上面並沒有調用array.size()每個迭代。

+0

對array.length的引用很便宜(不是一個調用),並且很容易在循環中進行優化。 – 2012-02-03 12:47:26

+0

@HotLicks你確定編譯器這樣做嗎?數組引用不可能在循環內改變(以及大小)嗎?因此,編譯器是否可以對此進行優化?我想如果它足夠聰明以確保數組引用在循環內部不被修改,它可以這樣做。再次,你有一些知識,這種優化正在完成? – 2012-02-03 12:50:14

+0

@HotLicks我可以假設你的語句持有數組而不是集合嗎? – 2012-02-03 12:50:58

2

Arrays.fill方法:

char f = '+'; 
char [] c = new char [50]; 
Arrays.fill(c, f); 
3

如果你有焦炭,char[] b的另一個數組,你想用b更換c,您可以使用c=b.clone();

+0

或者,如果您的數組長度可變,則創建一個「超長」原型並使用System.arraycopy。要麼會有效地在封面下面做一個'memcpy'。 – 2012-02-03 12:42:37

+0

@HotLicks有趣... – Dragos 2012-02-03 13:01:05

1
/** 
    * Assigns the specified char value to each element of the specified array 
    * of chars. 
    * 
    * @param a the array to be filled 
    * @param val the value to be stored in all elements of the array 
    */ 
    public static void fill(char[] a, char val) { 
     for (int i = 0, len = a.length; i < len; i++) 
      a[i] = val; 
    } 

這就是Arrays.fill所做的。

(我想你可以拖放到JNI和使用memset

0

你可以使用arraycopy但它取決於你是否可以預定義源陣列, - 你需要一個不同的角色,每次加滿,或者是你用同一個char重複填充數組?

顯然填充的長度很重要 - 要麼你需要一個比所有可能的目的地都大的源,或者你需要一個循環來重複地對一塊數據進行陣列複製直到目的地已滿。

char f = '+'; 
    char[] c = new char[50]; 
    for (int i = 0; i < c.length; i++) 
    { 
     c[i] = f; 
    } 

    char[] d = new char[50]; 
    System.arraycopy(c, 0, d, 0, d.length); 
6

Java Programmer's FAQ Part B Sect 6表明:

public static void bytefill(byte[] array, byte value) { 
    int len = array.length; 
    if (len > 0) 
    array[0] = value; 
    for (int i = 1; i < len; i += i) 
     System.arraycopy(array, 0, array, i, 
      ((len - i) < i) ? (len - i) : i); 
} 

這實質上使得LOG2(array.length)調用System.arraycopy其中希望利用一個優化的memcpy實現。

但是,這種技術仍然需要現代Java JIT,如Oracle/Android JIT?

+0

我找到了教程,但沒有具體的鏈接 - 你知道它可能在哪裏嗎? – Karussell 2015-07-20 18:59:26

+0

在我的答案中有更多關於此方法的深入討論的鏈接@Karussell – 2016-01-22 06:22:31

4

System.arraycopy是我的答案。請讓我知道有沒有更好的方法。 THX

private static long[] r1 = new long[64]; 
private static long[][] r2 = new long[64][64]; 

/**Proved: 
* {@link Arrays#fill(long[], long[])} makes r2 has 64 references to r1 - not the answer; 
* {@link Arrays#fill(long[], long)} sometimes slower than deep 2 looping.<br/> 
*/ 
private static void testFillPerformance() { 
    SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); 
    System.out.println(sdf.format(new Date())); 
    Arrays.fill(r1, 0l); 

    long stamp0 = System.nanoTime(); 
    //  Arrays.fill(r2, 0l); -- exception 
    long stamp1 = System.nanoTime(); 
    //  System.out.println(String.format("Arrays.fill takes %s nano-seconds.", stamp1 - stamp0)); 

    stamp0 = System.nanoTime(); 
    for (int i = 0; i < 64; i++) { 
     for (int j = 0; j < 64; j++) 
      r2[i][j] = 0l; 
    } 
    stamp1 = System.nanoTime(); 
    System.out.println(String.format("Arrays' 2-looping takes %s nano-seconds.", stamp1 - stamp0)); 

    stamp0 = System.nanoTime(); 
    for (int i = 0; i < 64; i++) { 
     System.arraycopy(r1, 0, r2[i], 0, 64); 
    } 
    stamp1 = System.nanoTime(); 
    System.out.println(String.format("System.arraycopy looping takes %s nano-seconds.", stamp1 - stamp0)); 

    stamp0 = System.nanoTime(); 
    Arrays.fill(r2, r1); 
    stamp1 = System.nanoTime(); 
    System.out.println(String.format("One round Arrays.fill takes %s nano-seconds.", stamp1 - stamp0)); 

    stamp0 = System.nanoTime(); 
    for (int i = 0; i < 64; i++) 
     Arrays.fill(r2[i], 0l); 
    stamp1 = System.nanoTime(); 
    System.out.println(String.format("Two rounds Arrays.fill takes %s nano-seconds.", stamp1 - stamp0)); 
} 

十二時33分十八秒
陣列2-循環需要133536毫微秒。
System.arraycopy循環需要22070納秒。
一輪Arrays.fill需要9777納秒。
兩輪Arrays.fill需要93028納秒。

12:33:38
陣列的2-looping需要133816納秒。
System.arraycopy循環需要22070納秒。
一輪Arrays.fill需要17042納秒。
兩輪Arrays.fill需要95263納秒。

12:33:51
陣列的2循環需要199187納秒。
System.arraycopy循環需要44140納秒。
一輪Arrays.fill需要19555納秒。
兩輪Arrays.fill需要449219納秒。

12:34:16
數組的2循環需要199467納秒。
System.arraycopy循環需要42464納秒。
一輪Arrays.fill需要17600納秒。
兩輪Arrays.fill需要170971納秒。

12:34:26
陣列的2迴路需要198907納秒。
System.arraycopy循環需要24584納秒。
一輪Arrays.fill需要10616納秒。
兩輪Arrays.fill需要94426納秒。

+5

當您的日誌聲明Arrays.fill(...)每次運行速度更快時,爲什麼選擇System.arraycopy(...)?一般來說,它速度非常快! – ingyhere 2014-04-29 09:38:28

32

作爲另一種選擇和對子孫後代的我一直在尋找這個最近發現this物品,其提供了一個解決方案,允許更短的循環被移交一些工作,關閉的System類,它(如果JVM你」重新使用是足夠聰明)可以變成一個memset操作: -

/* 
* initialize a smaller piece of the array and use the System.arraycopy 
* call to fill in the rest of the array in an expanding binary fashion 
*/ 
public static void bytefill(byte[] array, byte value) { 
    int len = array.length; 

    if (len > 0){ 
    array[0] = value; 
    } 

    for (int i = 1; i < len; i += i) { 
    System.arraycopy(array, 0, array, i, ((len - i) < i) ? (len - i) : i); 
    } 
} 

這種溶液從IBM研究論文"Java server performance: A case study of building efficient, scalable Jvms" by R. Dimpsey, R. Arora, K. Kuiper拍攝。

簡化解釋

作爲評論表明,這會將目標數組到您的值的索引0然後使用System類索引0處一個對象複製即對象到索引1,則這兩個對象(索引0和1)分成2和3,然後將這四個對象(0,1,2和3)分成4,5,6和7等等...

效率(寫作時)

在快速運行中,前後抓住System.nanoTime()和計算時間,我想出了: -

  • 這種方法:332617 - 390262( '最高 - 最低' 10測試)
  • Float[] n = new Float[array.length]; //Fill with null:666650
  • 設置通過循環: - ( - 從10個測試 '最高最低')
  • Arrays.fill

JVM和JIT編譯

應當注意的是,JVM和JIT的發展,這種做法很可能成爲過時的庫和運行的優化可以達到甚至乾脆使用超過這些數字fill()。 在撰寫本文時,這是我找到的最快選項。有人提到現在可能並非如此,但我沒有檢查過。這是Java的美麗和詛咒。

0

Arrays.fill是一般用途的最佳選擇。 如果你需要填寫大型數組,但最新的idk 1.8 u102,有一個更快的方式,利用System.arraycopy。 你可以看看這個替代Arrays.fill實現:

按照JMH benchmarks,你可以得到較大的陣列(1000 +)

在任何情況下,幾乎2倍的性能提升,這些實現應只在需要的地方使用。 JDKs Arrays.fill應該是首選。

0

從Java-8開始,有四種方法setAll設置指定數組的所有元素,使用提供的生成器函數來計算每個元素。

這四個重載只有其中接受聲明爲這樣的原語的數組:





如何使用上述方法的實例:

// given an index, set the element at the specified index with the provided value 
double [] doubles = new double[50]; 
Arrays.setAll(doubles, index -> 30D); 

// given an index, set the element at the specified index with the provided value 
int [] ints = new int[50]; 
Arrays.setAll(ints, index -> 60); 

// given an index, set the element at the specified index with the provided value 
long [] longs = new long[50]; 
Arrays.setAll(longs, index -> 90L); 

提供給setAll方法的函數接收元素索引並返回該索引的值。

你可能想知道字符數組怎麼樣?

這是setAll方法的第四次過載發揮作用的地方。由於沒有消耗字符基元數組的重載,我們唯一的選擇是將字符數組的聲明更改爲Character[]類型。

如果將數組的類型更改爲Character是不合適的,那麼您可以回退到Arrays.fill方法。

使用setAll方法與Character[]的實施例:

// given an index, set the element at the specified index with the provided value 
Character[] character = new Character[50]; 
Arrays.setAll(characters, index -> '+'); 

雖然,這是更簡單的使用方法Arrays.fill而非setAll方法設置特定值。

setAll方法具有既可以設置在陣列的所有元素具有相同的值或生成偶數,奇數或任何其他式的陣列的優點是:

例如

int[] evenNumbers = new int[10]; 
Arrays.setAll(evenNumbers, i -> i * 2); 

還有其並行執行的parallelSetAll方法的幾個重載,但要注意的是傳遞給parallelSetAll方法功能必須是自由副作用是非常重要的。

結論

如果你的目標僅僅是來設置然後使用Arrays.fill重載將是最合適的選擇數組中的每個元素的特定值。但是,如果您想要更靈活或按需生成元素,則可以使用Arrays.setAllArrays.parallelSetAll(如果適用)。

0

我對Ross Drew的答案有了一點小小的改進。

對於小型數組,由於與設置System.arraycopy相關的開銷,所以一個簡單的循環比System.arraycopy方法快。因此,最好使用一個簡單的循環來填充數組的前幾個字節,並且只有在填充數組具有一定大小時才移動到System.arraycopy。

當然,初始循環的最佳大小將是JVM特定的和系統特定的。

private static final int SMALL = 16; 

public static void arrayFill(byte[] array, byte value) { 
    int len = array.length; 
    int lenB = len < SMALL ? len : SMALL; 

    for (int i = 0; i < lenB; i++) { 
    array[i] = value; 
    } 

    for (int i = SMALL; i < len; i += i) { 
    System.arraycopy(array, 0, array, i, len < i + i ? len - i : i); 
    } 
} 
相關問題