皮皮网
皮皮网

【书画网整站源码】【机构建仓指标源码】【activiti工作流 源码】arraylist size源码

来源:iapp循环源码 发表时间:2024-12-22 17:27:22

1.如何自己实现一个简单的ArrayList
2.关于 List 的线程不安全
3.为什么说ArrayList是线程不安全的?
4.ArrayList详解及扩容源码分析
5.arraylist为什么线程不安全

arraylist size源码

如何自己实现一个简单的ArrayList

       ArrayList是Java集合框架中一个经典的实现类。他比起常用的数组而言,明显的优点在于,可以随意的添加和删除元素而不需考虑数组的大小。

       å®žçŽ°ä¸€ä¸ªç®€å•çš„ArrayList,实现的过程:

       å®žçŽ°çš„ArrayList主要的功能如下:

       é»˜è®¤æž„造器和一个参数的有参构造器

       add方法

       get方法

       indexOf方法

       contains方法

       size方法

       isEmpty方法

       remove方法

       è¿™ä¸ªç®€å•çš„ArrayListç±» 取名为SimpleArrayList,全部的代码查看SimpleArrayList代码

       æž„造器

       æºç ArrayList一共有三个构造器,一个无参构造器,一个参数为int型有参构造器,一个参数为Collection型的有参构造器。参数为Collection型的构造器用来实现将其他继承Collection类的容器类转换成ArrayList。SimpleArrayList类因为还没有手动实现其他的容器类,所以实现的构造方法只有2个。代码如下:

   public SimpleArrayList(){        this(DEFAULT_CAPACITY);

          }    public SimpleArrayList(int size){        if (size < 0){            throw new IllegalArgumentException("默认的大小" + size);

              }else{

                  elementData = new Object[size];

              }

          }

       æ— å‚构造器中的 DEFAULT_CAPACITY是定义的私有变量,默认值是,用来创建一个大小为的数组。有参构造器中,int参数是用来生成一个指定大小的Object数组。将创建好的数组传给elementData。elementData是真正的用来存储元素的数组。

       add方法

       add 方法用来往容器中添加元素,add方法有两个重载方法,一个是add(E e),另一个是add(int index, E e)。add本身很简单,但是要处理动态数组,即数组大小不满足的时候,扩大数组的内存。具体的代码如下:

   public void add(E e){

              isCapacityEnough(size + 1);

              elementData[size++] = e;

          }

       æ–¹æ³•isCapacityEnough就是来判断是否需要扩容,传入的参数就是最小的扩容空间。因为add一个元素,所以最小的扩容空间,即新的长度是所有元素+ 1。这里的size就是真正的元素个数。

  private void isCapacityEnough(int size){        if (size > DEFAULT_CAPACITY){

                  explicitCapacity(size);

              }       if (size < 0){            throw new OutOfMemoryError();

              }

          }

       åˆ¤æ–­æ‰©å®¹çš„方法也很简单,判断需要扩容的空间是不是比默认的空间大。如果需要的空间比默认的空间大,就调用explicitCapacity进行扩容。这里有个size小于0的判断,出现size小于0主要是因为当size超过Integer.MAX_VALUE就会变成负数。

   private final static int MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8;    private void explicitCapacity(int capacity){        int newLength = elementData.length * 2;        if (newLength - capacity < 0){

                  newLength = capacity;

              }        if (newLength > (MAX_ARRAY_LENGTH)){

                  newLength = (capacity > MAX_ARRAY_LENGTH ? Integer.MAX_VALUE : MAX_ARRAY_LENGTH);

              }

              elementData = Arrays.copyOf(elementData, newLength);

          }

       ä¸Šé¢çš„代码是扩容的代码,首先,定义一个数组最大的容量的常量为最大值,这个值按照官方的源码中的解释是要有些VM保留了数组的头部信息在数组中,因此实际存放数据的大小就是整数的最大值 - 8

       ç„¶åŽè®¾å®šä¸€ä¸ªè¦æ‰©å®¹çš„数组的大小,虽然上面说了有一个扩容空间的值 size + 1 ,这个是实际我们最小需要扩容的大小。但为了继续增加元素,而不频繁的扩容,因此一次性的申请多一些的扩容空间。这里newLength 打算申请为 数组长度的2倍,然后去判断这个长度是否满足需要的扩容空间的值。 即有了后续的两段代码

     if (newLength - capacity < 0){            newLength = capacity;

            }      if (newLength > (MAX_ARRAY_LENGTH)){            newLength = (capacity > MAX_ARRAY_LENGTH ? Integer.MAX_VALUE : MAX_ARRAY_LENGTH);

            }

       å¦‚æžœ2倍的长度仍然不满足,则申请到需要的扩容长度。在我们只增加一个元素的情况下,这个判断是永远不会生效的,但是如果有addAll方法,则增加的元素很多,就要导致一次申请2倍的长度是不够的。第二个判断是判断newLength的长度如果超过上面定义的数组最大长度则判断要需要的扩容空间是否大于数组最大长度,如果大于则newLength为 MAX_VALUE ,否则为 MAX_ARRAY_LENGTH。

       æœ€åŽï¼ŒçœŸæ­£å®žçŽ°æ•°ç»„扩容到设定长度的方法就没意思了,调用Arrays.copyOf(elementData, newLength)得到一个扩容后的数组。

       add的另一个重载方法也很简单。

  public void add(int index, E e) {

             //判断是不是越界

              checkRangeForAdd(index);

              //判断需不需要扩容

              isCapacityEnough(size + 1);

              //将index的元素及以后的元素向后移一位

              System.arraycopy(elementData,index,elementData,index + 1,size - index);

              //将index下标的值设为e

              elementData[index] = e;        size++;

          }

          private void checkRangeForAdd(int index){        //这里index = size是被允许的,即支持头,中间,尾部插入

              if (index < 0 || index > size){            throw new IndexOutOfBoundsException("指定的index超过界限");

              }

          }

       è‡³æ­¤ï¼Œä¸€ä¸ªç®€å•çš„add方法就实现完了。

       get方法

       get方法用来得到容器中指定下标的元素。方法实现比较简单,直接返回数组中指定下标的元素即可。

   private void checkRange(int index) {        if (index >= size || index < 0){

                  throw new IndexOutOfBoundsException("指定的index超过界限");

              }

          }    public E get(int index){

              checkRange(index);        return (E)elementData[index];

          }

       indexOf方法

       indexOf方法用来得到指定元素的下标。实现起来比较简单,需要判断传入的元素,代码如下:

   public int indexOf(Object o){        if (o != null) {            for (int i = 0 ; i < size ; i++){                if (elementData[i].equals(o)){                    return i;

                      }

                  }

              }else {            for (int i = 0 ; i < size ; i++){                if (elementData[i] == null) {                    return i;

                      }

                  }

              }        return -1;

          }

       åˆ¤æ–­ä¼ å…¥çš„元素是否为null,如果为null,则依次与null。如果不为空,则用equals依次比较。匹配成功就返回下标,匹配失败就返回-1。

       contains方法

       contains用来判断该容器中是否包含指定的元素。在有了indexOf方法的基础上,contains的实现就很简单了。

    public boolean contains(Object o){        return indexOf(o) >= 0;

           }

       size方法

       size方法用来得到容器类的元素个数,实现很简单,直接返回size的大小即可。

   public int size(){        return size;

          }

       isEmpty方法

       isEmpty方法用来判断容器是否为空,判断size方法的返回值是否为0即可。

   public boolean isEmpty(){        return size() == 0;

          }

       remove方法

       remove方法是用来对容器类的元素进行删除,与add一样,remove方法也有两个重载方法,分别是

       remove(Object o)和remove(int index)

    public E remove(int index) {

              E value = get(index);        int moveSize = size - index - 1;        if (moveSize > 0){

                  System.arraycopy(elementData,index + 1, elementData,index,size - index - 1);

              }

              elementData[--size] = null;        return value;

          }    

          public boolean remove(Object o){        if (contains(o)){

                  remove(indexOf(o));            return true;

              }else {            return false;

              }

          }

       ç¬¬ä¸€ä¸ªremove方法是核心方法,首先得到要删除的下标元素的值,然后判断index后面的要前移的元素的个数,如果个数大于零,则调用库方法,将index后面的元素向前移一位。最后elementData[--size] = null;缩减size大小,并将原最后一位置空。

       ç¬¬äºŒä¸ªremove方法不需要向第一个方法一样,需要告诉使用者要删除的下标对应的元素,只需要判断是否删除成功即可。如果要删除的元素在列表中,则删除成功,如果不在则失败。因此调用contains方法就可以判断是否要删除的元素在列表中。在则调用remove(int index),不在则返回失败。

