列表的创建
方法1:无参数构建
List<Integer> list1 = new ArrayList<>();
说明:该方法只是创建了一个容量为0的数组列表,只有在使用add方法时,才会分配默认容量DEFAULT_CAPACITY(为10),数组是有容量大小限制,当 add 操作时,如果数组已满,就会引发扩容
扩容步骤如下:
1.计算新的容量大小:一般是原来的1.5倍
2.按新的容量大小创建一个新的数组
3.将旧的数组里的元素全部复制到新创建的数组中
4.丢弃旧数组
方法2:指定列表容量
List<Integer> list2 = new ArrayList<>(int initialCapacity);
方法3:赋值已存在的List来创建列表
List<Integer> list3 = new ArrayList<>(list1);
列表的一些常用方法
- 在指定位置新增值
list.add(int index, Object value)
- 更新指定位置的元素
list1.set(int index, Object value)
ListIterator的一些使用
ListIterator 继承了 Iterator 类,但比Iterator强大多了,ListIterator 可以双向移动,只能用于List及其子类型
- 创建:每个List对象或子类对象都可以通过listName.listIterator()来获得对象的ListIterator
ListIterator<Integer> listIterator = list1.listIterator();
- nextIndex():返回下⼀次调⽤ next ⽅法时将返回的对象索引(注意不是值,是相当于下标的)
- previousIndex():返回下⼀次调⽤ previous ⽅法时将返回的对象索引(注意不是值,是相当于下标的)
- listIterator.set(Object o)会使用指定的元素替换previous或者next方法返回的最后一个元素(会更新列表)
示例:
private static void listIteratorTest(){
List<Integer> list1 = new ArrayList<>();
list1.add(1);
list1.add(3);
list1.add(5);
list1.add(7);
System.out.println(list1);
ListIterator<Integer> listIterator = list1.listIterator();
// ListIterator 继承了 Iterator 类,但比Iterator强大多了
// ListIterator 可以双向移动
System.out.print("顺序遍历列表:");
while(listIterator.hasNext()){
System.out.print(listIterator.next() + "\t");
}
// nextIndex():返回下⼀次调⽤ next ⽅法时将返回的对象索引(注意不是值,是相当于下标的)
// previousIndex():返回下⼀次调⽤ previous ⽅法时将返回的对象索引(注意不是值,是相当于下标的)
System.out.println("\n末尾元素的 nextIndex :" + listIterator.nextIndex());
System.out.println("末尾元素的 previousIndex :" + listIterator.previousIndex());
System.out.print("\n倒序遍历列表:");
while(listIterator.hasPrevious()){
System.out.print(listIterator.previous() + "\t");
}
System.out.println();
System.out.println("首个元素的 nextIndex :" + listIterator.nextIndex());
System.out.println("首个元素的 previousIndex :" + listIterator.previousIndex());
listIterator.next();
System.out.println("第2个元素的 nextIndex :" + listIterator.nextIndex());
System.out.println("第2个元素的 previousIndex :" + listIterator.previousIndex());
//
System.out.println("列表目前的值:" + list1);
// 更新列表迭代器
// listIterator.set(23);
// System.out.println("迭代器第2个元素前的元素改为23了:" + list1);
System.out.println(listIterator.next());
System.out.println(listIterator.next());
System.out.println(listIterator.previous());
System.out.println(listIterator.next());
// listIterator.set(Object o)会使用指定的元素替换previous或者next方法返回的最后一个元素
listIterator.set(34);
System.out.println("迭代器第2个元素改为34了:" + list1);
}
执行结果:
[1, 3, 5, 7]
顺序遍历列表:1 3 5 7
末尾元素的 nextIndex :4
末尾元素的 previousIndex :3
倒序遍历列表:7 5 3 1
首个元素的 nextIndex :0
首个元素的 previousIndex :-1
第2个元素的 nextIndex :1
第2个元素的 previousIndex :0
列表目前的值:[1, 3, 5, 7]
3
5
5
5
迭代器第2个元素改为34了:[1, 3, 34, 7]
- listIterator.remove() 删除previous或者next方法返回的最后一个元素(会更新列表)
private static void listIteratorRemoveTest() {
ArrayList<Integer> list5 = new ArrayList<>();
list5.add(1);
list5.add(3);
list5.add(5);
list5.add(7);
System.out.println(list5);
ListIterator<Integer> listIterator = list5.listIterator();
System.out.println("删除元素:" + list5);
System.out.println(listIterator.next());
System.out.println(listIterator.next());
System.out.println(listIterator.previous());
// 删除某个元素
listIterator.remove();
System.out.println("删除元素后:" + list5);
}
执行结果:
[1, 3, 5, 7]
删除元素:[1, 3, 5, 7]
1
3
3
删除元素:[1, 5, 7]
- listIterator.add(Object o) 在当前位置添加元素
private static void listIteratorAddTest() {
ArrayList<Integer> list6 = new ArrayList<>();
list6.add(1);
list6.add(3);
System.out.println(list6);
ListIterator<Integer> listIterator = list6.listIterator();
System.out.println("添加元素前:" + list6);
System.out.println(listIterator.next());
System.out.println(listIterator.next());
System.out.println(listIterator.previous());
// 添加某个元素
listIterator.add(34);
System.out.println("添加元素后:" + list6);
}
执行结果:
[1, 3]
添加元素前:[1, 3]
1
3
3
添加元素后:[1, 34, 3]
- 如果列表结构有更新(比如增加或减少元素,更新不算),则要重新获取列表迭代器ListIterator,否则继续使用未更新的迭代器会报错
根据列表获取列表迭代器时,是使用的listIterator(),在ArrayList.class中可以看到,listIterator方法是返回的ArrayList.ListItr(0)对象
public ListIterator<E> listIterator() {
return new ArrayList.ListItr(0);
}
ListItr继承了ArrayList.Itr,且从其构造函数可以看出,创建对象时是使用父类方法
private class ListItr extends ArrayList<E>.Itr implements ListIterator<E> {
ListItr(int index) {
super();
this.cursor = index;
}
...
}
父类Itr 中,有个expectedModCount,这个是用来记录迭代器包含的个数的,从Itr的构造函数中可以看出,在创建迭代器时,其初始长度值是列表的长度值modCount
private class Itr implements Iterator<E> {
int cursor;
int lastRet = -1;
int expectedModCount;
Itr() {
this.expectedModCount = ArrayList.this.modCount;
}
...
}
去查看列表的 add() 和 remove() 等相关方法,会发现都对modCount进行了改动
public void add(int index, E element) {
this.rangeCheckForAdd(index);
++this.modCount;
int s;
Object[] elementData;
if ((s = this.size) == (elementData = this.elementData).length) {
elementData = this.grow();
}
System.arraycopy(elementData, index, elementData, index + 1, s - index);
elementData[index] = element;
this.size = s + 1;
}
public E remove(int index) {
Objects.checkIndex(index, this.size);
Object[] es = this.elementData;
E oldValue = es[index];
this.fastRemove(es, index);
return oldValue;
}
private void fastRemove(Object[] es, int i) {
++this.modCount;
int newSize;
if ((newSize = this.size - 1) > i) {
System.arraycopy(es, i + 1, es, i, newSize - i);
}
es[this.size = newSize] = null;
}
在内部类 ListItr 或其父类 Itr 中,add()、remove()、next()、previous()等会改变迭代器当前节点位置的方法,都先执行了checkForComodification()方法
public E previous() {
this.checkForComodification();
int i = this.cursor - 1;
if (i < 0) {
throw new NoSuchElementException();
} else {
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
} else {
this.cursor = i;
return elementData[this.lastRet = i];
}
}
}
checkForComodification()方法就是对迭代器的长度值和当前列表的长度值进行对等判断了,若不对等,则抛出异常
final void checkForComodification() {
if (ArrayList.this.modCount != this.expectedModCount) {
throw new ConcurrentModificationException();
}
}
综上所述,在使用列表方法改动了列表结构后,最好是重新获取迭代器,否则后续使用迭代器会抛出异常
- ArrayList 中的⽅法不是同步的,如果有线程安全需要可以使⽤ Vector。Vector 每次扩容到原来的 2 倍。
- 链表创建的时候不需要指定容量大小,因为链表保存的数据不是根据数组来实现的,是根据每个节点来保存的,每个节点都会保存其上一个元素的地址和下一个元素的地址,不需要连续的空间,是由节点连起来的,使⽤链表唯⼀的理由是尽可能地减少在列表中间插⼊或删除元素所付出的代价