第八章:并发类容器之Queue

第八章:并发类容器之Queue第八章:并发类容器之Queue

大家好,又见面了,我是你们的朋友全栈君。

文章转自:  https://blog.csdn.net/u012453843/article/details/73863615

 在并发队列上JDK提供了两套实现,一个是以ConcurrentLinkedQueue为代表的高性能队列,一个是以BlockingQueue接口为代表的阻塞队列,无论哪种都继承自Queue。如下图所示。

第八章:并发类容器之Queue        

           首先我们来学ConcurrentLinkedQueue,ConcurrentLinkedQueue:是一个适用于高并发场景下的队列,通过无锁的方式,实现了高并发状态下的高性能,通常ConcurrentLinkedQueue性能好于BlockingQueue。它是一个基于链接节点的无界线程安全队列。该队列的元素遵循先进先出的原则。头是最先加入的,尾是最近加入的,该队列不允许null元素。
        ConcurrentLinkedQueue重要方法:
        add()和offer()都是加入元素的方法(在ConcurrentLinkedQueue中,这两个方法没有任何区别,大家可能有疑问,既然两个没有区别为何还要弄两个方法,这是因为这两个方法都继承自父类Queue,在其它场景下是可能不一样的)
        poll()和peek()都是取头元素节点,区别在于前者会删除元素,后者不会。
        下面我们来看个例子,如下所示。这是最常用的几个方法。

[html] 
view plain
 copy

  1. package com.internet.queue;  
  2.   
  3. import java.util.Iterator;  
  4. import java.util.concurrent.ConcurrentLinkedQueue;  
  5.   
  6. public class UseQueue {  
  7.     public static void main(String[] args) {  
  8.         //高性能无阻塞无界队列:ConcurrentLinkedQueue  
  9.         ConcurrentLinkedQueue<String> concurrentLinkedQueue = new ConcurrentLinkedQueue<String>();  
  10.         concurrentLinkedQueue.offer(“a”);  
  11.         concurrentLinkedQueue.add(“b”);  
  12.         concurrentLinkedQueue.offer(“c”);  
  13.         concurrentLinkedQueue.add(“d”);  
  14.           
  15.         System.out.println(concurrentLinkedQueue.poll());//取出第一个元素并删除  
  16.         System.out.println(concurrentLinkedQueue.size());//打印队列的大小  
  17.         System.out.println(concurrentLinkedQueue.peek());//取出第一个元素,不删除  
  18.         System.out.println(concurrentLinkedQueue.size());//打印队列的大小  
  19.         System.out.println(“——————————————————–“);  
  20.         for (Iterator iterator = concurrentLinkedQueue.iterator(); iterator.hasNext();) {  
  21.             String str = (String) iterator.next();  
  22.             System.out.println(str);  
  23.         }  
  24.     }  
  25. }  

        上面运行结果如下所示。

[html] 
view plain
 copy

  1. a  
  2. 3  
  3. b  
  4. 3  
  5. ——————————————————–  
  6. b  
  7. c  
  8. d  

          下面我们来验证ConcurrentLinkedQueue是线程安全的,我们向队列里添加一个元素,然后用多个线程去获取队列中的这个元素,如下所示。

