ArrayList数据结构
元素的数据类型相同,所占内存大小一样,由于是顺序存储的,可以直接计算每个元素所在的内存地址,根据索引查找时速度快。
- 在内存中分配连续的空间,实现了长度可变的数组
- 优点:遍历元素和随机访问元素的效率比较高
- 缺点:添加和删除需大量移动元素效率低(需要覆盖前移),按照内容查询效率低
ArrayList特点:不唯一、 有顺序(索引的顺序)。
集合中只能放对象,不能放基本数据类型,jdk1.5之后会自动装箱。
ArrayList常用方法
添加
index为可选参数,list下标
list. add([index,]object) ;
list. addAll([index,]list2) ;
获取元素个数
list.size()
获取第i个元素
list.get(i)
list数据交给迭代器
Iterator it=list.iterator()
实例
import java.util.ArrayList;
import java.util.Iterator;
public class TestArrayList {
public static void main(String[] args) {
// 创建ArrayList
ArrayList<Integer> list=new ArrayList<Integer>();
// 向ArrayList添加数据
list.add(1);//add参数需要是对象,基本数据类型时会自动装箱
list.add(1,2);
ArrayList<Integer> list1=new ArrayList<Integer>();
list1.add(3);
list1.add(4);
list.addAll(0,list1);//将一个ArrayList添加另一个ArrayList的指定index位置
// 遍历ArrayList
// 增强型foreach循环,不会遍历下标
for (int elem:
list) {
// int elem1=(int)elem;
System.out.println(elem);
}
// 使用Iterator迭代器遍历
Iterator<Integer> iterator=list.iterator();
while (iterator.hasNext()){//当前遍历的集合是否还有元素
System.out.println(iterator.next());//next返回iterator指针当前元素的值
}
}
}
删除
参数传入为整数,默认按照索引所以删除,不按照值删除,如果要删除值为int的需要包装为Integer,非整数则直接按值删除
list.remove(index)
list.remove(object)
list.remove(new Integer(int))
删除一个集合中所包含的元素
list.removeAll(collection)
清除整个list
list.clear()
修改
list.set(index,value)
其他方法
list.isEmpty()
list.toArray()
list.retainAll(collection)//仅保留此列表中指定集合中包含的元素
是否包含某个元素
list.contains(value)
查找元素的第一个或最后一个index
list.indexOf(value)
list.lastIndexOf(value)
ArrayList源码
底层能动态改变长度的数组
构造方法
无参构造底层数组直接指向空数组,elementData 为引用,{}为实际地址内容。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;
private static final Object[] EMPTY_ELEMENTDATA = {};
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
带指定内存大小参数的构造,传入0时直接指向空数组,大于0则创建指定内存大小的数组
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
add方法
add方法会先调用扩容方法,将当前的size+1作为最小内存minCapacity(如果elementData 为空,即首次使用list,会为elementData 自动分配10个空间大小作为minCapacity),当minCapacity大于底层数组elementData 的长度(一般是自动分配的10个空间),就要考虑将elementData 扩容。扩容规则:在elementData .length()增加50%的容量,如果增加50%后的容量仍无法满足需求minCapacity,就用minCapacity的大小,数组扩容,创建新数组,将原数组的元素复制到新数组,elementData 引用新数组地址。(size+1能用到这么大的内存?addAll重用会有这么大)
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 扩容方法
elementData[size++] = e;//添加到list中
return true;
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//elementData 为空,即首次使用list
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);//分配10个内存空间作为最小内存,DEFAULT_CAPACITY=10
}
return minCapacity;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// minCapacity大于底层数组elementData 的长度,扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//扩容规则
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//向左移一位增加50%的容量
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果增加50%后的容量仍无法满足需求minCapacity,就用minCapacity的大小
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 数组扩容,创建新数组,将原数组的元素复制到新数组,elementData 引用新数组地址。
elementData = Arrays.copyOf(elementData, newCapacity);
}
toString
通过Iterator接口实现遍历。重用父类实现[elment1,elment2…]
public String toString() {
Iterator<E> it = iterator();
if (! it.hasNext())
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = it.next();
sb.append(e == this ? "(this Collection)" : e);
if (! it.hasNext())
return sb.append(']').toString();
sb.append(',').append(' ');
}
}
泛型
使用场景1繁琐
取出元素时需要强制转换,否则是0bject类型。使用泛型将返回的为指定类型,而不是默认的Object
ArrayList<Integer> list=new ArrayList<Integer>();
//int elem = (int)list.get(i);
list.get(i) //返回的为Integer类型,而不是默认的Object
使用场景2不安全
添加元素的时候可以加入不同的数据类型,带来安全问题。使用泛型Integer就不能添加String。
list. add("abc");
LinkedList
与ArrayList的区别
增加了操作首尾节点的方法
List<> list=new LinkedList<>();
list.addFirst();
list.addLast();
list.removeFirst();
list.removeLast();
list.getFirst();
list.getLast();
添加、删除操作多使用LinkedList
随机访问多用ArrayList
E为泛型,在创建对象时会确定泛型E的数据类型,并将数据类型作为参数传递,赋值给泛型E
add方法
添加流程
- 首先l保存last最后一个节点
- 创建新节点,pre指向最后一个节点l,e为值,由于新节点会成为最后一个节点next为null
- 将新节点变成最后一个节点
- l如果为空,则新节点既是第一个节点又是最后一个节点,如果l不为空,l的next指向新节点。
first为头结点的next指向,last为最后一个节点。
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
private static class Node<E> {
E item;//存储值
Node<E> next;//指向下一个节点,直接存储节点
Node<E> prev;//指向前一个节点
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}