2015-01-16 25 views
8

Bir Integers akışı var ve elementlerin indekslerini her elementin değerine göre gruplamak istiyorum.
Örneğin, {1, 1, 1, 2, 3, 3, 4} endeksler haritalama Listeye tamsayı olarak gruplandırılır:Java 8, Tamsayı Akışı, Bir Akımın Integers Tarafından Gruplandırılması?

1 -> 0, 1, 2 
2 -> 3 
3 -> 4, 5 
4 -> 6 

I, ancak ek bir sınıfı ile, akışı kullanılarak çalıştık:

@Test 
public void testGrouping() throws Exception { 
    // actually it is being read from a disk file 
    Stream<Integer> nums = Stream.of(1, 1, 1, 2, 3, 3, 4); 
    // list to map by index 
    int[] ind = {0}; // capture array, effectively final 
    class Pair { 
     int left; 
     int right; 

     public Pair(int left, int right) { 
      this.left = left; 
      this.right = right; 
     } 
    } 

    Map<Integer, List<Integer>> map = nums.map(e -> new Pair(ind[0]++, e)) 
      .collect(Collectors.groupingBy(e -> e.right)) 
      .entrySet().parallelStream() 
      .collect(Collectors.toConcurrentMap(
        Map.Entry::getKey, 
        e -> e.getValue().parallelStream().map(ee -> ee.left).collect(Collectors.toList()) 
      )); 
} 

I Yayın tarihi Akış okumak zorunda Tamsayı uygulamasında bir disk dosyasından okunur.
Yukarıda yaptığım gibi oldukça iyi bir şekilde optimal yapıyorum. Bunu yapmak için daha iyi veya daha zarif bir yolu var mı?
Yardımlarınız için teşekkürler.

+0

kullanarak: http://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors .html # groupingBy-java.util.function.Function-java.util.stream.Collector-? (Üzgünüz, şu anda detaylandırmak için Java8'e sahip olmayın) – GPI

cevap

4

biraz yardımcı yöntemle toplanması için:

class MapAndIndex { 
    Map<Integer,List<Integer>> map=new HashMap<>(); 
    int index; 

    void add(int value) { 
     map.computeIfAbsent(value, x->new ArrayList<>()).add(index++); 
    } 
    void merge(MapAndIndex other) { 
     other.map.forEach((value,list) -> { 
      List<Integer> l=map.computeIfAbsent(value, x->new ArrayList<>()); 
      for(int i: list) l.add(i+index); 
     }); 
     index+=other.index; 
    } 
} 

tüm operasyon haline gelir:

Map<Integer,List<Integer>> map = IntStream.of(1, 1, 1, 2, 3, 3, 4) 
    .parallel() 
    .collect(MapAndIndex::new, MapAndIndex::add, MapAndIndex::merge).map; 

Önceden bilinmeyen indeksleri takip etmeniz gerektiğinde, değişebilir duruma ve dolayısıyla “mutable reduction” olarak adlandırılan işleme ihtiyacınız vardır.

Burada ConcurrentMap'a ihtiyacınız olmadığını unutmayın. Stream uygulaması eşzamanlılığı eşzamanlı olarak ele alacaktır. İlgili her bir iş parçacığı için bir MapAndIndex kapsayıcı oluşturur ve her ikisi de ilişkili iş parçacıkları işlerini tamamladıktan sonra iki kapsayıcıda merge işlemini çağırır. Bu, Stream'un bu örnekte olduğu gibi bir sıralaması varsa, siparişi koruyacak şekilde de yapılacaktır (aksi takdirde, indeks kaydetme görevi hiçbir anlam ifade etmez…).

+1

Plus "değişebilir azaltma" –

2
  1. Her öğenin indeksini almak için IntStream#range(int startInclusive, int endExclusive) yöntemini kullanabilirsiniz.
  2. sonra dizi i -> array[i] karşılık gelen elemanına her bir indeks olarak eşlenmesi ve bir liste halinde tekrarlanan elemanları toplayarak IntStream kutulu Integer s
  3. Grup Stream bir dönüştürmek IntStream.boxed() yöntemini kullanmak.

    int[] array = {1, 1, 1, 2, 3, 3, 4}; 
    Map<Integer, List<Integer>> result = 
         IntStream.range(0, array.length) 
           .boxed() 
           .collect(Collectors.groupingBy(i -> array[i], Collectors.toList())); 
    

    Güncelleme:

Örneğin

sizin (ve dolayısıyla elemanları saymak) dizi yoksa, ama bir Stream<Integer>, sen ait elemanlarını toplayabilir İlk Stream, bir List<Integer> içine. Eğer Stream boyutunu bilmek ve Bu şekilde o zaman yapabilirsiniz:

Stream<Integer> = .... // The input stream goes here 
//Collecting the input stream to a list, so that we get it's size. 
List<Integer> list = stream.collect(Collectors.toList()); 
//Grouping process 
Map<Integer, List<Integer>> result = 
    IntStream.range(0, list.size()) 
      .boxed() 
      .collect(Collectors.groupingBy(i -> list.get(i), Collectors.toList())); 
+0

Eğer dizi uzunluğunu elden bilmiyorsam ne olur? – Daniel

+0

Ve orijinal dizinin kendisine ancak akışa rasgele erişemiyorum. – Daniel

+0

Yani bir "Stream " ve başka bir şey değil, doğru mu? –

0

Ne yapabilirsiniz Bu da Listesine eklenmeden önce bir haritalayan elemanlarını uygulamak için izin verir

Map<Integer, List<Integer>> map = nums.map(e -> new Pair(ind[0]++, e)) 
     .collect(groupingBy(p -> p.right, HashMap::new, 
          mapping(p -> p.left, toList()))); 

olduğunu.

1

Neden:

Stream<Integer> nums = Stream.of(1, 1, 1, 2, 3, 3, 4); 

OfInt indexes = IntStream.iterate(0, x -> x + 1).iterator(); 
Map<Integer, List<Integer>> result = new HashMap<>(); 

nums.iterator().forEachRemaining(i -> result.merge(i, 
                new ArrayList<>(Arrays.asList(indexes.next())), 
                (l1, l2) -> {l1.addAll(l2); return l1;}) 
           ); 

Sonuç: Belki

{1=[0, 1, 2], 2=[3], 3=[4, 5], 4=[6]}