Excel精英培训网

 找回密码
 注册
数据透视表40+个常用小技巧,让你一次学会!
查看: 5621|回复: 16

[分享] 学习使用集合

[复制链接]
发表于 2013-12-2 22:09 | 显示全部楼层 |阅读模式
    必须要说:此贴是在网络中学习集合的使用笔记,所以整体基本上是这里抄一点那里摘一段,自己只是稍整合了一下而已,所以,根本就不是所谓的什么原创,更不是什么教学贴。
    还有点费话:集合,从某种程度上来说,sheets、cells、workbooks等等都可以算作集合,所以在vba中并不鲜见。但是,这里所说的集合,不是指这些,而是定指VBA中用dim 名称 as new collection来定义的这个东东。
   

--------------------------------------------------------------------------------------------------------------------------------------
    正题:在vba代码中,dilm c as new collection后,就定义了一个集合c。以后输入c.就可以看到集合的三个方法(add、remove、item)和一个属性count。其中add又有item、key、before、after四个参数。v这么多?整晕了,不能这样来,换下想法,从使用角度重新慢慢说

一、加入内容(1)

Dim c as new collection  ‘定义一个集合c,这句也可以写成Set c = new collection
c.add "a"  ‘”a”站到尾巴上去吧(由于原先没有,尾不尾巴都一样,”a”站过去了,只有他一个,他的序号就是1)
c.add "b"  ‘”b”站到尾巴上去,由于先前只有”a”,那”b”就在“a”后面了,”b”的序号就是2
c.add 100  ‘100站到尾巴上去,由于先前有”a”、”b”,100的序号就是3
c.add "b"  ‘报告:前边已经有”b”,重复了呀。回:重复没关系,是可以重复的哟。那这个“b”同样站到尾巴上去,由于先前已经有”a”、”b”、100,那这个“b”的序号就是4哈
s= c(1)   ‘把序号为1的值取出来,当然就是”a”了哈,所以s就为”a”
msgbox  c(4)   ‘显示一下序号为4的值是什么,所以显示”b”
    可见,向集合中加内容时,直接 c.add 内容  就可以了,系统根据你加入的顺序自动给他们分配一个序号1、2、3……。读取内容时,就可以按这个序号来取。这……这不就是与数组一样吗?如以下两段:
Sub t1()
Dim c As New Collection
For i = 1 To 100
   c.Add Int(Rnd * 50)
Next
Stop
End Sub


Sub t2()
Dim ar()
For i = 1 To 100
   ReDim Preserve ar(1 To i)
   ar(i) = Int(Rnd * 50)
Next
Stop
End Sub
    第一段代码,让集合中有100项,内容就是0至50间的随机数;第二段代码是产生100个元素的数组ar,内容也是0至50间的随机数。这太相似了嘛,哈哈……

评分

参与人数 1 +20 金币 +20 收起 理由
爱疯 + 20 + 20 支持学习贴

查看全部评分

 楼主| 发表于 2013-12-2 22:11 | 显示全部楼层
二、读取内容(1)
    前面说了单个取出值,如果要全部取出来,如填到A列单元格中去,怎么办呢?这可不好办了,这就不能象字典一样一次性全部取,只有一个一个的来。如:
Sub t3 ()
Dim c As New Collection,ar()
‘以下生成0至50间的随机数
For i = 1 To 100
   c.Add Int(Rnd * 50)
Next
‘以下填到A1起的单元格中去:
‘方式1:按序号读,且直接填到单元格中
For i=1 to c.count     ‘c.count的作用就是获取集合中有多少项
   Cells(I,1)=c(i)
next
‘方式2:用for each来读取,且先写到临时数组中然后再一次性填到单元格中
Redim ar(1 to c.count,1 to 1)  ‘c.count同前
I=0
For each tmp in  c     
   I=i+1
Ar(I,1)=tmp
Next
[b1].resize(i)=ar
End Sub
    也就是说,读取内容可以按序号来读,也可以用for each的从头到尾全读一次(还有其它读取方式,后面再说),只是,要全部读出来必须用循环。同时,这两种循环全读的形式,for each 比用序号来读要快很多很多,具体见下节。
回复

使用道具 举报

 楼主| 发表于 2013-12-2 22:13 | 显示全部楼层
三、加入与读取内容(2)
    前面的加入内容、按序号读内容时,比如集合c中有10000项,现在要取c(6000),系统是怎么取的呢?这与数组直奔目标完全不同,据说是这样的:从第一个开始,问你的序号是6000吗?不是,那问下一个(即第二个)你的序号是6000吗?不是,那再问下一个(第三个)……,就这样如此这般地一个一个问下去,直到问到序号是6000的,好,对了,把你的内容给出来。也就是说,不管你c中有好多项,如果找第k项,他都是从1项开始找起直到第k项。具体可比较下面代码的时间(电脑性能不是很好的请将数字改小点,下同。要不然会象死机了一样):

Sub t4()
Dim c As New Collection
For i& = 1 To 60000  ‘生成含60000项的集合c
    c.Add i
