ArrayList 和linkedList 也是面试中经常遇到的问题,也是平时开发中最常用的list,了解这两个的特性并恰当区分使用,可以一定程度上提高代码的执行效率。今个记一下这两个的使用方法,并总结区别。
ArrayList
arraylist就是一个数组,一个动态数组,可以动态改变元素,自定义数组大小。实现了RandomAcess,Cloneable、Serializable接口。
一、先简单看一下构造方法:
1.public ArrayList();
2.public ArrayList(ICollection);
3.public ArrayList(int);
第一种:默认的构造器,初始化一个容量为10的数组。
第二种:传入一个实现ICollection的对象,并将传入对象中的元素赋值给新建的list;
第三种:自定义数组的容量。
二、Arraylist中的方法
其中有get、set、add、indexof,contains,clone、size、toarray、isEmpty、sort等方法的使用,我觉得不是什么难点,api介绍很清楚,在后续比较与linkedlist区别时会介绍一些,现在稍微总结一下:
1.当add方法一直添加,知道超过现有容量大小时,arraylist重新设置的容量为(原始容量*3)/2+1,也就是原本容量增加1/2
2.arraylist实现了Cloneable接口,中心就是其中的clone方法。可以将此对象的全部元素复制到另一个数组中。
3.arraylist实现Serializable接口,是可序列化的,当读写的时候,会容量先行,即:写入的时候,先写入容量,在放置元素;读取时,先读取容量,在去除元素。需要注意的是,可序列化只是一个标志,意味着它可以被序列化,并没有实际性的方法、字段,但是这不代表没有意义,因为在上述读写过程中,就会依次进行。我还没有了解深入,但事实就是这样。后续在补充吧。
三、ArrayList的遍历
谈到数组,集合。免不了使用遍历,
有三种方法可以遍历ArrayList;
1.迭代器
部分代码:
Iterator iter = list.iterator();
while (iter.hasNext()) {
value = (Integer)iter.next();
}
2.随机访问
for (int i=0; i<list.size; i++) {
value = (Integer)list.get(i);
}
3.foreach
for (Integer integ:list) {
value = integ;
}
测试一下,就能知道他们之间的效率 随机访问>foreach>迭代器。这算是ArrayList的最大优点。
LinkedList
LinkedList是一个双向链表,可以被当做堆栈、队列或者双向队列。实现了list、Deque、Cloneable、Serializable接口。
一、为数不多的三个属性:
1、size,记录当前list有多少节点。
2、first,代表当前第一个节点。
3、last,代表当前最后一个节点。
后面的方法都与这仨属性息息相关。
二方法
、构造方法:
1.public LinkedList() {
}
2.
public LinkedList(Collection<? extends E> c) { this(); addAll(c);
}
只有两个,与arraylist中对应的两个用法相同。
三、类中方法
1.add
public boolean add(E e) { linkLast(e); return true; }
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++; }此方法就是将新的元素放到链表最后,长度加1,修改次数加1;
2.带参add
public void add(int index, E element) { checkPositionIndex(index); if (index == size) linkLast(element); else linkBefore(element, node(index)); }很清晰,不赘述。
3.get
public E get(int index) { checkElementIndex(index); return node(index).item; }
Node<E> node(int index) { // assert isElementIndex(index); if (index < (size >> 1)) { Node<E> x = first; for (int i = 0; i < index; i++) x = x.next; return x; } else { Node<E> x = last; for (int i = size - 1; i > index; i--) x = x.prev; return x; } }首先判断有没有超出索引长度。然后和长度的1/2对比,小于就从头遍历,大于就从尾部遍历。
(linkedlist不能快速访问,只能挨个遍历,为了尽可能少的遍历,判断从头部开始还是尾部开始是个很好的方法)
4.remove
public E remove() { return removeFirst(); }
public E removeFirst() { final Node<E> f = first; if (f == null) throw new NoSuchElementException(); return unlinkFirst(f); }
private E unlinkFirst(Node<E> f) { // assert f == first && f != null; final E element = f.item; final Node<E> next = f.next; f.item = null; f.next = null; // help GC first = next; if (next == null) last = null; else next.prev = null; size--; modCount++; return element; }即:remove就是removefirst,将第一个节点设为null,并将全局变量first设置为当前节点的下一个节点。长度减一,修改次数加一
5.removelast
private E unlinkLast(Node<E> l) { // assert l == last && l != null; final E element = l.item; final Node<E> prev = l.prev; l.item = null; l.prev = null; // help GC last = prev; if (prev == null) first = null; else prev.next = null; size--; modCount++; return element; }将最后一个节点设为null,将原本最后一个节点的上一个节点设为全局变量last,长度减一,修改次数加一。
6.remove(object o)
public boolean remove(Object o) { if (o == null) { for (Node<E> x = first; x != null; x = x.next) { if (x.item == null) { unlink(x); return true; } } } else { for (Node<E> x = first; x != null; x = x.next) { if (o.equals(x.item)) { unlink(x); return true; } } } return false; }
从first到last循环遍历,如果参数和当前元素相等,调用unlink(下面解释此方法)
7.remove(int index)
public E remove(int index) { checkElementIndex(index); return unlink(node(index)); }
Node<E> node(int index) { // assert isElementIndex(index); if (index < (size >> 1)) { Node<E> x = first; for (int i = 0; i < index; i++) x = x.next; return x; } else { Node<E> x = last; for (int i = size - 1; i > index; i--) x = x.prev; return x; } }先调用node方法,找到索引对应的值,之后和remove(object o)相同
E unlink(Node<E> x) { // assert x != null; final E element = x.item; final Node<E> next = x.next; final Node<E> prev = x.prev; if (prev == null) { first = next; } else { prev.next = next; x.prev = null; } if (next == null) { last = prev; } else { next.prev = prev; x.next = null; } x.item = null; size--; modCount++; return element; }
这个方法的思路是获取要移除节点的上一节点和下一节点,
如果上一节点是空,则list的first节点为当前节点的下一节点,
如果下一节点为空,list的last节点为当前节点的上一节点。
如果都不是,把当前节点设为null,这样当前节点的上一节点指向当前节点的下一节点。
8.toarray
public Object[] toArray() { Object[] result = new Object[size]; int i = 0; for (Node<E> x = first; x != null; x = x.next) result[i++] = x.item; return result; }
遍历当前list,并挨个赋值到新的数组中。
区别
从上面的分析已经可以大致看出两者的区别了,总结一下:
linkedlist是基于链表的list,从大量的成员方法也能看出,添加,删除占了大比重,相比Arraylist的增删,linkedlist更高效,因为没有list容量的问题,不需要再添加时判断容量是否超出;删除时直接删除元素,上一节点会指向下一节点,而arraylist删除指定元素,后面的所有元素都必须向前移动相应位置。
ArrayList的快速访问是一个优势,从上面linkedlist的方法中可以看到,遍历占据了大量的份额,任何的查询,大部分的删除都得在真正的行为开始前进行所有元素的遍历。而作为数组的arrayList,可以通过索引,直接访问到对应的元素。
总而言之,arraylist更适合读取,linkedlist更适合增删。