美文网首页
Groovy开发套件-处理集合

Groovy开发套件-处理集合

作者: 程序员文集 | 来源:发表于2020-01-01 21:52 被阅读0次

    处理集合

    Groovy为各种集合类型提供本地支持,包括列表MapCollection。其中大多数基于Java集合类型,并以Groovy开发工具包中的其他方法修饰。

    2.1 List

    2.1.1 List语法

    您可以如下创建列表。请注意,[]是空列表表达式。

    def list = [5, 6, 7, 8]
    assert list.get(2) == 7
    assert list[2] == 7
    assert list instanceof java.util.List
    
    def emptyList = []
    assert emptyList.size() == 0
    emptyList.add(5)
    assert emptyList.size() == 1
    

    每个列表表达式都会创建java.util.List的实现。

    当然,列表可以用作构建另一个列表的源:

    def list1 = ['a', 'b', 'c']
    //construct a new list, seeded with the same items as in list1
    def list2 = new ArrayList<String>(list1)
    
    assert list2 == list1 // == checks that each corresponding element is the same
    
    // clone() can also be called
    def list3 = list1.clone()
    assert list3 == list1
    

    列表是对象的有序集合:

    def list = [5, 6, 7, 8]
    assert list.size() == 4
    assert list.getClass() == ArrayList     // the specific kind of list being used
    
    assert list[2] == 7                     // indexing starts at 0
    assert list.getAt(2) == 7               // equivalent method to subscript operator []
    assert list.get(2) == 7                 // alternative method
    
    list[2] = 9
    assert list == [5, 6, 9, 8,]           // trailing comma OK
    
    list.putAt(2, 10)                       // equivalent method to [] when value being changed
    assert list == [5, 6, 10, 8]
    assert list.set(2, 11) == 10            // alternative method that returns old value
    assert list == [5, 6, 11, 8]
    
    assert ['a', 1, 'a', 'a', 2.5, 2.5f, 2.5d, 'hello', 7g, null, 9 as byte]
    //objects can be of different types; duplicates allowed
    
    assert [1, 2, 3, 4, 5][-1] == 5             // use negative indices to count from the end
    assert [1, 2, 3, 4, 5][-2] == 4
    assert [1, 2, 3, 4, 5].getAt(-2) == 4       // getAt() available with negative index...
    try {
        [1, 2, 3, 4, 5].get(-2)                 // but negative index not allowed with get()
        assert false
    } catch (e) {
        assert e instanceof IndexOutOfBoundsException
    }
    

    2.1.2 以布尔表达式形式列出

    assert ![]             // an empty list evaluates as false
    
    //all other lists, irrespective of contents, evaluate as true
    assert [1] && ['a'] && [0] && [0.0] && [false] && [null]
    

    2.1.3 迭代列表

    通常通过调用each和eachWithIndex方法对列表的元素进行迭代,该方法在列表的每个项目上执行代码:

    [1, 2, 3].each {
        println "Item: $it" // `it` is an implicit parameter corresponding to the current element
    }
    ['a', 'b', 'c'].eachWithIndex { it, i -> // `it` is the current element, while `i` is the index
        println "$i: $it"
    }
    

    除了迭代之外,通过将其每个元素转换为其他元素来创建新列表通常也很有用。由于以下collect方法,该操作(通常称为映射)在Groovy中完成:

    assert [1, 2, 3].collect { it * 2 } == [2, 4, 6]
    
    // shortcut syntax instead of collect
    assert [1, 2, 3]*.multiply(2) == [1, 2, 3].collect { it.multiply(2) }
    
    def list = [0]
    // it is possible to give `collect` the list which collects the elements
    assert [1, 2, 3].collect(list) { it * 2 } == [0, 2, 4, 6]
    assert list == [0, 2, 4, 6]
    

    2.1.4 操作清单

    筛选和搜索
    Groovy开发套件包含增强的标准集合与务实的方法,其中一些在这里说明集合了很多方法:

    assert [1, 2, 3].find { it > 1 } == 2           // find 1st element matching criteria
    assert [1, 2, 3].findAll { it > 1 } == [2, 3]   // find all elements matching critieria
    
    assert ['a', 'b', 'c', 'd', 'e'].findIndexOf {      // find index of 1st element matching criteria
        it in ['c', 'e', 'g']
    } == 2
    
    assert ['a', 'b', 'c', 'd', 'c'].indexOf('c') == 2  // index returned
    assert ['a', 'b', 'c', 'd', 'c'].indexOf('z') == -1 // index -1 means value not in list
    
    assert ['a', 'b', 'c', 'd', 'c'].lastIndexOf('c') == 4
    
    assert [1, 2, 3].every { it < 5 }               // returns true if all elements match the predicate
    assert ![1, 2, 3].every { it < 3 }
    
    assert [1, 2, 3].any { it > 2 }                 // returns true if any element matches the predicate
    assert ![1, 2, 3].any { it > 3 }
    
    assert [1, 2, 3, 4, 5, 6].sum() == 21                // sum anything with a plus() method
    
    assert ['a', 'b', 'c', 'd', 'e'].sum {
        it == 'a' ? 1 : it == 'b' ? 2 : it == 'c' ? 3 : it == 'd' ? 4 : it == 'e' ? 5 : 0
        // custom value to use in sum
    } == 15
    
    assert ['a', 'b', 'c', 'd', 'e'].sum { ((char) it) - ((char) 'a') } == 10
    assert ['a', 'b', 'c', 'd', 'e'].sum() == 'abcde'
    assert [['a', 'b'], ['c', 'd']].sum() == ['a', 'b', 'c', 'd']
    
    // an initial value can be provided
    assert [].sum(1000) == 1000
    assert [1, 2, 3].sum(1000) == 1006
    
    assert [1, 2, 3].join('-') == '1-2-3'           // String joining
    
    assert [1, 2, 3].inject('counting: ') {
        str, item -> str + item                     // reduce operation
    } == 'counting: 123'
    
    assert [1, 2, 3].inject(0) { count, item ->
        count + item
    } == 6
    

    以下是惯用的Groovy代码,用于查找集合中的最大值和最小值:

    def list = [9, 4, 2, 10, 5]
    assert list.max() == 10
    assert list.min() == 2
    
    // we can also compare single characters, as anything comparable
    assert ['x', 'y', 'a', 'z'].min() == 'a'
    
    // we can use a closure to specify the sorting behaviour
    def list2 = ['abc', 'z', 'xyzuvw', 'Hello', '321']
    assert list2.max { it.size() } == 'xyzuvw'
    assert list2.min { it.size() } == 'z'
    

    除闭包外,您还可以使用aComparator定义比较条件:

    Comparator mc = { a, b -> a == b ? 0 : (a < b ? -1 : 1) }
    
    def list = [7, 4, 9, -6, -1, 11, 2, 3, -9, 5, -13]
    assert list.max(mc) == 11
    assert list.min(mc) == -13
    
    Comparator mc2 = { a, b -> a == b ? 0 : (Math.abs(a) < Math.abs(b)) ? -1 : 1 }
    
    
    assert list.max(mc2) == -13
    assert list.min(mc2) == -1
    
    assert list.max { a, b -> a.equals(b) ? 0 : Math.abs(a) < Math.abs(b) ? -1 : 1 } == -13
    assert list.min { a, b -> a.equals(b) ? 0 : Math.abs(a) < Math.abs(b) ? -1 : 1 } == -1
    

    添加或删除元素
    我们可以[]用来分配一个新的空列表并向<<其添加项目:

    def list = []
    assert list.empty
    
    list << 5
    assert list.size() == 1
    
    list << 7 << 'i' << 11
    assert list == [5, 7, 'i', 11]
    
    list << ['m', 'o']
    assert list == [5, 7, 'i', 11, ['m', 'o']]
    
    //first item in chain of << is target list
    assert ([1, 2] << 3 << [4, 5] << 6) == [1, 2, 3, [4, 5], 6]
    
    //using leftShift is equivalent to using <<
    assert ([1, 2, 3] << 4) == ([1, 2, 3].leftShift(4))
    

    我们可以通过多种方式添加到列表中:

    assert [1, 2] + 3 + [4, 5] + 6 == [1, 2, 3, 4, 5, 6]
    // equivalent to calling the `plus` method
    assert [1, 2].plus(3).plus([4, 5]).plus(6) == [1, 2, 3, 4, 5, 6]
    
    def a = [1, 2, 3]
    a += 4      // creates a new list and assigns it to `a`
    a += [5, 6]
    assert a == [1, 2, 3, 4, 5, 6]
    
    assert [1, *[222, 333], 456] == [1, 222, 333, 456]
    assert [*[1, 2, 3]] == [1, 2, 3]
    assert [1, [2, 3, [4, 5], 6], 7, [8, 9]].flatten() == [1, 2, 3, 4, 5, 6, 7, 8, 9]
    
    def list = [1, 2]
    list.add(3)
    list.addAll([5, 4])
    assert list == [1, 2, 3, 5, 4]
    
    list = [1, 2]
    list.add(1, 3) // add 3 just before index 1
    assert list == [1, 3, 2]
    
    list.addAll(2, [5, 4]) //add [5,4] just before index 2
    assert list == [1, 3, 5, 4, 2]
    
    list = ['a', 'b', 'z', 'e', 'u', 'v', 'g']
    list[8] = 'x' // the [] operator is growing the list as needed
    // nulls inserted if required
    assert list == ['a', 'b', 'z', 'e', 'u', 'v', 'g', null, 'x']
    

    但是,重要的是列表上的运算符+不要变异。与<<相比,它将创建一个新列表,这通常不是您想要的,并且可能导致性能问题。

    Groovy开发套件还包含让您轻松通过值删除列表中的元素的方法:

    assert ['a','b','c','b','b'] - 'c' == ['a','b','b','b']
    assert ['a','b','c','b','b'] - 'b' == ['a','c']
    assert ['a','b','c','b','b'] - ['b','c'] == ['a']
    
    def list = [1,2,3,4,3,2,1]
    list -= 3           // creates a new list by removing `3` from the original one
    assert list == [1,2,4,2,1]
    assert ( list -= [2,4] ) == [1,1]
    

    也可以通过将元素的索引传递给remove方法来删除元素,在这种情况下,列表会发生突变:

    def list = ['a','b','c','d','e','f','b','b','a']
    assert list.remove(2) == 'c'        // remove the third element, and return it
    assert list == ['a','b','d','e','f','b','b','a']
    

    如果您只想删除列表中具有相同值的第一个元素,而不是删除所有元素,则可以调用remove传递值的方法:

    def list= ['a','b','c','b','b']
    assert list.remove('c')             // remove 'c', and return true because element removed
    assert list.remove('b')             // remove first 'b', and return true because element removed
    
    assert ! list.remove('z')           // return false because no elements removed
    assert list == ['a','b','b']
    

    如您所见,有两种remove可用的方法。一个使用整数并通过其索引删除元素,另一个将删除与传递的值匹配的第一个元素。那么,当我们有一个整数列表时该怎么办?在这种情况下,您可能希望使用removeAt通过索引删除元素,并removeElement删除与值匹配的第一个元素。

    def list = [1,2,3,4,5,6,2,2,1]
    
    assert list.remove(2) == 3          // this removes the element at index 2, and returns it
    assert list == [1,2,4,5,6,2,2,1]
    
    assert list.removeElement(2)        // remove first 2 and return true
    assert list == [1,4,5,6,2,2,1]
    
    assert ! list.removeElement(8)      // return false because 8 is not in the list
    assert list == [1,4,5,6,2,2,1]
    
    assert list.removeAt(1) == 4        // remove element at index 1, and return it
    assert list == [1,5,6,2,2,1]
    

    当然,removeAt并且removeElement可以使用任何类型的列表。

    此外,可以通过调用以下clear方法来删除列表中的所有元素:

    def list= ['a',2,'c',4]
    list.clear()
    assert list == []
    

    设定操作

    assert 'a' in ['a','b','c']             // returns true if an element belongs to the list
    assert ['a','b','c'].contains('a')      // equivalent to the `contains` method in Java
    assert [1,3,4].containsAll([1,4])       // `containsAll` will check that all elements are found
    
    assert [1,2,3,3,3,3,4,5].count(3) == 4  // count the number of elements which have some value
    assert [1,2,3,3,3,3,4,5].count {
        it%2==0                             // count the number of elements which match the predicate
    } == 2
    
    assert [1,2,4,6,8,10,12].intersect([1,3,6,9,12]) == [1,6,12]
    
    assert [1,2,3].disjoint( [4,6,9] )
    assert ![1,2,3].disjoint( [2,4,6] )
    

    排序
    使用集合通常意味着排序。Groovy提供了各种选项来对列表进行排序,从使用闭包到比较器,如以下示例所示:

    assert [6, 3, 9, 2, 7, 1, 5].sort() == [1, 2, 3, 5, 6, 7, 9]
    
    def list = ['abc', 'z', 'xyzuvw', 'Hello', '321']
    assert list.sort {
        it.size()
    } == ['z', 'abc', '321', 'Hello', 'xyzuvw']
    
    def list2 = [7, 4, -6, -1, 11, 2, 3, -9, 5, -13]
    assert list2.sort { a, b -> a == b ? 0 : Math.abs(a) < Math.abs(b) ? -1 : 1 } ==
            [-1, 2, 3, 4, 5, -6, 7, -9, 11, -13]
    
    Comparator mc = { a, b -> a == b ? 0 : Math.abs(a) < Math.abs(b) ? -1 : 1 }
    
    // JDK 8+ only
    // list2.sort(mc)
    // assert list2 == [-1, 2, 3, 4, 5, -6, 7, -9, 11, -13]
    
    def list3 = [6, -3, 9, 2, -7, 1, 5]
    
    Collections.sort(list3)
    assert list3 == [-7, -3, 1, 2, 5, 6, 9]
    
    Collections.sort(list3, mc)
    assert list3 == [1, 2, -3, 5, 6, -7, 9]
    

    复制元素

    Groovy开发套件还利用运算符重载提供允许列表中元素的复制方法的:

    assert [1, 2, 3] * 3 == [1, 2, 3, 1, 2, 3, 1, 2, 3]
    assert [1, 2, 3].multiply(2) == [1, 2, 3, 1, 2, 3]
    assert Collections.nCopies(3, 'b') == ['b', 'b', 'b']
    
    // nCopies from the JDK has different semantics than multiply for lists
    assert Collections.nCopies(2, [1, 2]) == [[1, 2], [1, 2]] //not [1,2,1,2]
    

    2.2 Map

    2.2.1 Map语法

    在Groovy中,可以使用Map语法创建Map(也称为关联数组)[:]:

    def map = [name: 'Gromit', likes: 'cheese', id: 1234]
    assert map.get('name') == 'Gromit'
    assert map.get('id') == 1234
    assert map['name'] == 'Gromit'
    assert map['id'] == 1234
    assert map instanceof java.util.Map
    
    def emptyMap = [:]
    assert emptyMap.size() == 0
    emptyMap.put("foo", 5)
    assert emptyMap.size() == 1
    assert emptyMap.get("foo") == 5
    

    映射键默认为字符串:[a:1]等效于['a':1]。如果您定义一个名为的变量a,并且希望将其值作为a映射中的键,则可能会造成混淆。如果是这种情况,则必须通过添加括号对键进行转义,如以下示例所示:

    def a = 'Bob'
    def ages = [a: 43]
    assert ages['Bob'] == null // `Bob` is not found
    assert ages['a'] == 43     // because `a` is a literal!
    
    ages = [(a): 43]            // now we escape `a` by using parenthesis
    assert ages['Bob'] == 43   // and the value is found!
    

    2.2.2 Map属性

    Map也像bean一样,因此Map只要键是有效的Groovy标识符的字符串,就可以使用属性表示法来获取/设置其中的项目:

    def map = [name: 'Gromit', likes: 'cheese', id: 1234]
    assert map.name == 'Gromit'     // can be used instead of map.get('name')
    assert map.id == 1234
    
    def emptyMap = [:]
    assert emptyMap.size() == 0
    emptyMap.foo = 5
    assert emptyMap.size() == 1
    assert emptyMap.foo == 5
    

    注意:根据设计,map.foo它将始终在Map中寻找foo。这意味着在一个不包含class键的Map foo上,foo.class将返回null。如果您真的想了解该类,则必须使用getClass():

    def map = [name: 'Gromit', likes: 'cheese', id: 1234]
    assert map.class == null
    assert map.get('class') == null
    assert map.getClass() == LinkedHashMap // this is probably what you want
    

    2.2.3 在Map上迭代

    Map上的惯用迭代使用each和eachWithIndex方法。值得注意的是,使用Map语法创建的Map是有序的,也就是说,如果您对Map条目进行迭代,则可以确保返回的条目将与添加到Map中的顺序相同。

    def map = [
            Bob  : 42,
            Alice: 54,
            Max  : 33
    ]
    
    // `entry` is a map entry
    map.each { entry ->
        println "Name: $entry.key Age: $entry.value"
    }
    
    // `entry` is a map entry, `i` the index in the map
    map.eachWithIndex { entry, i ->
        println "$i - Name: $entry.key Age: $entry.value"
    }
    
    // Alternatively you can use key and value directly
    map.each { key, value ->
        println "Name: $key Age: $value"
    }
    
    // Key, value and i as the index in the map
    map.eachWithIndex { key, value, i ->
        println "$i - Name: $key Age: $value"
    }
    

    2.2.4 操纵Map

    添加或删除元素
    将一个元素增加到一个Map可以使用put来完成,或使用putAll方法:

    def defaults = [1: 'a', 2: 'b', 3: 'c', 4: 'd']
    def overrides = [2: 'z', 5: 'x', 13: 'x']
    
    def result = new LinkedHashMap(defaults)
    result.put(15, 't')
    result[17] = 'u'
    result.putAll(overrides)
    assert result == [1: 'a', 2: 'z', 3: 'c', 4: 'd', 5: 'x', 13: 'x', 15: 't', 17: 'u']
    

    可以通过调用clear方法来删除Map的所有元素:

    def m = [1:'a', 2:'b']
    assert m.get(1) == 'a'
    m.clear()
    assert m == [:]
    

    使用Map语法生成的Map使用对象equals和hashcode方法。这意味着您永远不要使用hashcode随时间变化的对象,否则您将无法找回关联的值。

    还值得注意的是,永远不要将GString用作Map的键​​,因为GString 的hashcode与String等效项的hashcode不同:

    def key = 'some key'
    def map = [:]
    def gstringKey = "${key.toUpperCase()}"
    map.put(gstringKey,'value')
    assert map.get('SOME KEY') == null
    

    筛选和搜索

    def people = [
        1: [name:'Bob', age: 32, gender: 'M'],
        2: [name:'Johnny', age: 36, gender: 'M'],
        3: [name:'Claire', age: 21, gender: 'F'],
        4: [name:'Amy', age: 54, gender:'F']
    ]
    
    def bob = people.find { it.value.name == 'Bob' } // find a single entry
    def females = people.findAll { it.value.gender == 'F' }
    
    // both return entries, but you can use collect to retrieve the ages for example
    def ageOfBob = bob.value.age
    def agesOfFemales = females.collect {
        it.value.age
    }
    
    assert ageOfBob == 32
    assert agesOfFemales == [21,54]
    
    // but you could also use a key/pair value as the parameters of the closures
    def agesOfMales = people.findAll { id, person ->
        person.gender == 'M'
    }.collect { id, person ->
        person.age
    }
    assert agesOfMales == [32, 36]
    
    // `every` returns true if all entries match the predicate
    assert people.every { id, person ->
        person.age > 18
    }
    
    // `any` returns true if any entry matches the predicate
    
    assert people.any { id, person ->
        person.age == 54
    }
    

    分组

    我们可以使用一些条件将列表分组到Map中:

    assert ['a', 7, 'b', [2, 3]].groupBy {
        it.class
    } == [(String)   : ['a', 'b'],
          (Integer)  : [7],
          (ArrayList): [[2, 3]]
    ]
    
    assert [
            [name: 'Clark', city: 'London'], [name: 'Sharma', city: 'London'],
            [name: 'Maradona', city: 'LA'], [name: 'Zhang', city: 'HK'],
            [name: 'Ali', city: 'HK'], [name: 'Liu', city: 'HK'],
    ].groupBy { it.city } == [
            London: [[name: 'Clark', city: 'London'],
                     [name: 'Sharma', city: 'London']],
            LA    : [[name: 'Maradona', city: 'LA']],
            HK    : [[name: 'Zhang', city: 'HK'],
                     [name: 'Ali', city: 'HK'],
                     [name: 'Liu', city: 'HK']],
    ]
    

    2.3 Range

    用..符号定义的范围包括所有范围(即列表包含from和to值)。
    用..<符号定义的范围是半开的,它们包括第一个值,但不包括最后一个值。

    // an inclusive range
    def range = 5..8
    assert range.size() == 4
    assert range.get(2) == 7
    assert range[2] == 7
    assert range instanceof java.util.List
    assert range.contains(5)
    assert range.contains(8)
    
    // lets use a half-open range
    range = 5..<8
    assert range.size() == 3
    assert range.get(2) == 7
    assert range[2] == 7
    assert range instanceof java.util.List
    assert range.contains(5)
    assert !range.contains(8)
    
    //get the end points of the range without using indexes
    range = 1..10
    assert range.from == 1
    assert range.to == 10
    

    请注意,有效地实现了int范围,从而创建了一个包含from和to值的轻量级Java对象。

    范围可用于实现java.lang.Comparable的任何Java对象进行比较,并具有方法next()并previous()返回范围中的下一个/上一个项目。例如,您可以创建一系列String元素:

    // an inclusive range
    def range = 'a'..'d'
    assert range.size() == 4
    assert range.get(2) == 'c'
    assert range[2] == 'c'
    assert range instanceof java.util.List
    assert range.contains('a')
    assert range.contains('d')
    assert !range.contains('e')
    

    您可以使用经典for循环在范围上进行迭代:

    for (i in 1..10) {
        println "Hello ${i}"
    }
    

    但您也可以通过使用each方法迭代范围来以更Groovy惯用的风格实现相同的效果:

    (1..10).each { i ->
        println "Hello ${i}"
    }
    

    范围也可以在switch语句中使用:

    switch (years) {
        case 1..10: interestRate = 0.076; break;
        case 11..25: interestRate = 0.052; break;
        default: interestRate = 0.037;
    }
    

    2.4 集合的语法增强

    2.4.3 “ *”算子

    “星点”运算符是一种快捷方式运算符,它允许您在集合的所有元素上调用方法或属性:

    assert [1, 3, 5] == ['a', 'few', 'words']*.size()
    
    class Person {
        String name
        int age
    }
    def persons = [new Person(name:'Hugo', age:17), new Person(name:'Sandra',age:19)]
    assert [17, 19] == persons*.age
    

    2.4.4 下标切片运算符
    您可以使用下标表达式索引到列表,数组,Map中。有趣的是,在这种情况下,字符串被视为特殊的集合:

    def text = 'nice cheese gromit!'
    def x = text[2]
    
    assert x == 'c'
    assert x.class == String
    
    def sub = text[5..10]
    assert sub == 'cheese'
    
    def list = [10, 11, 12, 13]
    def answer = list[2,3]
    assert answer == [12,13]
    

    请注意,您可以使用Range来提取集合的一部分:

    list = 100..200
    sub = list[1, 3, 20..25, 33]
    assert sub == [101, 103, 120, 121, 122, 123, 124, 125, 133]
    

    下标运算符可用于更新现有集合(对于不可变的集合类型):

    list = ['a','x','x','d']
    list[1..2] = ['b','c']
    assert list == ['a','b','c','d']
    

    值得注意的是,允许使用负索引从集合的末尾更轻松地提取:

    text = "nice cheese gromit!"
    x = text[-1]
    assert x == "!"
    

    您可以使用负索引从列表,数组,字符串等的末尾开始计数。

    def name = text[-7..-2]
    assert name == "gromit"
    

    最终,如果您使用向后范围(起始索引大于终止索引),则答案将相反。

    text = "nice cheese gromit!"
    name = text[3..1]
    assert name == "eci"
    

    2.5 增强的Clollection方法

    除了列表MapRange,Groovy还提供了许多其他过滤,收集,分组,计数等方法,这些方法可以直接在集合上或更容易迭代的方式上使用。

    我们邀请您阅读Groovy开发套件 API文档,尤其是:

    • 添加的方法Iterable可以在这里找到

    • 添加的方法Iterator可以在这里找到

    • 添加的方法Collection可以在这里找到

    • 添加的方法List可以在这里找到

    • 添加的方法Map可以在这里找到

    相关文章

      网友评论

          本文标题:Groovy开发套件-处理集合

          本文链接:https://www.haomeiwen.com/subject/cxpgoctx.html