关于 List 的线程不安全

       讨论 List 数据结构在多线程环境下的安全性问题。首先,答案是否定的,因为 List 类在 Java 中并未提供线程安全的实现,以牺牲一致性保证了效率。以 ArrayList 为例,书画网整站源码其核心方法 add(E e) 未加锁,如源码所示:

       public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }

       为提升性能和并发性,未对并发操作进行同步处理,从而可能导致并发修改异常(ConcurrentModificationException)。通过在模拟代码中执行并发添加操作即可复现该异常。

       针对此问题,有以下几种解决策略:

       1. **使用 Vector**:Vector 类提供了线程安全的 add 方法,通过 synchronized 关键字对方法进行同步,机构建仓指标源码确保并发安全。但其性能表现低于无锁的实现,特别是在高并发场景下。

       2. **利用 Collections.synchronizedList()**:此方法通过将非线程安全的 List 实例包装为同步的 List 实例,提供了一个简单的解决方式。通过以下代码即可实现:

       java

       List list = Collections.synchronizedList(new ArrayList());

       3. **采用 CopyOnWriteArrayList**:此类在添加元素时不会修改原有数据结构,而是在添加后创建新的数据结构副本。核心源码揭示了这一实现机制,具体为使用 ReentrantLock 上锁,复制现有数组,添加元素至新数组,并最终释放锁。

       综上所述,activiti工作流 源码解决 List 类线程不安全问题的常见策略包括使用 Vector、Collections.synchronizedList() 方法或 CopyOnWriteArrayList 类。每种方法都有其适用场景和性能考量,开发者应根据具体需求选择合适的解决方案。

