你好,游客 登录
背景:
阅读新闻

【Java学习笔记】(六)ArrayList源码分析

[日期:2018-05-08] 来源:简书  作者: [字体: ]

正文之前

上一篇文章分析了ArrayList的构造器和基本操作的源码,这一次来讲一讲ArrayList常用方法剩下的源码(迭代器的留到下次说)

主要内容

  1. 容器容量

  2. 查找特定元素,包含特定元素,克隆之类的基本用法

正文

开始正题之前,需要先分清楚两个概念,容器的capacity和size,capacity指的是容器的容量,最多能装多少,而size指的是当前容器中元素的数量

接下来直奔正题

1. 扩容

  • 设定容量

public void ensureCapacity(int minCapacity) { //如果初始时有设定容量,就按设定的来,没有就按默认的来
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) // any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY; //如果给定的比上述的要大,就按给定的来
if (minCapacity > minExpand) { //这个方法在下面
ensureExplicitCapacity(minCapacity);
}
} //私有方法,用来确定容量
private void ensureExplicitCapacity(int minCapacity) {
modCount++; //增加容量
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}

  • 增长

/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
//注解说是因为有的虚拟机要保留数组的前几位,这个常量的值为2147483639
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; /**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) { // overflow-conscious code
int oldCapacity = elementData.length; //右移一位,相当于除以2
int newCapacity = oldCapacity + (oldCapacity >> 1); //两者相比,用更大的一个作为容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity; //容量特别大时的操作
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity); //按照新容量复制数组
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}

  • 大容量

private static int hugeCapacity(int minCapacity) { //溢出
if (minCapacity < 0) // overflow
throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}

2. 调整大小

如果发现容量多了很多,有点浪费,就可以调整容器大小到当前大小

public void trimToSize() {
modCount++; //如果确实有多余部分
if (size < elementData.length) { //如果size不为0,则按照size复制数组
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}

3. 列表元素数量

public int size() { return size;
}

4. 列表是否为空

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

5. 查找列表中特定元素的位置

public int indexOf(Object o) { if (o == null) { for (int i = 0; i < size; i++) if (elementData[i]==null) return i;
} else { //遍历数组查找
for (int i = 0; i < size; i++) if (o.equals(elementData[i])) return i;
} return -1;
}

6. 判断列表是否包含特定元素

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

7. 如果列表中有多个特定元素

public int lastIndexOf(Object o) { if (o == null) { for (int i = size-1; i >= 0; i--) if (elementData[i]==null) return i;
} else { //从尾部开始遍历
for (int i = size-1; i >= 0; i--) if (o.equals(elementData[i])) return i;
} return -1;
}

8. 克隆

需要先说明一个概念:浅拷贝和深拷贝

浅拷贝,两个ArrayList在内存中的地址是不一样的,但是他们中的元素都指向同一个地址,手绘一个图解释一下:

深拷贝,不仅ArrayList在内存中的地址是不一样的,他们中的元素也不指向同一个地址,也手绘个图解释一下:

深拷贝可能好理解一些,就是所有的东西都拷贝一下,完全分离的状态

ArrayList的clone()方法是浅拷贝,我们来做个实验:

public class Test {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { list.add(String.valueOf(i));
} //强制类型转换
ArrayList<String> list1 = (ArrayList<String>)list.clone();
System.out.println("list" + list);
System.out.println("list1" + list1);
System.out.println("二者对于某个特定元素是否相同 " + (list.get(1) == list1.get(1)));
System.out.println("二者的地址是否相同 " + (list == list1));
}
}

9. 转换为数组

ArrayList是一个列表,和数组不一样,不能直接用下标访问元素,转换为数组就可以了,转换为数组有两种方法:

//按照列表中元素的顺序,将列表中元素复制到一个数组
public Object[] toArray() { return Arrays.copyOf(elementData, size);
}

这一种方法可能会抛出数组存储异常和空指针异常,这里讲述两种情况(数组大小小于列表元素数量):

  1. 传入的数组类型和列表元素相同或者是列表元素的基类型,直接将列表中元素赋值在数组内

  2. 传入的数组类型不是列表元素的基类型,抛出数组存储异常

public <T> T[] toArray(T[] a) { //传入的数组大小更小
if (a.length < size) //创建一个数组,类型是数组a的运行时类型
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size); if (a.length > size)
a[size] = null; return a;
}

传入的数组 s 是Object类型的,符合上述的情况一:

接下来是情况二:

这样子,ArrayList常用的方法就讲完了,下一篇就会是与ArrayList有关的迭代器的使用了

作者:胖若两人_
链接:https://www.jianshu.com/p/ac8522a184a2
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

收藏 推荐 打印 | 录入:Cstor | 阅读:
相关新闻      
本文评论   查看全部评论 (0)
表情: 表情 姓名: 字数
点评:
       
评论声明
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款