[html] 
view plain
 copy

  1. package com.internet.queue;  
  2.   
  3. import java.util.concurrent.ConcurrentLinkedQueue;  
  4.   
  5. public class UseQueue {  
  6.     public static void main(String[] args) {  
  7.         //高性能无阻塞无界队列:ConcurrentLinkedQueue  
  8.         ConcurrentLinkedQueue<String> concurrentLinkedQueue = new ConcurrentLinkedQueue<String>();  
  9.         concurrentLinkedQueue.offer(“a”);  
  10.           
  11.         Thread t1 = new Thread(new Runnable() {  
  12.               
  13.             @Override  
  14.             public void run() {  
  15.                //不要使用.size()方法,因为那样效率非常低  
  16.                if(!concurrentLinkedQueue.isEmpty()){  
  17.                    System.out.println(“进入线程1”);  
  18.                    String str = concurrentLinkedQueue.poll();  
  19.                    System.out.println(“线程1取出的元素:”+str);  
  20.                }  
  21.             }  
  22.         },”t1″);  
  23.         Thread t2 = new Thread(new Runnable() {  
  24.               
  25.             @Override  
  26.             public void run() {  
  27.                //不要使用.size()方法,因为那样效率非常低  
  28.                if(!concurrentLinkedQueue.isEmpty()){  
  29.                    System.out.println(“进入线程2”);  
  30.                    String str = concurrentLinkedQueue.poll();  
  31.                    System.out.println(“线程2取出的元素:”+str);  
  32.                }  
  33.             }  
  34.         },”t2″);  
  35.         Thread t3 = new Thread(new Runnable() {  
  36.               
  37.             @Override  
  38.             public void run() {  
  39.                //不要使用.size()方法,因为那样效率非常低  
  40.                if(!concurrentLinkedQueue.isEmpty()){  
  41.                    System.out.println(“进入线程3”);  
  42.                    String str = concurrentLinkedQueue.poll();  
  43.                    System.out.println(“线程3取出的元素:”+str);  
  44.                }  
  45.             }  
  46.         },”t3″);  
  47.         Thread t4 = new Thread(new Runnable() {  
  48.               
  49.             @Override  
  50.             public void run() {  
  51.                //不要使用.size()方法,因为那样效率非常低  
  52.                if(!concurrentLinkedQueue.isEmpty()){  
  53.                    System.out.println(“进入线程4”);  
  54.                    String str = concurrentLinkedQueue.poll();  
  55.                    System.out.println(“线程4取出的元素:”+str);  
  56.                }  
  57.             }  
  58.         },”t4″);  
  59.         Thread t5 = new Thread(new Runnable() {  
  60.               
  61.             @Override  
  62.             public void run() {  
  63.                //不要使用.size()方法,因为那样效率非常低  
  64.                if(!concurrentLinkedQueue.isEmpty()){  
  65.                    System.out.println(“进入线程5”);  
  66.                    String str = concurrentLinkedQueue.poll();  
  67.                    System.out.println(“线程5取出的元素:”+str);  
  68.                }  
  69.             }  
  70.         },”t5″);  
  71.         t1.start();  
  72.         t2.start();  
  73.         t3.start();  
  74.         t4.start();  
  75.         t5.start();  
  76.     }  
  77. }  

         其中一次运行结果如下所示,可以看到,能取出元素的只有一个线程,无论执行多少次,都只有一个线程能够获取到元素a,其它线程获取的都是null。注意判断队列是否为空时,不要使用.size()方法,因为.size() 是要遍历一遍集合的,因此比较慢,使用isEmpty()效率比较高。

[html] 
view plain
 copy

  1. 进入线程1  
  2. 进入线程4  
  3. 进入线程3  
  4. 进入线程2  
  5. 线程3取出的元素:null  
  6. 线程2取出的元素:null  
  7. 线程4取出的元素:null  
  8. 线程1取出的元素:a  

        下面来学习下常见的几个阻塞队列,由于底层源码都比较难懂,我这里还是只说用法,想深入研究的同学可以去查看源码。


第一个:ArrayBlockingQueue


         基于数组的阻塞队列实现,在ArrayBlockingQueue内部,维护了一个定长数组,以便缓存队列中的数据对象,其内部没实现读写分离,也就意味着生产和消费不能完全并行,长度是需要定义的,可以指定先进先出或者先进后出,也叫有界队列,在很多场合非常适用。


         之所以说ArrayBlockingQueue是有界队列,是因为我们在使用该队列时必须指定队列的容量大小,如下图所示,三种实例化方式都必须有”int capacity”(容量大小)。

第八章:并发类容器之Queue

         ArrayBlockingQueue向队列添加元素有三种方法,分别是put、add、offer。这三个方法虽然都是添加元素,但是作用却不同。首先我们来看下put方法,如下所示,我们给队列设置容量为5,然后故意向容器中添加6个元素,看是什么效果。

[html] 
view plain
 copy

  1. package com.internet.queue;  
  2.   
  3. import java.util.concurrent.ArrayBlockingQueue;  
  4.   
  5. public class UseQueue {  
  6.     public static void main(String[] args) throws Exception {  
  7.         ArrayBlockingQueue<String> array = new ArrayBlockingQueue<>(5);  
  8.         array.put(“a”);  
  9.         array.put(“b”);  
  10.         array.put(“c”);  
  11.         array.put(“d”);  
  12.         array.put(“e”);  
  13.         array.put(“f”);  
  14.     }  
  15. }  

         上面代码执行效果如下图,可以看到,线程一直处于running状态,这是因为put将指定元素插入到此队列的尾部,如有必要,则等待空间变得可用。现在第六个元素由于插入不到队列当中,它就在这儿等着,什么时候有元素从队列中出去了,它就插入到队列当中。

