一:ArrayList实现类:ArrayList是List接口的实现类
数据插入有序
可以存储null值
数据可以重复
底层的数据结构是数组
ArrayList的继承关系:
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
继承AbstractList类,实现List接口;
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>
AbstractLiST继承AbstractCollection类,实现List接口;
public abstract class AbstractCollection<E> implements Collection<E>
AbstractCollection实现Collection接口;
public interface Collection<E> extends Iterable<E>
Collection继承Iterable类;
public interface Iterable<T> { /** * Returns an iterator over elements of type {@code T}. * * @return an Iterator. */ Iterator<T> iterator(); }
Iterable是一个接口;
相关属性:
底层:
ArrayList中维护了一个Object类型的数组elementData,创建对象时初始容量设置为0,当第一次添加时,将elementData容量设置为10,该容量代表了数组的大小。随着容器中的元素不断增加,当容器中的元素个数达到10个时,容器已满,此时容器就会进行扩容,扩容后的大小为扩容前大小的1.5倍,在每次向容器中增加元素的同时都会进行容量检查,当快溢出时,就会进行扩容操作。
private static final int DEFAULT_CAPACITY = 10;//默认容量是10 private static final Object[] EMPTY_ELEMENTDATA = {}; private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. Any * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA * will be expanded to DEFAULT_CAPACITY when the first element is added. */ transient Object[] elementData; // non-private to simplify nested class access /** * The size of the ArrayList (the number of elements it contains). * * @serial */ private int size;
Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。当一个对象被序列化的时候,transient型变量的值不包括在序列化的表示中,然而非transient型的变量是被包括进去的。
1. 扩容机制: 数组的扩容是按照原数组长度的1.5倍扩容
/** * 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; int newCapacity = oldCapacity + (oldCapacity >> 1); //>>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); }
2.得到该元素:
/** * Returns the element at the specified position in this list. * * @param index index of the element to return * @return the element at the specified position in this list * @throws IndexOutOfBoundsException {@inheritDoc} */ public E get(int index) { rangeCheck(index); return elementData(index); }
3.清空数组:clear
public void clear() { modCount++; // clear to let GC do its work垃圾回收器 for (int i = 0; i < size; i++) elementData[i] = null; size = 0; }
4.删除:因为ArrayList中可以存储值为null的元素,所以需要对null进行特殊判断
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; }
fastRemove的实现:主要将index+1号以后的元素移动到index号元素
private void fastRemove(int index) { modCount++; 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 }
arraycopy源码:
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
5.indexOf( )和lastIndexOf( )
假如一个数组中没有相同的两个数或者不存在该数,它的lastIndexOf()=indexOf();
所以,用此方法可以判断一个数组中是否有两个相同的元素。
lastIndexOf源码:
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; }
6.toArray()方法:将集合转化为数组类型
Collection<String> col=new ArrayList<>(); col.add("zhang"); col.add("zhao"); col.add("wang"); col.add("li"); col.add("hui"); Object[] c=col.toArray(); String[] s=(String[]) col.toArray();//强制转型 System.out.println(Arrays.toString(c)); System.out.println(Arrays.toString(s));
会报类型转换的异常:Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
原因:toArray是一个Object类型的数组,不能将Object[ ] 转化为String[ ],转化的话只能是取出每一个元素再转化。
java中的强制类型转换只是针对单个对象的,想要偷懒将整个数组转换成另外一种类型的数组是不行的,这和数组初始化时需要一个个来也是类似的。
一:自定义实现ArrayList
package Lesson1; import java.util.Arrays; /** * 问题:my.size()方法不等于elementData-1 elementData.length等于构造函数初始化时,传入的capacity, * 所以mysize()方法与elementData.length无关。所以getSize()方法的返回值可以直接为size * 自定义实现ArrayList * @param <T> */ public class ArrayListMyself <T>{ private Object[] elementData;//定义了一个数组 private int size; public ArrayListMyself(int capacity){ elementData=(T[])new Object[capacity]; } public ArrayListMyself(){ this(2); } /** * 添加值为value的元素 */ public boolean add(T value) { if(full()){ Arrays.copyOf(elementData,elementData.length*2); } this.elementData[this.size++]=value; return true; } public boolean full() { return this.elementData.length==this.getSize(); } /** * * @return直接返回长度size */ public int getSize(){ return size; } /** * 获取index号元素 ,返回元素的值 */ public T get(int index) { if(index<0 || index>size){ throw new IndexOutOfBoundsException("参数异常"); } for (int i=0;i<elementData.length;i++){ if(i==index){ return (T)elementData[i]; } } return null; } /** * 删除index号元素 */ public boolean remove(int index) { if(index<0 ||index>size){ return false; }else { for (int i=index;i<size-1;i++){ elementData[i]=elementData[i+1]; } size--; } return true; } public static void main(String[] args) { ArrayListMyself my=new ArrayListMyself(); my.add(4); my.add(5); System.out.println(my.size);//2 System.out.println(my.elementData.length);//2 System.out.println(my.remove(1));//true System.out.println(my.getSize());//1 System.out.println(my.remove(0));//true System.out.println(my.size);//0 if (my.size!=0){ for (int i = 0; i <my.size; i++) { System.out.print(my.get(i)+" "); } }else { System.out.println("当前数组为空!"); } } }
或者可以自己写一个tooString方法,实现数组的打印:
public String tooString(){ StringBuilder s=new StringBuilder(); if(this.size==0){ return null; } else { for (int i = 0; i < this.size; i++) { s = s.append(get(i) + " "); } return s.toString(); } }
可以封装一个方法进行参数的检查,在添加或者删除的时候直接调用该方法:
(源码中给出了专门添加元素的方法rangeCheckForAdd)
private void rangeCheckForAdd(int index) { if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }
♥ subList方法:推荐文章:为什么呢阿里巴巴要求谨慎使用ArrayList中的subList方法
测试代码:
package Lesson2;
import java.util.ArrayList;
import java.util.List;
class A{
public static void main(String[] args) {
ArrayList a=new ArrayList();
a.add(1);
a.add(2);
a.add(3);
//List类型的subList public List<E> subList(int fromIndex, int toIndex)
List b=a.subList(0,2);//返回子串0-1号
System.out.println(a);// 1 2 3
System.out.println(b);//1 2
/**
* 非结构性地对子串进行改变,原串与子串都会被改变
*/
b.set(1,6);//将1号位置的元素设置为6
System.out.println(b);//1 6
System.out.println(a);//1 6 3
/**
* 结构性的改变字串:增加元素,原串也会相应增加
*/
b.add(10);
System.out.println(b);//1 6 10
System.out.println(a);//1 6 10 3
/**
* 结构性地改变原串,会报错:Exception in thread "main" java.util.ConcurrentModificationException
*/
// a.add(4);
// System.out.println(b);
// System.out.println(a);
}
}
[1, 2, 3] [1, 2] [1, 6] [1, 6, 3] [1, 6, 10] [1, 6, 10, 3]
所以,对子串和原List的修改还需要注意几点,尤其是他们之间的相互影响:
1、对父(sourceList)子(subList)List做的非结构性修改(non-structural changes),都会影响到彼此。
2、对子List做结构性修改,操作同样会反映到父List上。
3、对父List做结构性修改,会抛出异常ConcurrentModificationException。
二:LinkedList实现类:
继承关系:
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable
LinkedList 是一个继承于AbstractSequentialList的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。
LinkedList 实现 List 接口,能对它进行队列操作。
LinkedList 实现 Deque 接口,即能将LinkedList当作双端队列使用。
LinkedList 实现了Cloneable接口,即覆盖了函数clone(),能克隆。
LinkedList 实现java.io.Serializable接口,这意味着LinkedList支持序列化,能通过序列化去传输。
默认属性:
transient int size = 0; transient Node<E> first; transient Node<E> last;
数据结构:
LinkedList的本质是双向链表。
(01) LinkedList继承于AbstractSequentialList,并且实现了Dequeue接口。
(02) LinkedList包含两个重要的成员:header 和 size。
header是双向链表的表头,它是双向链表节点所对应的类Entry的实例。Entry中包含成员变量: previous, next, element。其中,previous是该节点的上一个节点,next是该节点的下一个节点,element是该节点所包含的值。
size是双向链表中节点的个数。
LinkedList的扩容机制:
由于它的底层是用双向链表实现的,没有初始化大小,也没有扩容的机制。
package List; import java.util.Iterator; import java.util.LinkedList; /** * 大量操作首尾元素的方法: * void addFirst(E e) void addLast(E e) * E getFirst() E getLast() * E removeFist() E removeLast() * E pop() void push(E e) boolean isEmpty() * */ public class LinkedListDemo { public static void main(String[] args) { show1(); } private static void show1() { LinkedList<String> linkedList=new LinkedList(); linkedList.add("a"); linkedList.add("b"); linkedList.add("c"); System.out.println(linkedList); //addFirst等效于push linkedList.addFirst("begin"); System.out.println(linkedList); linkedList.push("start"); System.out.println(linkedList); //add类似于addLast linkedList.addLast("last"); System.out.println(linkedList.getFirst());//得到第一个元素 System.out.println(linkedList.getLast());//得到最后一个元素 //removeFirst( )相当于pop( ) System.out.println("第一个元素:"+linkedList.removeFirst());//删除第一个元素 System.out.println("最后一个元素:"+linkedList.removeLast());//删除最后一个元素 //迭代器遍历集合 Iterator<String> i=linkedList.iterator(); while (i.hasNext()){ System.out.print(i.next()+" "); } System.out.println(); System.out.println(linkedList.isEmpty()); linkedList.clear();//清空链表 System.out.println(linkedList.isEmpty()); }
public static void show2(){
LinkedList<String> linkedList=new LinkedList();
linkedList.add("a");
linkedList.add("b");
linkedList.add("c");
System.out.println(linkedList);
System.out.println(linkedList.get(1));
System.out.println(linkedList.size());
System.out.println(linkedList.remove(1));
Iterator<String> i=linkedList.iterator();
while (i.hasNext()){
System.out.print(i.next()+" ");
}
}
}