为什么说ArrayList是线程不安全的?

       为什么说ArrayList是线程不安全的?

       在Java编程语言中,ArrayList是一个常用的集合类,它用于存储和管理一系列对象。然而,当涉及到多个线程同时操作ArrayList时,人们常常会问到:ArrayList是否线程安全?答案是,ArrayList在设计时并非线程安全。

       要理解ArrayList为何线程不安全,我们需要从其内部实现开始。jsp视频网站源码ArrayList主要由一个Object数组(elementData)和一个表示当前数组中元素数量的变量(size)组成。当需要添加新元素时,ArrayList通过判断当前数组容量是否足够,以及实际添加操作这两个步骤来决定是否进行数组扩容以及在相应位置设置值。

       在多线程环境下,这种操作可能会引发问题。具体来说,有两个主要隐患导致ArrayList在多线程环境下不安全:

       1. **数组扩容问题**:当多个线程尝试同时进行扩容操作时,可能出现数组越界问题。例如,假设列表大小为9,两个线程分别尝试添加元素时,它们各自计算的java项目案例分析源码数组需求大小为。如果两个线程同时进行判断,并且都发现当前数组可以满足需求,那么两个线程都可能开始扩容操作。然而,在线程A完成扩容后,线程B在尝试设置值时,会因数组未扩容而触发数组越界异常。

       2. **元素设置问题**:在添加元素时,ArrayList会执行两个操作:在指定位置设置值以及更新size变量。如果多个线程同时进行这些操作,可能导致一个线程的值覆盖另一个线程添加的值,从而破坏数据一致性。

       通过源码分析,我们可以看到ArrayList的这些设计缺陷导致了其在多线程环境下的不安全性。因此,在需要线程安全的多线程操作场景中,应避免直接使用ArrayList,或考虑使用如ConcurrentArrayList等线程安全的替代方案。

       在实际应用中,验证ArrayList的线程不安全性可以通过编写测试代码来实现。例如,可以编写一段代码来添加多个元素,观察在多线程环境下,数组是否会出现预期之外的元素覆盖或数组越界异常。

       总之,ArrayList的线程不安全性源自其内部实现的潜在并发问题。在多线程环境中使用时,应采取相应措施确保数据的一致性和完整性。