第八章:并发类容器之Queue

          下面我们再试试add方法,代码如下所示。

[html] 
view plain
 copy

  1. package com.internet.queue;  
  2.   
  3. import java.util.concurrent.ArrayBlockingQueue;  
  4.   
  5. public class UseQueue {  
  6.     public static void main(String[] args) throws Exception {  
  7.         ArrayBlockingQueue<String> array = new ArrayBlockingQueue<>(5);  
  8.         array.add(“a”);  
  9.         array.add(“b”);  
  10.         array.add(“c”);  
  11.         array.add(“d”);  
  12.         array.add(“e”);  
  13.         array.add(“f”);  
  14.     }  
  15. }  

         运行结果如下图,可以看到抛出了异常,说队列已经满了,盛不下第六个元素了。add方法的作用便是:将指定的元素插入到此队列中(如果立即可行且不会违反容量限制),在成功时返回 true,如果当前没有可用空间,则抛出 IllegalStateException。

第八章:并发类容器之Queue

          下面我们再看下offer方法,代码如下:

[html] 
view plain
 copy

  1. package com.internet.queue;  
  2.   
  3. import java.util.concurrent.ArrayBlockingQueue;  
  4. import java.util.concurrent.TimeUnit;  
  5.   
  6. public class UseQueue {  
  7.     public static void main(String[] args) throws Exception {  
  8.         ArrayBlockingQueue<String> array = new ArrayBlockingQueue<>(5);  
  9.         array.offer(“a”);  
  10.         array.offer(“b”);  
  11.         array.offer(“c”);  
  12.         array.offer(“d”);  
  13.         array.offer(“e”);  
  14.         System.out.println(array.offer(“f”,3,TimeUnit.SECONDS));  
  15.     }  
  16. }  

        下面是运行结果,可以看到offer返回的是bool类型的值,offer方法将指定元素插入到此队列的尾部(如果立即可行且不会超出此队列的容量),在成功时返回 true,如果此队列已满,则返回 false。当使用有容量限制的队列时,此方法通常要优于 add 方法,后者可能无法插入元素,而只是抛出一个异常。


第八章:并发类容器之Queue

 第二个:LinkedBlockingQueue

          举个例子如下:

第八章:并发类容器之Queue

         代码如下,其中q.drainTo(list,3)是一次性把队列中的三个元素都存放到list当中,返回值是成功从队列中取出的元素个数。我们说LinkedBlockingQueue是无界队列是因为我们可以不设置队列的长度,这样队列便是无界的。

[html] 
view plain
 copy

  1. package com.internet.queue;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5. import java.util.concurrent.LinkedBlockingQueue;  
  6.   
  7. public class UseQueue {  
  8.    public static void main(String[] args) {  
  9.       //改队列可以是无界队列也可以是有界队列,不指定长度便是无界队列,指定长度便是有界队列  
  10.       LinkedBlockingQueue<String> q = new LinkedBlockingQueue<String>();  
  11.       q.offer(“a”);  
  12.       q.offer(“b”);  
  13.       q.offer(“c”);  
  14.       q.offer(“d”);  
  15.       q.offer(“e”);  
  16.       q.offer(“f”);  
  17.       List<String> list = new ArrayList<String>();  
  18.       System.out.println(q.drainTo(list,3));  
  19.       System.out.println(list.size());  
  20.       for(String str : list){  
  21.           System.out.println(str);  
  22.       }  
  23.    }  
  24. }  

       运行结果如下:

[html] 
view plain
 copy

  1. 3  
  2. 3  
  3. a  
  4. b  
  5. c  

       但是如果给LinkedBlockingQueue指定长度的话,它就变成了有界队列,比如我们把LinkedBlockingQueue的长度设置为5,超出队列的话,将无法再添加元素,如下图所示。

第八章:并发类容器之Queue

          运行结果如下图所示,q.offer()方法如果返回true表示添加成功,返回false表示添加失败。可见第6个元素并没有成功添加。

第八章:并发类容器之Queue

第三个:SynchronousQueue

         这个队列非常特殊,它不能装任何元素。      

