如何在Groovy中“切片”一个集合

时间:2011-05-03 17:53:34

标签: groovy

我有一组对象,我想分成一组集合,其中每个连续的3个元素组都在一个集合中。 例如,如果我有

def l = [1,4,2,4,5,9]

我想把它变成:

def r = [[1,4,2], [4,5,9]]

我现在正在通过迭代集合并将其分解来实现它。但是我需要将这些'组'传递给处理它们的并行化函数。消除这个O(n)会很好预处理工作,只需说出像

这样的内容
l.slice(3).collectParallel { subC -> process(subC) }

我在Range类上找到了step方法,但看起来它只对索引起作用。任何聪明的想法?

更新: 我不认为这是引用链接的重复,尽管它非常接近。如下所示,它更像是我正在寻找的迭代器类型的东西..子集合将被传递到GPars collectParallel。理想情况下,我不需要分配整个新集合。

4 个答案:

答案 0 :(得分:21)

查看groovy 1.8.6。 List上有一个新的整理方法。

def list = [1, 2, 3, 4]
assert list.collate(4) == [[1, 2, 3, 4]] // gets you everything   
assert list.collate(2) == [[1, 2], [3, 4]] //splits evenly
assert list.collate(3) == [[1, 2, 3], [4]] // won't split evenly, remainder in last list.

请查看Groovy List documentation以获取更多信息,因为还有其他一些参数可以为您提供其他选项,包括删除剩余部分。

就您的并行处理而言,您可以使用gpars浏览列表。

def list = [1, 2, 3, 4, 5]
GParsPool.withPool {
  list.collate(2).eachParallel {
     println it
  }
}

答案 1 :(得分:2)

如果我理解正确,您当前正在将原始集合中的元素复制到子集合中。有关这些方面的更多建议,请查看以下问题的答案:Split collection into sub collections in Groovy

听起来你正在寻找的是一种让子集有效地成为原始集合视图的方式。如果是这种情况,请查看List.subList() method。您可以将索引从0循环到size(),以3为增量(或您选择的任何切片大小),或者您可以获得更好的并构建一个Iterable / List,它将隐藏调用者的详细信息。这是后者的实现,受Ted's answer启发。

class Slicer implements Iterator {
  private List backingList
  private int sliceSize
  private int index

  Slicer(List backingList, int sliceSize) {
    this.backingList = backingList
    this.sliceSize = sliceSize
  }

  Object next() {
    if (!hasNext()) {
      throw new NoSuchElementException()
    }

    def ret
    if (index + sliceSize <= backingList.size()) {
      ret = backingList.subList(index, index+sliceSize)
    } else if (hasNext()) {
      ret = backingList.subList(index, backingList.size())
    }
    index += sliceSize
    return ret
  }

  boolean hasNext() {
    return index < backingList.size()
  }

  void remove() {
    throw new UnsupportedOperationException() //I'm lazy ;)
  }
}

答案 2 :(得分:1)

我喜欢这两种解决方案,但这是我非常喜欢的第一种解决方案的略微改进版本:

class Slicer implements Iterator {
private List backingList
private int sliceSize
private int index

Slicer(List backingList, int sliceSize) {
  this.backingList = backingList;
  int ss = sliceSize;

  // negitive sliceSize = -N means, split the list into N equal (or near equal) pieces  
  if( sliceSize < 0) {
      ss = -sliceSize;
      ss = (int)((backingList.size()+ss-1)/ss);
  }
  this.sliceSize = ss
}

Object next() {
  if (!hasNext()) {
    throw new NoSuchElementException()
  }

  def ret = backingList.subList(index, Math.min(index+sliceSize , backingList.size()) );
  index += sliceSize
  return ret
  }

  boolean hasNext() {
    return index < backingList.size() - 1
  }

  void remove() {
    throw new UnsupportedOperationException() //I'm lazy ;)
  }

  List asList() {
    this.collect { new ArrayList(it) }
  }

  List flatten() {
    backingList.asImmutable()
  }

}

// ======== TESTS

    def a = [1,2,3,4,5,6,7,8];
    assert  [1,2,3,4,5,6,7,8] == a;
    assert [[1, 2], [3, 4], [5, 6], [7, 8]] ==  new Slicer(a,2).asList(); 
    assert [[1,2,3], [4,5,6], [7,8]] == (new Slicer(a,3)).collect { it } // alternative to asList but inner items are subList
    assert [3, 2, 1, 6, 5, 4, 8, 7] == ((new Slicer(a,3)).collect { it.reverse() } ).flatten()

    // show flatten iterator
    //new Slicer(a,2).flattenEach { print it }
    //println ""

    // negetive slice into N pieces, in this example we split it into 2 pieces
    assert [[1, 2, 3, 4], [5, 6, 7, 8]] ==  new Slicer(a,-2).collect { it as List }  // same asList
    assert [[1, 2, 3], [4, 5, 6], [7, 8]] == new Slicer(a,-3).asList()
    //assert a == (new Slicer(a,3)).flattenCollect { it } 
    assert [9..10, 19..20, 29..30] == ( (new Slicer(1..30,2)).findAll { slice -> !(slice[1] % 10) } )
    assert [[9, 10], [19, 20], [29, 30]] == ( (new Slicer(1..30,2)).findAll { slice -> !(slice[1] % 10) }.collect { it.flatten() } )

    println( (new Slicer(1..30,2)).findAll { slice -> !(slice[1] % 10) } )
    println( (new Slicer(1..30,2)).findAll { slice -> !(slice[1] % 10) }.collect { it.flatten() } )

答案 3 :(得分:0)

没有内置任何内容可以完全按照你想要的方式执行,但是如果我们@Delegate调用本机列表的迭代器,我们就可以编写自己的类,它就像一个返回块的迭代器一样工作。寻找:

class Slicer {
    protected Integer sliceSize 
    @Delegate Iterator iterator

    Slicer(objectWithIterator, Integer sliceSize) {
        this.iterator = objectWithIterator.iterator()
        this.sliceSize = sliceSize
    }

    Object next() {
        List currentSlice = []
        while(hasNext() && currentSlice.size() < sliceSize) {
            currentSlice << this.iterator.next()
        }
        return currentSlice
    }
}

assert [[1,4,2], [4,5,9]] == new Slicer([1,4,2,4,5,9], 3).collect { it }

因为它具有普通迭代器所做的所有方法,所以你可以免费获得groovy语法糖方法,并对任何具有iterator()方法的方法进行惰性求值,如范围:

assert [5,6] == new Slicer(1..100, 2).find { slice -> slice.first() == 5 }

assert [[9, 10], [19, 20], [29, 30]] == new Slicer(1..30, 2).findAll { slice -> !(slice[1] % 10) }