移除集合效率高还是add高_Java 集合与队列的插入、删除在并发下的性能比较

这两天在写一个java多线程的爬虫,以广度优先爬取网页,设置两个缓存:

一个保存已经访问过的URL:vistedUrls

一个保存没有访问过的URL:unVistedUrls

需要爬取的数据量不大,对URL压缩后,可以把这两个数据结构都放入内存,vistedUrls很显然用HashSet实现,因为已经访问的URL只会添加,不会删除和修改,使用HashSet可以高效判断一个URL是否已经访问。

纠结unVistedUrls该用什么数据结构,如果用队列的话,并发情况下,队列中可能会用重复的URL,比如一个线程A爬了CSDN的一个URL1,另一个线程B爬了博客园的一个URL2,URL1和URL2的页面都有一个相同的出链URL3,线程A把URL3加入到unVistedUrls的队尾,等待下次爬取,但在URL3被爬取之前,线程B也把URL3加到队尾,这样队列中就有两个相同的URL,可能会导致重复爬取网页,当然可以通过其他方法来保证不会重复爬取。

然后就想能否也用Set来保存未访问的URL,这样在添加新的URL时,自动去重处理了,能够有效保证不爬取重复网页。但是unVistedUrls会有大量的插入和删除操作,我认为对集合进行大量的插入删除性能会比较低,为了测试集合的插入删除性能对比队列低多少,我写了一个简单的并发测试:

1 /**

2 * 测试集合与队列的插入与读写性能3 *4 *@authorjiqunpeng@gmail.com5 *6 */

7 public classSetQueueTest {8 //随即数构造器

9 private static Random r = new Random(10);10 //控制测试线程停止的原子变量

11 private static AtomicBoolean stop = new AtomicBoolean(false);12

13 /***14 * 基类,供测试用15 *16 *@authorjiqunpeng@gmail.com17 *18 */

19 static abstract classService {20 //操作的计数器

21 protected long count = 0;22

23 //添加一堆元素,并去一个元素

24 public abstract String addAndPick(Listelements);25

26 //取一个元素

27 public abstractString pickOne();28

29 /**

30 * 打印操作次数31 */

32 public voidtell() {33 System.out.println(this + " :\t" +count);34 }35 }36

37 /***38 * 采用TreeSet的集合工具39 *40 *@authorjiqunpeng@gmail.com41 *42 */

43 static class SetService extendsService {44 private TreeSet set = new TreeSet();45

46 @Override47 public synchronized String addAndPick(Listelements) {48 count++;49 set.addAll(elements);50 returnset.pollFirst();51 }52

53 @Override54 public synchronizedString pickOne() {55 count++;56 returnset.pollFirst();57 }58

59 }60

61 /***62 * 采用LinkedList的队列工具63 *64 *@authorjiqunpeng@gmail.com65 *66 */

67 static class QueueService extendsService {68 private Queue queue = new LinkedList();69

70 @Override71 public synchronized String addAndPick(Listelements) {72 count++;73 queue.addAll(elements);74 returnqueue.poll();75 }76

77 @Override78 public synchronizedString pickOne() {79 count++;80 returnqueue.poll();81 }82 }83

84 /***85 * 测试类86 *87 *@authorjiqunpeng@gmail.com88 *89 */

90 static class Tester implementsRunnable {91 //绑定要测试的工具对象

92 privateService service;93

94 Tester(Service s) {95 this.service =s;96 }97

98 @Override99 public voidrun() {100 while (stop.get() == false) {101 List elements = new ArrayList();102 int len = r.nextInt(200) + 8;103 for (int i = 0; i < len; i++) {104 elements.add(String.valueOf(r.nextInt()));105 }106 service.addAndPick(elements);107 for (int i = 0; i < 104; i++)108 service.pickOne();109 }110 }111 }112

113 /***114 * 多线程方式,测试一个插入、删除工具115 *116 *@paramservice117 *@paramtime118 *@paramunit119 *@throwsInterruptedException120 */

121 private static void test(Service service, inttime, TimeUnit unit)122 throwsInterruptedException {123 ExecutorService execs =Executors.newCachedThreadPool();124 for (int i = 0; i < 20; i++) {125 execs.execute(newTester(service));126 }127 execs.shutdown();128 unit.sleep(time);129 stop.compareAndSet(false, true);130 service.tell();131 }132

133 public static void main(String[] args) throwsInterruptedException {134 Service setService = newSetService();135 test(setService, 5, TimeUnit.SECONDS);136 stop.compareAndSet(true, false);//重置终止条件

137 Service queueService = newQueueService();138 test(queueService, 5, TimeUnit.SECONDS);139 }140 }

输出的结果如下:

SetQueueTest$SetService@5e9de959 : 7149859SetQueueTest$QueueService@11b343e0 :24303408

测试结果让我感到吃惊,TreeSet的插入删除效率确实比LinkedList低,20个线程跑了10秒,使用队列,插入删除24303408次,使用集合,插入删除7149859次。它们之间差距并不大,队列只比集合快2~3倍。属于同一个数量级。于是我这个小型的爬虫应该放心的选择用Set作为unVistedUrls的实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值