[html] 
view plain
 copy

  1. package com.internet.queue;  
  2.   
  3. import java.util.concurrent.SynchronousQueue;  
  4.   
  5. public class UseQueue {  
  6.    public static void main(String[] args) {  
  7.       SynchronousQueue<String> q = new SynchronousQueue<>();  
  8.       System.out.println(q.offer(“a”));  
  9.    }  
  10. }  

        运行结果如下图所示

第八章:并发类容器之Queue

        下面看个例子,这个例子,貌似SynchronousQueue可以添加元素,如下所示。但是其实SynchronousQueue依然是没有存储元素的,这里之所以没有报错,是因为我们先启动了一个线程t1要消费SynchronousQueue这个队列中的元素,线程t2要向SynchronousQueue队列添加一个元素,这时候会发生什么呢?这时候,线程t2并不会真的把元素添加到队列中,而是直接将要添加的元素交给线程t1了。也就是说,SynchronousQueue队列还是不会真正存储元素的。

[html] 
view plain
 copy

  1. package com.internet.queue;  
  2.   
  3. import java.util.concurrent.SynchronousQueue;  
  4.   
  5. public class UseQueue {  
  6.    public static void main(String[] args) {  
  7.       final SynchronousQueue<String> q = new SynchronousQueue<String>();  
  8.       Thread t1 = new Thread(new Runnable() {  
  9.           
  10.             @Override  
  11.             public void run() {  
  12.                 try {  
  13.                     System.out.println(q.take());  
  14.                 } catch (Exception e) {  
  15.                     e.printStackTrace();  
  16.                 }  
  17.             }  
  18.        });  
  19.        t1.start();  
  20.        Thread t2 = new Thread(new Runnable() {  
  21.           
  22.             @Override  
  23.             public void run() {  
  24.                 q.add(“ffasss”);  
  25.             }  
  26.         });  
  27.         t2.start();  
  28.    }  
  29. }  

          肯定有些人会有疑问,既然SynchronousQueue不能装任何元素的话,那么要它有何用?还有就是有界队列和无界队列的应用场景是什么呢?如下图所示。

第八章:并发类容器之Queue
第四个:PriorityBlockingQueue

         基于优先级的阻塞队列(优先级的判断通过构造函数传入的Compator对象来决定,也就是说传入队列的对象必须实现Comparable接口),在实现PriorityBlockingQueue时,内部控制线程同步的锁采用的是公平锁,他也是一个无界的队列。下面我们便以一个小例子来说明。

         参与比较的对象必须实现Comparable接口,如下所示,重写了compareTo方法,用id来进行比较。

[html] 
view plain
 copy

  1. package com.internet.queue;  
  2.   
  3. public class Task implements Comparable<Task>{  
  4.     private int id;  
  5.     private String name;  
  6.   
  7.     public int getId() {  
  8.         return id;  
  9.     }  
  10.   
  11.     public void setId(int id) {  
  12.         this.id = id;  
  13.     }  
  14.   
  15.     public String getName() {  
  16.         return name;  
  17.     }  
  18.   
  19.     public void setName(String name) {  
  20.         this.name = name;  
  21.     }  
  22.   
  23.     @Override  
  24.     public int compareTo(Task task) {  
  25.         return this.id > task.id ? 1 : (this.id < task.id ? -1 : 0);  
  26.     }  
  27.   
  28. }  

          下面我们使用PriorityBlockingQueue ,如下所示。

[html] 
view plain
 copy

  1. package com.internet.queue;  
  2.   
  3. import java.util.Iterator;  
  4. import java.util.concurrent.PriorityBlockingQueue;  
  5.   
  6. public class UsePriorityBlockingQueue {  
  7.    public static void main(String[] args) {  
  8.       PriorityBlockingQueue<Task> q = new PriorityBlockingQueue<Task>();  
  9.       Task t1 = new Task();  
  10.       t1.setId(3);  
  11.       t1.setName(“任务1”);  
  12.       Task t2 = new Task();  
  13.       t2.setId(6);  
  14.       t2.setName(“任务2”);  
  15.       Task t3 = new Task();  
  16.       t3.setId(1);  
  17.       t3.setName(“任务3”);  
  18.       q.add(t1);  
  19.       q.add(t2);  
  20.       q.add(t3);  
  21.       //添加到队列里面的元素还是没有顺序的  
  22.       for (Iterator iterator = q.iterator(); iterator.hasNext();) {  
  23.         Task task = (Task) iterator.next();  
  24.         System.out.println(task.getName());  
  25.       }  
  26.       //只有当往外取数据的时候才有顺序  
  27.       try {  
  28.           System.out.println(q.take().getId());  
  29.           System.out.println(q.take().getId());  
  30.           System.out.println(q.take().getId());  
  31.       } catch (InterruptedException e) {  
  32.           e.printStackTrace();  
  33.       }  
  34.         
  35.    }  
  36. }  

         我们运行main方法,可以看到结果如下所示,可以看到,添加到队列里的对象其实是没有顺序的(任务3对应的对象的id是1,任务2对应的对象的id是6,任务1对应的对象的id是3),而我们往外取的时候可以看到取出的顺序是1、3、6,符合排序规则。

