ArrayList:
基于动态数组的List
它有两个重要的变量,size为存储的数据的个数、elementData 数组则是arraylist 的基础,因为他的内部就是通过这个数组来存储数据的。
private int size;
private transient Object[] elementData;
优点:
1,随机访问 调用get()、set()的时间复杂度为O(1),很高效
public E get(int index) { rangeCheck(index); return elementData(index); //内部通过数组索引获取数据,所以高效 } public E set(int index, E element) { rangeCheck(index); E oldValue = elementData(index); elementData[index] = element; return oldValue; }
2,尾部添加 add(E e)E为泛型,影响因素为扩容,扩容大小为1.5倍,需要进行内部数组的复制
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
缺点:
1,删除数据的效率相对较低,时间复杂度O(n) 主要影响因素为数据移动和遍历数组
public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { //遍历数组寻找元素,删除时索引之后的数据都要向前移动一位 fastRemove(index); return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false; }
public E remove(int index) { rangeCheck(index); modCount++; E oldValue = elementData(index); int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); //删除时索引后的所有数据向前移动一位 elementData[--size] = null; // clear to let GC do its work return oldValue; }
2,头部添加元素 时间复杂度O(n) ,影响因素数据移动
public void add(int index, E element) { rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index); //索引及之后的数据都要向后移动一位 elementData[index] = element; size++; }
3, 查找元素 indexof() 时间复杂度O(n),影响因素遍历数组
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; }
LinkedList:
基于双向链表
三个重要的变量,1、int size 2、Node<E> first 3、Node<E> last
size指LinkedList存储的元素的个数,first 指头结点,last 指尾结点
ArrayList 基于动态数组,需要分配特定的存储空间、内存地址连续。
LinkedList 基于双向链表,不需要分配特定存储空间、内存不连续,通过结点的头尾指针连接前后结点。
优点:
1,头部插入,删除效率高 时间复杂度O(1) 他不需要像ArrayList 那样要移动头部之后的所有元素
private void linkFirst(E e) { final Node<E> f = first; final Node<E> newNode = new Node<>(null, e, f); //新建一个结点,前结点为null,元素 e,后结点为 f first = newNode; if (f == null) last = newNode; else f.prev = newNode; size++; modCount++; }
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; }
2,尾部插入、删除 效率很高 时间复杂度O(1),相对于ArrayList来说不用扩容
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,随机访问 LinkedList的随机访问访问方面比较慢,时间复杂度O(n/2) 而ArrayList的随机访问为O(1)
Node<E> node(int index) { // assert isElementIndex(index); // 访问时,先获取该位置的结点,然后返回结点的值 if (index < (size >> 1)) { // 如果位置小于LinkedList元素个数的一半,则从头开始遍历,否则从后开始遍历,这也是时间复杂度为n/2的原因 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; }
2,查找元素 和ArrayList 一样需要遍历整个list,时间复杂度为O(n)
public int indexOf(Object o) { int index = 0; if (o == null) { for (Node<E> x = first; x != null; x = x.next) { if (x.item == null) return index; index++; } } else { for (Node<E> x = first; x != null; x = x.next) { if (o.equals(x.item)) return index; index++; } } return -1; }
3,中间部分插入和删除 时间复杂度为O(n),影响原因需要先定位到指定元素。定位后修改的效率很高
void linkBefore(E e, Node<E> succ) { // assert succ != null; final Node<E> pred = succ.prev; final Node<E> newNode = new Node<>(pred, e, succ); succ.prev = newNode; if (pred == null) first = newNode; else pred.next = newNode; size++; modCount++; }
LinkedList在执行中间插入和删除时比ArrayList高效,随机访问反面比较逊色。
向前遍历List,不修改List对象本身,可以用foreach
Iterator 只能向前移动,可以用next(),和remove()联合起来删除List中的对象
1 public static void main(String[] args) { 2 List<Integer> iList=new ArrayList<>(); 3 for (int i = 0; i < 8; i++) { 4 iList.add(i); 5 } 6 7 for (Integer i : iList) { 8 System.out.println(i); 9 } //foreach 遍历list 10 18 Iterator<Integer> iterator=iList.iterator(); 19 while (iterator.hasNext()) { 20 iterator.next(); 21 iterator.remove(); //迭代器遍历list,并进行删除 22 } 23 System.out.println(iList); 24 25 }
listIterator只能用于list类的访问,可以双向移动
TreeSet将元素储存在红-黑树,数据结构中。HashSet使用散列函数。LinkedHashSet使用散列和链表维护插入顺序。