Java集合List的总结。
java集合框架图。
常用的集合有ArrayList,LinkedList,它们继承于List接口;List接口继承于Collection。
关于ArrayList
ArrayList底层基于数组实现,数组在内存中是连续分配的地址空间。
区别:ArrayList的优势是对数组的升级,ArrayList是一个动态的数组,提供了一些方法,比如增删改查,直接操作。
数组在内存中地址空间是固定的,插入删除元素时,需要扩容缩容操作,效率没有使用ArrayList高;然而数组利用下标索引查找元素时效率更高,ArrayList需要从头到尾进行遍历。
自定义ArrayList
提供了add(E value)/remove(E value)/set(index,value)/get(index)/contains(value)/size()/isEmpty()
import java.util.Arrays;
public class MyArrayList<E> {
private E elements[];
private int size;
private static final int defaultcapacity=5;//默认的初始化参数
public MyArrayList(int capacity) {
elements = (E[])new Object[capacity];
}
public MyArrayList(){
this(defaultcapacity);
}
public void add(E value){
//是否需要扩容
if (size==elements.length){
elements=Arrays.copyOf(elements,elements.length*2);//二倍扩容
}else {
elements[size++]=value;
}
}
public boolean remove(E value){
if (size==0){
return false;
}else {
for (int i=0;i<size;i++){
if (elements[i]==value){
System.arraycopy(elements,i+1,elements,i,size-1-i);
elements[--size]=null;
return true;
}
}
}
return false;
}
public void checkIndex(int index){
if (index<0||index>elements.length){
throw new UnsupportedOperationException("下标违法");
}
}
public boolean set(int index,E value){
try{
checkIndex(index);
elements[index] = value;
return true;
}catch (UnsupportedOperationException e){
System.out.println(e.getMessage());
return false;
}
}
public E get(int index){
try{
checkIndex(index);
return elements[index];
}catch (UnsupportedOperationException e){
System.out.println(e.getMessage());
return null;
}
}
public boolean contain(E value){
if (size==0){
return false;
}
for (int i=0;i<size;i++){
if (elements[i].equals(value)){
return true;
}
}
return false;
}
public int size(){
return this.size;
}
public boolean isEmpty(){
return this.size==0;
}
public String toString(){
StringBuilder strs = new StringBuilder();
for (int i=0;i<size;i++){
strs.append(elements[i]+" ");
}
return strs.toString();
}
}
ArrayList源码
扩容方式
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 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);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
增删改查操作
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
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;
}
//尾部添加元素
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
//中间添加元素
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++;
}
//有返回值的删除
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;
}
//无返回值的删除
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;
}
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
}
ArrayList中迭代器的实现
自定义迭代器
class MyIterator<E> implements Iterable{
private E[] elements;//存放元素的容器
private int size; //存放元素的个数
public MyIterator(E[] elements){
this.elements = elements;
this.size = elements.length;
}
@Override
public Iterator iterator() {
return new Iterator() {
@Override
public boolean hasNext() {
return false;
}
@Override
public Object next() {
return null;
}
};
}
}
迭代器源码
源码中有modCount,定义于AbstractList,存储结构的修改次数
fast-fail机制:使用迭代器遍历时,如果调用集合add/remove方法修改集合的结构,则会抛出ConcurrentModificationException异常;
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
CopyOnWriteArrayList实现了non-fast-fail机制
non-fast-fail:复制集合,对集合结构性操作都建立在副本之上,迭代器迭代的是原先的集合,迭代器遍历读取到的数据并不是最新的数据,以copyonwriteArrayList中的add源码为例:
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
lock方法使对集合的结构性操作都建立在getArray方法复制的副本之上,不会对原来的集合产生影响。
Vector
Vector和ArrayList都是基于数组实现的,继承关系和实现的接口都是一样的;
但ArrayList是以1.5倍方式扩容的,Vector是2倍方式扩容。
Vector构造器中可以传入当前扩容方式;
Vector是线程安全的,同步的,所以效率会低于ArrayList;
将ArrayList使用Collection.synchornizedlist(list)就可以实现线程安全。
关于LinkedList
LinkedList底层是基于双向链表实现的,它和基于数组的list相比,在内存中的空间是不连续的。
Linkedlist可以作为list使用,也可以作为queue使用。
自定义LinkedList
包括增删改查方法
import java.sql.SQLOutput;
public class MyLinkedList1<T extends Comparable<T>> {
private Node<T> head;
private Node<T> tail;
private static class Node<E>{// this
E value;
Node<E> next;//后继
Node<E> prev;//前驱
public Node(E value) {
this.value = value;
}
}
public MyLinkedList1(Node<T> head, Node<T> tail) {
this.head = head;
this.tail = tail;
}
public void addHead(T value){
Node<T> newNode = new Node<T>(value);
if (head==null||tail==null){
head=newNode;
tail=newNode;
}
head.prev=newNode;
newNode.next=head;
head=newNode;
}
public void addTail(T value){
Node<T> newNode = new Node<T>(value);
if (tail==null||head==null){//空链表
head=newNode;
tail=newNode;
}
tail.next=newNode;
newNode.prev=tail;
tail=newNode;
}
public void removeHead(){
if (head==null||tail==null){
return;
}
if (head==tail){//只有一个结点
head=null;
}
Node<T> p;
p=head;
head=head.next;
head.prev=null;
free(p);
}
public void free(Node<T> node){
if (node == null){
return;
}
node.prev=null;
node.next=null;
node.value=null;
}
public void removeTail(){
if (head==null||tail==null){
return;
}
if (head==tail){
head=null;
free(head);
}
Node<T> q;
q = tail;
tail=tail.prev;
tail.next=null;
free(q);
}
public void remove(T value){
if (head==null||tail==null){
return;
}
if (head==tail&&head.value.compareTo(value)==0){
removeHead();
}
Node<T> q;
for (Node<T> p=head;p!=null;){
if(p.value.compareTo(value) == 0){
q = p;
p=p.next;
if(q == head){
head = head.next;
}
}else{
// p 待删除结点的前驱指向
q = p.next;
if(q!= null && q.value.compareTo(value) == 0){
p.next = q.next;
if(q.next != null) {
q.next.prev = p;
}
}else{
p = p.next;
}
}
free(q);
}
}
public void change(T value,T aim,){
for (Node<T> p =head;p!=null;p=p.next){
if (p.value.compareTo(value)==0){
p.value=aim;
}
}
}
public boolean contain(T value){
for (Node<T> p =head;p!=null;p=p.next){
if (p.value.compareTo(value)==0){
return true;
}
}
return false;
}
public void show(){
for (Node<T> p =head;p!=null;p=p.next){
System.out.println(p.value+" ");
}
System.out.println( );
}
}
LinkedList和ArrayList的区别和联系
ArrayList和LinkedList都是实现了List接口的容器类,用于存储一系列的对象引用。
他们都可以对元素的增删改查进行操作。
1.ArrayList底层由数组实现,内存中是连续的内存空间,需要维护容量大小;Linkedlist底层由链表实现,内存是非连续的内存空间,不需要维护容量大小。
2.当实现get和set操作时,ArrayList比LinkedList的效率更高,因为LinkedList是线性的数据存储方式,所以需要移动指针
从前往后依次查找。
当实现增加和删除操作时,LinkedList比ArrayList的效率更高,因为ArrayList是数组,进行增删操作时,
会对数据的下标索引造成影响,需要进行数据的移动。
3.ArrayList需要手动设置固定大小的容量,但使用方便,创建数组,添加数据,然后通过调用下标进行使用;
LinkedList能够动态的随数据量的变化而变化,但不便于使用。