[html] 
view plain
 copy

  1. 任务3  
  2. 任务2  
  3. 任务1  
  4. 1  
  5. 3  
  6. 6  

第五个:DelayQueue

        带有延迟时间的Queue,其中的元素只有当其指定的延迟时间到了,才能够从队列中获取该元素。DelayQueue中的元素必须实现Delayed接口,DelayQueue是一个没有大小限制的队列,应用场景很多,比如对缓存超时的数据进行移除、任务超时处理、空闲连接的关闭等等。

        下面我们便来看一个网民在网吧上网的例子,首先我们来新建一个网民类,如下所示

[html] 
view plain
 copy

  1. package com.internet.queue;  
  2.   
  3. import java.util.concurrent.Delayed;  
  4. import java.util.concurrent.TimeUnit;  
  5.   
  6. /**  
  7.  * 网民类  
  8.  * @author wanghaijie  
  9.  *  
  10.  */  
  11. public class Wangmin implements Delayed{  
  12.     //网名  
  13.     private String name;  
  14.     //身份证号  
  15.     private String id;  
  16.     //截止时间  
  17.     private long endTime;  
  18.     //定义时间工具类,以秒为单位  
  19.     private TimeUnit timeUnit = TimeUnit.SECONDS;  
  20.   
  21.     public Wangmin(String name,String id,long endTime){  
  22.         this.name = name;  
  23.         this.id = id;  
  24.         this.endTime = endTime;  
  25.     }  
  26.       
  27.       
  28.     public String getName() {  
  29.         return name;  
  30.     }  
  31.   
  32.   
  33.   
  34.     public void setName(String name) {  
  35.         this.name = name;  
  36.     }  
  37.   
  38.   
  39.   
  40.     public String getId() {  
  41.         return id;  
  42.     }  
  43.   
  44.   
  45.   
  46.     public void setId(String id) {  
  47.         this.id = id;  
  48.     }  
  49.   
  50.   
  51.   
  52.     public long getEndTime() {  
  53.         return endTime;  
  54.     }  
  55.   
  56.   
  57.   
  58.     public void setEndTime(long endTime) {  
  59.         this.endTime = endTime;  
  60.     }  
  61.   
  62.   
  63.   
  64.     @Override  
  65.     public int compareTo(Delayed delayed) {  
  66.         Wangmin w = (Wangmin)delayed;  
  67.         return this.getDelay(this.timeUnit) – w.getDelay(this.timeUnit) > 0 ? 1:0;  
  68.     }  
  69.   
  70.     @Override  
  71.     public long getDelay(TimeUnit unit) {  
  72.         return unit.convert(endTime, TimeUnit.MILLISECONDS) – unit.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS);  
  73.     }  
  74.    
  75. }  

         下面我们便来使用DelayQueue,如下所示



