2014-09-26 19 views
7

Java 8 için yeni ve Akımlar tam olarak anlaşılamadı, Akış işlevlerini kullanarak bir diziyi doldurmak mümkün mü? ben o Akışı kullanarak bunu nasıl mümkün değilseBir Akış kullanarak Çok Boyutlu Bir Diziyi Doldurma

public static void testForLoop(){ 
    String[][] array = new String[3][3]; 
    for (int x = 0; x < array.length; x++){ 
     for (int y = 0; y < array[x].length; y++){ 
      array[x][y] = String.format("%c%c", letter(x), letter(y)); 
     } 
    }    
} 

public static char letter(int i){ 
    return letters.charAt(i); 
} 

: Bu benim döngü bir standarda yapacağını bir örnek kodudur? Mümkünse, uygun mu (performans ve okunabilirlik bilge)?

+0

Değil alakalı, ama iç döngü için 'dizi [x] .length' geliyordu düşünüyorum. –

+0

Evet Kesinlikle –

+0

yaptım Sadece standart bir döngü kullanın. Kodunuz okuyucuya basit ve açık bir şekilde açıktır. Bir akarsu çözümü kadar zarif göründüğü gibi, burada bir şey eklemediğini görmüyorum. –

cevap

12

Daha önce tanımlanmış değişkeni değiştirmek yerine dizi üreten bir çözüme sahip veya nesne). Yarış koşullarına veya diğer eşzamanlılık sorunlarına yol açabilir.Bu konuda daha fazlasını okuyabilirsin java.util.stream package documentation - bkz. Parazit olmayan, Durumsuz davranışlar ve Yan etkiler bölümler. olurdu ben teklif edilen spesifik durumda (

IntStream.range(0, array.length).forEach(x -> Arrays.setAll(array[x], y -> builder.build2Dobject(x, y))); 

:

bir 3d dizisi için
IntStream.range(0, array.length).forEach(x -> Arrays.setAll(array[x], y -> String.format("%c%c", letter(x), letter(y))); 

basitçe var

+0

Ben aslında bu kendimi bulduğum nihai çözümden biraz daha kıvrımlı gibi görünüyor, hangisinin daha hızlı olduğunu test etmeliyim. –

+0

Doğru - diğer çözümlerden daha az okunabilir ama paralel akımları kullanmak istediğinizde genellikle çok önemli olan dış değişkenleri değiştirmemesi avantajı - yarış koşullarını düşünün –

+0

Bu konuda daha fazlasını okuyabilirsiniz [java.util.stream] (https: // docs .oracle.com/javase/8/docs/api/java/util/stream/package-summary.html) paket belgeleri (* Non-interference *, * Durum bilgisi olmayan davranışlar * ve * Yan etkiler * bölümler.) –

2

Bunu yapmanın birkaç yolu vardır.

bir yolu satır ve sütun indeksleri üzerinden IntStreams yuvalanmış bir çift ile geçerli:

String[][] testStream() { 
    String[][] array = new String[3][3]; 
    IntStream.range(0, array.length).forEach(x -> 
     IntStream.range(0, array[x].length).forEach(y -> 
      array[x][y] = String.format("%c%c", letter(x), letter(y)))); 
    return array; 
} 

umut verici görünüyor başka yolu da akışlarının yerine Array.setAll kullanmaktır. Bu, tek boyutlu bir dizi için değerler oluşturmak için mükemmeldir: dizi dizininden dizide atanmasını istediğiniz değere eşlenen bir işlev sağlarsınız. Örneğin, bunu yapabilirsiniz: Bu, çok boyutlu diziler için daha az uygun olduğu için maalesef daha uygundur. Bu dizinde dizi konumuna atanan bir değer döndüren bir lambda alan setAll yöntemi. Çok boyutlu bir dizi oluşturduysanız, daha yüksek boyutlar daha düşük boyutlu dizilerle zaten başlatılır. Onlara atamak istemezsiniz, ancak setAll'un örtük döngü davranışını istiyorsunuz. Bu düşünceyle

, böyle çok boyutlu diziyi başlatmak için setAll kullanabilirsiniz:

static String[][] testArraySetAll() { 
    String[][] array = new String[3][3]; 
    Arrays.setAll(array, x -> { 
     Arrays.setAll(array[x], y -> String.format("%c%c", letter(x), letter(y))); 
     return array[x]; 
    }); 
    return array; 
} 

setAll makul güzel, ama dış bir iç setAll çağıran bir açıklama lambda olması gerekiyor ve sonra geçerli diziyi döndürür. Çok hoş değil.

Bu yaklaşımlardan herhangi birinin, tipik iç içe geçmiş döngülerden daha iyi olduğu açık değildir.

4

En iyi yol, Stuart Marks’ answer'un iki yaklaşımının birleşimidir.

IntStream.range(0, array.length).forEach(x -> Arrays.setAll(
    array[x], y -> String.format("%c%c", letter(x), letter(y)))); 

çözeltiye gelen mantık String[][] olarak “tek boyutlu dizi doldurma” bir takiben, “dış dizi (ler) i üzerinde yineleme” Java “çok-boyutlu bir dizi doldurma” terimi, bir Java'da sadece bir dizi String[] öğesi. Öğelerini ayarlamak için tüm String[] öğelerinde yineleme yapmanız gerekir ve son değeri hesaplamak için dizine ihtiyacınız olduğundan, Arrays.stream(array).forEach(…)'u kullanamazsınız. Dolayısıyla, dizinler üzerinde yinelenen dış dizi uygun olur.

İç diziler için arama, bir (tek boyutlu) dizinin değiştirilmesi için en iyi çözümdür. Burada Arrays.setAll(…,…) uygundur.

String[][] array = 
    IntStream.range(0, 3) 
      .mapToObj(x -> IntStream.range(0, 3) 
            .mapToObj(y -> String.format("%c%c", letter(x), letter(y))) 
            .toArray(String[]::new)) 
      .toArray(String[][]::new); 

bunu bir değişkenin değişiklikler gibi yan etkilerinden kaçınmak için çok önemli sonra paralel akışları kullanmak istiyorsanız

(dizi: Burada

0

çalışma ve bu çevrede test ettikten sonra ben iyi seçenek geldi edilmektedir:

IntStream.range(0, array.length).forEach(x -> IntStream.range(0, array[x].length).forEach(y -> Arrays.setAll(array[x][y], z -> builder.build3Dobject(x, y, z)))); 

bu program en hızlı seçeneği sağlayan koddur:

public static void fill2DArray(Object[][] array, Object2DBuilderReturn builder){ 
    int totalLength = array.length * array[0].length; 
    if (totalLength < 200){ 
     for(int x = 0; x < array.length; x++){ 
      for (int y = 0; y < array[x].length; y++){ 
       array[x][y] = builder.build2Dobject(x, y); 
      } 
     } 
    } else if (totalLength >= 200 && totalLength < 1000){ 
     IntStream.range(0, array.length).forEach(x -> Arrays.setAll(array[x], y -> builder.build2Dobject(x, y))); 
    } else { 
     IntStream.range(0, array.length).forEach(x -> Arrays.setAll(array[x], y -> builder.build2Dobject(x, y))); 
    } 
} 

işlevsel arayüzü:

@FunctionalInterface 
public interface Object2DBuilderReturn<T> { 
    public T build2Dobject(int a, int b); 
} 
korkunç
İlgili konular