ArrayList详解及扩容源码分析

       在集合框架中,ArrayList作为普通类实现List接口,如下图所示。

       它实现了RandomAccess接口,表明支持随机访问;Cloneable接口,表明可以实现克隆;Serializable接口,表明支持序列化。

       与其他类不同,如Vector,ArrayList在单线程环境下的线程安全性较差,但适用于多线程环境下的Vector或CopyOnWriteArrayList。

       ArrayList底层基于连续的空间实现,为动态可扩展的顺序表。

       一、构造方法解析

       使用ArrayList(Collection c)构造方法时,传入类型必须为E或其子类。

       二、扩容分析

       不带参数的构造方法初始容量为,此时底层数组为空,即`DEFAULT_CAPACITY_EMPTY_ELEMENTDATA`长度为0。

       元素添加时,默认插入数组末尾,调用`ensureCapacityInternal(size + 1)`增加容量。

       若当前容量无法满足增加需求,计算新的容量以达到所需规模,确保添加元素成功并避免频繁扩容。

       三、常用方法

       通过List.subList(int fromIndex, int toIndex)方法获取子列表,修改原列表元素亦会改变此子列表。

       四、遍历方式

       ArrayList提供for循环、foreach循环、迭代器三种遍历方法。

       五、缺陷与替代方案

       ArrayList基于数组实现,插入或删除元素导致频繁元素移动,时间复杂度高。在需要任意位置频繁操作的场景下,性能不佳。

       因此,在Java集合中引入了更适合频繁插入和删除操作的LinkedList类。

       版权声明:本文内容基于阿里云实名注册用户的贡献,遵循相关协议规定,包括用户服务协议和知识产权保护指引。发现抄袭内容,可通过侵权投诉表单举报,确保社区内容健康、合规。

arraylist为什么线程不安全

       é¦–先说一下什么是线程不安全:线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。 如图,List接口下面有两个实现,一个是ArrayList,另外一个是vector。 从源码的角度来看,因为Vector的方法前加了,synchronized 关键字,也就是同步的意思,sun公司希望Vector是线程安全的,而希望arraylist是高效的,缺点就是另外的优点。 说下原理(百度的,很好理解): 一个 ArrayList ,在添加一个元素的时候,它可能会有两步来完成:

       1. 在 Items[Size] 的位置存放此元素;

       2. 增大 Size 的值。

       åœ¨å•çº¿ç¨‹è¿è¡Œçš„情况下,如果 Size = 0,添加一个元素后,此元素在位置 0,而且 Size=1;

       è€Œå¦‚果是在多线程情况下,比如有两个线程,线程 A 先将元素存放在位置 0。但是此时 CPU 调度线程A暂停,线程 B 得到运行的机会。线程B也向此 ArrayList 添加元素,因为此时 Size 仍然等于 0 (注意哦,我们假设的是添加一个元素是要两个步骤哦,而线程A仅仅完成了步骤1),所以线程B也将元素存放在位置0。然后线程A和线程B都继续运行,都增加 Size 的值。

       é‚£å¥½ï¼ŒçŽ°åœ¨æˆ‘们来看看 ArrayList 的情况,元素实际上只有一个,存放在位置 0,而 Size 却等于 2。这就是“线程不安全”了。

       ç¤ºä¾‹ç¨‹åºï¼š

       package test;

       import java.util.ArrayList;

       import java.util.List;

       public class ArrayListInThread implements Runnable {

       List<String> list1 = new ArrayList<String>();// not thread safe

       // List<String> list1 = Collections.synchronizedList(new ArrayList<String>());// thread safe

       public void run() {

       try {

       Thread.sleep((int)(Math.random() * 2));

       }

       catch (InterruptedException e) {

       e.printStackTrace();

       }

       list1.add(Thread.currentThread().getName());

       }

       public static void main(String[] args) throws InterruptedException {

       ThreadGroup group = new ThreadGroup("mygroup");

       ArrayListInThread t = new ArrayListInThread();

       for (int i = 0; i < ; i++) {

       Thread th = new Thread(group, t, String.valueOf(i));

       th.start();

       }

       while (group.activeCount() > 0) {

       Thread.sleep();

       }

       System.out.println();

       System.out.println(t.list1.size()); // it should be if thread safe collection is used.

       }

       }

相关栏目:百科