[html] 
view plain
 copy

  1. package com.internet.queue;  
  2.   
  3. import java.util.concurrent.DelayQueue;  
  4.   
  5. public class WangBa implements Runnable{  
  6.     //延迟队列  
  7.     private DelayQueue<Wangmin> queue = new DelayQueue<>();  
  8.     //是否营业的标志  
  9.     public boolean yingye = true;  
  10.   
  11.     //上机方法,为了测试方便,规定交1块钱只能上1秒网。  
  12.     public void shangji(String name, String id, int money){  
  13.         //第三个参数是下机时间,上网时长加上当前时间就是下机时间  
  14.         Wangmin man = new Wangmin(name, id, 1000*money + System.currentTimeMillis());  
  15.         System.out.println(“网名”+man.getName()+” 身份证”+man.getId()+” 交钱”+money+”块,开始上机…”);  
  16.         this.queue.add(man);  
  17.     }  
  18.       
  19.     public void xiaji(Wangmin man){  
  20.         System.out.println(“网名”+man.getName()+” 身份证”+man.getId()+”时间到下机…”);  
  21.     }  
  22.       
  23.     @Override  
  24.     public void run() {  
  25.         while(yingye){  
  26.             try {  
  27.                 Wangmin man = queue.take();  
  28.                 xiaji(man);  
  29.             } catch (Exception e) {  
  30.                 e.printStackTrace();  
  31.             }  
  32.         }  
  33.     }  
  34.       
  35.     public static void main(String[] args){  
  36.         try {  
  37.             System.out.println(“网吧开始营业”);  
  38.             WangBa wangBa = new WangBa();  
  39.             Thread shangwang = new Thread(wangBa);  
  40.             shangwang.start();  
  41.               
  42.             wangBa.shangji(“路人甲”, “123”, 1);  
  43.             wangBa.shangji(“路人乙”, “234”, 10);  
  44.             wangBa.shangji(“路人丙”, “345”, 5);  
  45.         } catch (Exception e) {  
  46.             e.printStackTrace();  
  47.         }  
  48.     }  
  49.        
  50. }  

         运行main方法,结果如下所示,可见,DelayQueue在处理网吧上网的问题上还是非常方便的。

[html] 
view plain
 copy

  1. 网吧开始营业  
  2. 网名路人甲 身份证123 交钱1块,开始上机…  
  3. 网名路人乙 身份证234 交钱10块,开始上机…  
  4. 网名路人丙 身份证345 交钱5块,开始上机…  
  5. 网名路人甲 身份证123时间到下机…  
  6. 网名路人丙 身份证345时间到下机…  
  7. 网名路人乙 身份证234时间到下机…<span style=“font-family:Arial, Helvetica, sans-serif;background-color:rgb(255,255,255);”>         </span>  
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/106139.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • Sublime Text3 搭建python环境「建议收藏」

    Sublime Text3 搭建python环境「建议收藏」一.安装sublimeSublimeText:一款具有代码高亮、语法提示、自动完成且反应快速的编辑器软件,不仅具有华丽的界面,还支持插件扩展机制,最重要的是非常的轻便。sublimeTexe3下载地址:http://www.sublimetext.com/3二.安装packageControlSublimetext是通过packagecontrol来对插件进行安装和卸载。…

  • c0000005 access_violation_0X0000005

    c0000005 access_violation_0X00000050xC0000005:AccessViolation-vc++6.0aps001,002,003创建的C:\SMW200DATA\DATA,内容是不一样的,不通用的。读取相关文件就会报错咯。如果想要运行,则目录内容得删除。然要运行时,自动创建。每台设备已经有了数据时,需要先备份C:\SMW200DATA\DATA里面的内容。否则数据丢失。参考:https://blog.csd…

  • C#中如何遍历ArrayList

    C#中如何遍历ArrayList 前言:ArrayList是非常方便的动态数组,在使用ArrayList时经常会遇到一些问题,码了一些百度文库查找到的资料以及例子,希望可以帮助大家在需要时方便查找。1、什么是ArrayListArrayList就是传说中的动态数组,用MSDN中的说法,就是Array的复杂版本,它提供了如下一些好处:     &lt;1&gt;动态的增加和减少元素     &…

  • scrapy爬虫,爬取图片

    scrapy爬虫,爬取图片

    2021年11月19日
  • 正则表达式全解析+常用示例「建议收藏」

    正则表达式全解析+常用示例「建议收藏」在开始写这篇文章之前,我的心里还是纠结的。我在问自己要不要写这篇东西,关于相似的内容网上多如牛毛,而且还不乏珍品,况且,就算我写了也不一定能写的好。但是现在你既然看到了,那说明我还是写了出来。就算是对自己学习的一个总结吧!同时也把常见的常用的正则表达式给收集整理出来,以便用到的时候不用满世界的找。关于正则表达式一直都是个让很多程序员都觉得很郁闷的一个东西,我觉得创造正则表达式的那个家伙简直就是

  • 13万人12306信息泄露 小伙挨个发邮件通知

    13万人12306信息泄露 小伙挨个发邮件通知

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号