Next
'以下按index来访问第一项30000次
t = Timer
For i = 1 To 30000
    n = c(1)
Next
Msgbox  Timer - t

'以下按index来访问最后一项30000次
t = Timer
coun = c.Count
For i = 1 To 30000
    n = c(coun)
Next
Msgbox  Timer - t
End Sub

    差别太大了!可是,标题不是说是加入内容吗?这整到哪儿去了?马上转回来:c.add 内容  这个方法中还有些参数,现在来看key参数:
c.add 内容 , 不重复的字符串
     逗号前的就不说了,只看新增的“不重复的字符串”,关键在两点:一是必须是字符串,不能为数字(费话真多);二是不重复。要命的是系统不提供判断是否重复的方法(这点不象字典,字典有专门来判断重复没有),只晓得重复了他就报错且摆工。如:
Dim c As New Collection
C.add 1,”a”   ‘可以,内容为1 , key为”a”
c.add 1,”b”   ‘可以,内容为1 , key为”b”
c.add “b”,”c”   ‘可以,内容为”b” , key为”c”
c.add “b”,”b”   ‘不可以,报错且摆工。因为内容为”b”可以,但key为”b”与上面的重复了
c.add “c”,1   ‘不可以,报错且摆工。因为内容为”c”可以,但key为1,而1不是字串

    读取内容时,可以用前面说的序号来读取msgbox c(2),也可以按新加的key来读取msgbox c(“b”)。
    可是,这不是多事吗?还加个不重复来管到起?
    还记得用序号读取时,读前面的和读后面的速度不同吗?是因为读第k项时,要把前面的都要惊动。据说用key来读取,他就完全不同了,要快很多很多,如以下代码(电脑性能不是很好的请将数字改小点,下同。要不然会象死机了一样):

Sub t5()
Dim c As New Collection
For i& = 1 To 60000
    c.Add i, "a" & I    ‘写代码时就要注意构造不重复的字串作为key,这里是来这样构造的
Next
'以下按index来访问第一项30000次
t = Timer
For i = 1 To 30000
    n = c(1)
Next
msgbox Timer - t

'以下按index来访问最后一项30000次
t = Timer
coun = c.Count
For i = 1 To 30000
    n = c(coun)
Next
msgbox Timer - t


'以下按key来访问第一项30000次
t = Timer
s = "a" & 1
For i = 1 To 30000
    n = c(s)
Next
msgbox Timer - t


'以下按key来访问最后一项30000次
t = Timer
s = "a" & c.Count
For i = 1 To 30000
    n = c(s)
Next
msgbox Timer - t
End Sub
     有区别吧?
     用序号来访问时,前边的省事,越后面越耗时。用key访问时,耗时都差不多,很快。
回复

使用道具 举报

 楼主| 发表于 2013-12-2 22:13 | 显示全部楼层
四、特权:before、after
    前面说的add,都是先来的就站前头,后到的只能站后面。如果都这样,与当今社会显得格格不入,没体现权力。所以,得有特权。这不,就有了JC开道的:before和after:
Dim c As New Collection
c.add 100
c.add 200,”a”
msgbox c(1)
msgbox c(2)
msgbox c(“a”)
‘上面的都是显然的,没问题。继续:
C.add 300,before:=1   ‘正常的应该加到200的后面,可是,由于有了before:=1,要求就是指定放到序号为1的前面,以前所有的都得给我往后边挪一下,把第一个位置空出来,让给新来300。这样,执行的结果就是c中的顺序就为300、100、200了。这是before指定序号的结果。
c.add 400,before:=”a”  ‘这里,before指定key,这样,c中的顺序就是300、100、400、200。
c.add 500,”b”,”a”   ‘这句与c.add 500,”b”,before:=”a”等效。放在key为”a”的前面,且他的key为”b”
c.add 600,”c”,,”a”   ‘这句与c.add 600,”b”,after:=”a”等效,与before雷同,只是放在指定位置的后面。
c.add 700,,”f”  ‘这句与c.add 700,before:=”f”一样。可他要摆工,因为前面没以“f”为key的
回复

使用道具 举报

 楼主| 发表于 2013-12-2 22:14 | 显示全部楼层
五、删除
删除就只有remove。
Dim c As New Collection
c.add 100
c.add 200,”a”
c.add 300,”c”
c.add 400,”d”
c.remove(1) ‘把序号为1的删了,后面的依次往前挪,即这时msgbox c(1) 显示的是200
c.remove(“c”)  ’ ‘把key为”c”的删了
msgbox c(3)  ‘报错,经过前面的add和remove后,c中只有两项,序号为3显然不存在

所以说,remove可以按序号、key来删除

如果要全删,只能用循环来做:

Sub t6()
Dim c1 As New Collection,c2 As New Collection
For i& = 1 To 60000  ‘生成两个集合
C1.Add i, "a" & i
C2.Add i, "a" & i
Next
'以下按从后往前删除c1
t = Timer
For i = 1 To 60000
    C1.remove(c1.count)
Next
Msgbox Timer - t

'以下按从前往后删除c2
t = Timer
For i = 1 To 60000
    C2.remove(1)
Next
Msgbox Timer - t
End Sub
同样的删除,区别就这么明显。理由很简单,前面有。
回复

使用道具 举报

 楼主| 发表于 2013-12-2 22:17 | 显示全部楼层
六、几个实例:

生成1至10000之间不重复的随机数序列:

Sub 不重复随机数1()
tim = Timer
Dim c As New Collection, ar(1 To 10001, 1 To 1)
c.Add 0
For i% = 1 To 10000
    c.Add i, before:=Int(i * Rnd + 1)   '此处利用序号
Next
i = 0
For Each t In c   '此处开始把生成的序列写进数组
    i = i + 1
    ar(i, 1) = t
Next
MsgBox Timer - tim
[a1].Resize(i - 1) = ar
End Sub



Sub 不重复随机数2()
tim = Timer
Dim c As New Collection, ar(1 To 10001, 1 To 1)
c.Add 0, "a0"
For i% = 1 To 10000
    c.Add i, "a" & i, before:=("a" & Int(i * Rnd))   '此处利用key,要比上一个快些
Next
i = 0
For Each t In c  '同上一段,写进数组
    i = i + 1
    ar(i, 1) = t
Next
MsgBox Timer - tim
[b1].Resize(i - 1) = ar
End Sub



Sub 不重复随机数m_n_j001()   '源于eh中的另一思路
tim = Timer
Dim s As New Collection, ar(1 To 10000, 1 To 1)
For i% = 1 To 10000
     s.Add i
Next
Randomize
For i = 1 To 10000
    r% = Int(Rnd * s.Count + 1)
    ar(i, 1) = s(r)
    s.Remove (r)
Next
MsgBox Timer - tim
[c1].Resize(i - 1) = ar
End Sub


排序:把前面生成的A列中数据排序。没有其他思路,就全抄写eh中的了


Sub 排序_by_m_n_j001()   
Dim c As New Collection, ar()
ar = [a1:a10000].Value
tim = Timer
For i% = 1 To 10000
     For Each t In c
         If t >= ar(i, 1) Then Exit For
     Next
     If t = Empty Then
        c.Add ar(i, 1), "|" & ar(i, 1)
     Else
        If t <> ar(i, 1) Then
           c.Add ar(i, 1), "|" & ar(i, 1), before:="|" & t
        Else
           i2 = i2% + 1
           c.Add ar(i, 1), "|" & i2 & ar(i, 1), AFTER:="|" & ar(i, 1)
        End If
     End If
Next
i = 0
For Each t In c
    i = i + 1
    ar(i, 1) = t
Next
Msgbox  Timer - tim
[e1:e10000] = ar
End Sub



Sub 排序_by_northwolves()  ‘原代码不是这样,略改了点。思想是原作者的。
Dim c As New Collection, ar()
ar = [a1:a10000].Value
tim = Timer
c.Add ar(1, 1)
For i% = 2 To 10000
    last% = c.Count
    If ar(i, 1) <= c(1) Then c.Add ar(i, 1), before:=1: GoTo nextnum
    If ar(i, 1) >= c(last) Then c.Add ar(i, 1): GoTo nextnum
    first = 1
    Do While last > first + 1  '二分法查找。
       temp = (last + first) / 2
       If ar(i, 1) > c(temp) Then
          first = temp
       Else
          last = temp
       End If
    Loop
    c.Add ar(i, 1), before:=last
nextnum:
    Next
For i = 1 To 10000
    ar(i, 1) = c(i)
Next
msgbox Timer - tim
[f1:f10000] = ar
End Sub
回复

使用道具 举报

 楼主| 发表于 2013-12-2 22:22 | 显示全部楼层
几个实例,其实就两方面:不重复随机数和排序。不得不说:这两个用集合来写,不见得比常见的方式强,他只能算是目前坛子中解决这两个的常见方式中处于中等的应用而已。所以,只是又增加一种可供选择的方法。

欢迎大家提供其它方面的应用实例。
回复

使用道具 举报

发表于 2013-12-2 22:45 | 显示全部楼层
除开Before,after,其余的字典对象好像都能实现。
集合里貌似没法提取到所有的关键字。

但这个毕竟是VBA自带的类型,还是有优势的。

回复

使用道具 举报

发表于 2013-12-2 22:48 | 显示全部楼层
另外,在用自定义类的时候,可以用这个取代对象数组,不用考虑动态扩容,直接ADD。
回复

使用道具 举报

 楼主| 发表于 2013-12-3 09:07 | 显示全部楼层
老人不如新贵,这是自然的。
Collection在ms 的b系列中,是早就进元老院的了,虽然虎威犹存。
7楼已说,所列举的几个例子,都已赶不上现在好多常用方法。只是提供另一种解决的途径而已。
但是其某些思想还是有闪光之处
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|Archiver|Excel精英培训 ( 豫ICP备11015029号 )

GMT+8, 2024-6-5 05:08 , Processed in 0.438864 second(s), 6 queries , Gzip On, Yac On.

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表