list类 java_面试3——java集合类总结(List)

1.集合类

数组:可以存储对象,也可以存储基本数据类型,但是一次只能存储一种类型,且长度一定,不可改变。

集合:只能存储对象,长度可变,可以存储不同类型的对象。Java集合类主要有三种:set,list,map

eb6eb73076a221b03efc52eb2cc74395.png

其中,实现边框的是实现类,折线边框的是抽象类,点线边框的是接口

从图中可以看出,Collection接口是集合类(List,Set,Queue)的根接口,java中没有提供这个接口的直接实现类。有三个子接口List,Set,Queue,注意,Map不是collection的子接口。

Collection中的方法:

a2e1c5dba6600a55f5b81e131d279d05.png

2.Collection中的List和Set接口

首先说一下List接口。 List里存放的对象是有序的,可重复的,可以为null的集合。List关注的是索引,拥有一系列和索引相关的方法,查询速度快。

b1370e019a6145c81ac89f4623622950.png

List接口下主要的三个实现类:Arraylist,Linkedlist,Vector。

(1)ArrayList

arraylist实现List接口,继承AbstractList。底层是数组实现,可以自增扩容。是非线程安全的,一般用于单线程环境中(与Vector最大的区别就是,V是线程安全的,所以A比V的性能相对要好些),在多线程中,可以选择Vector或者CopyOnWriteArrayList。Arraylist实现了Serializable接口,支持序列化,能够通过序列化传输;实现了RandomAccess接口(只是个标注接口,没有实际的方法),支持快速随机访问,主要变现为可以通过下标直接访问(因为Arraylist的底层是数组,可直接用数组下标来索引);实现了Cloneable接口,能被克隆。 Arraylist是基于动态数组实现的。

public class ArrayList

extends AbstractList

implements List, RandomAccess, Cloneable, Serializable

1)初始化

Arraylist提供了三种初始化方法。

public ArrayList(intinitialCapacity) {if (initialCapacity > 0) {this.elementData = newObject[initialCapacity];

}else if (initialCapacity == 0) {this.elementData =EMPTY_ELEMENTDATA;

}else{throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);

}

}/*** Constructs an empty list with an initial capacity of ten.//默认提供容量为10的数组。*/

publicArrayList() {this.elementData =DEFAULTCAPACITY_EMPTY_ELEMENTDATA;

}/*** Constructs a list containing the elements of the specified

* collection, in the order they are returned by the collection's

* iterator.

*

*@paramc the collection whose elements are to be placed into this list

*@throwsNullPointerException if the specified collection is null*/

public ArrayList(Collection extends E>c) {

elementData=c.toArray();if ((size = elementData.length) != 0) {//c.toArray might (incorrectly) not return Object[] (see 6260652)

if (elementData.getClass() != Object[].class)

elementData= Arrays.copyOf(elementData, size, Object[].class); //注意size是记录该list集合当前元素的数量,不是容量

}else{//replace with empty array.

this.elementData =EMPTY_ELEMENTDATA;

}

}

2)动态调整

无参构造函数默认的是空数组,为什么注释说是容量为10的数组。主要是ArrayList的add方法。add方法中调用了ensureCapacityInternal()方法,

/*** Appends the specified element to the end of this list.

*

*@parame element to be appended to this list

*@returntrue (as specified by {@linkCollection#add})*/

public booleanadd(E e) {

ensureCapacityInternal(size+ 1); //Increments modCount!!

elementData[size++] =e;return true;

}private void ensureCapacityInternal(intminCapacity) {

ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));

}private static int calculateCapacity(Object[] elementData, intminCapacity) {if (elementData ==DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {returnMath.max(DEFAULT_CAPACITY, minCapacity);

}returnminCapacity;

}

从上述源码中可以看出,当elementData为空数组时,则使用Math.max(DEFAULT_CAPACITY, minCapacity)进行选择一个最大的,其中DEFAULT_CAPACITY为arraylist定义的静态常量=10;

private void ensureCapacityInternal(intminCapacity) {

ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));

}private void ensureExplicitCapacity(intminCapacity) {

modCount++;//overflow-conscious code

if (minCapacity - elementData.length > 0)

grow(minCapacity);

}/*** Increases the capacity to ensure that it can hold at least the

* number of elements specified by the minimum capacity argument.

*

*@paramminCapacity the desired minimum capacity*/

private void grow(intminCapacity) {//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(intminCapacity) {if (minCapacity < 0) //overflow

throw newOutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :

MAX_ARRAY_SIZE;

}

动态扩容最关键是grow方法 。通过源码中int newCapacity = oldCapacity + (oldCapacity >> 1);可得容量扩大为原来的1.5倍。

总之,ArrayList默认容量是10,如果初始化时一开始指定了容量,或者通过集合作为元素,则容量为指定的大小或参数集合的大小。每次扩容为原来的1.5倍,如果新增后超过这个容量,则容量为新增后所需的最小容量。如果增加1.5倍后的新容量超过限制的容量,则用所需的最小容量与限制的容量进行判断,超过则指定为Integer的最大值,否则指定为限制容量大小。然后通过数组的复制将原数据复制到一个更大(新的容量大小)的数组。

3)遍历方式

第一,随机访问,通过索引获取元素。ArrayList实现了randomaccess接口。

/*** Returns the element at the specified position in this list.

*

*@paramindex index of the element to return

*@returnthe element at the specified position in this list

*@throwsIndexOutOfBoundsException {@inheritDoc}*/

public E get(intindex) {

rangeCheck(index);returnelementData(index);

}

第二,for循环,foreach循环。

packageTwo;importjava.util.ArrayList;public classone {public static voidmain(String[] args) {

ArrayList arrayList = newArrayList();

arrayList.add(1);

arrayList.add(3);

arrayList.add(9);//for循环

for (int i = 0;i

System.out.print(arrayList.get(i));

}//foreach循环

for(Integer list:arrayList) {

System.out.print(list);

}

}

}

第三种:通过迭代器遍历

public static voidmain(String[] args) {

ArrayList arrayList = newArrayList();

arrayList.add(1);

arrayList.add(3);

arrayList.add(9);

Integer integer= null;

Iterator iterator=arrayList.iterator();while(iterator.hasNext()){

integer=(Integer) iterator.next();

System.out.println(integer);

}

}

上述三种遍历方式中,随机访问的效率最高,使用迭代器的效率最低。

总结:

ArrayList是List接口的一个可变大小的数组的实现

ArrayList的内部是使用一个Object对象数组来存储元素的

初始化ArrayList的时候,可以指定初始化容量的大小,如果不指定,就会使用默认大小,为10

当添加一个新元素的时候,首先会检查容量是否足够添加这个元素,如果够就直接添加,如果不够就进行扩容,扩容为原数组容量的1.5倍

当删除一个元素的时候,会将数组右边的元素全部左移,添加一个元素时,右移。

2)LinkedList

public class LinkedList

extends AbstractSequentialList

implements List, Deque, Cloneable, Serializable

LinkedList是一个继承AbstractSequentialList的双向链表。它可以被当作堆栈、队列或双端队列进行操作。

LinkedList实现了List接口,能对它进行队列操作。

LinkedList实现了Deque接口,即能将LinkedList当作双端队列使用

LinkedList实现了Cloneable接口,能克隆

LinkedList实现了java.io.Serializable接口,支持序列化,能通过序列化去传输

LinkedList是非同步的。

请注意,此实现不同步。 如果多个线程同时访问链接列表,并且至少有一个线程在结构上修改列表,则必须在外部进行同步。 (结构修改是添加或删除一个或多个元素的任何操作;仅设置元素的值不是结构修改。)这通常通过在自然封装列表的对象上进行同步来实现。 如果没有这样的对象存在,列表应该使用Collections.synchronizedList方法“包装”。 这最好在创建时完成,以防止意外的不同步访问列表:

List list= Collections.synchronizedList(new LinkedList(...));

源码阅读有兴趣可自己去看

LinkedList特点:

双向链表实现,没有固定容量,不需扩容

元素是有序的,允许null值,输入输出顺序一致

所有指定位置的操作都是从头开始遍历的

需要更多的内存,LinkedList每个节点中需要存储前后节点的信息,占用空间更多

查找效率低,插入删除效率高。

3)Vector

Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和ArrayList创建的Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出ConcurrentModificationException,因此必须捕获该异常。

3.List实现类的各种比较

1)Vector和ArrayList

相同点:两者都是基于存储元素的数组来实现的,它们会在内存中开辟块连续的空间来存储,由于数据存储是连续的,它们支持用序号(下标)来访问元素,但是插入和删除是要移动容器中的元素,所以执行较慢。两者都有一个初始化的容量的大小,为10;当里面存储的元素超过这个大小时,就会动态的进行扩容。Vector默认扩充为原来的2倍,ArrayList默认扩充为原来的1.5倍。

区别:二者最大的区别在与synchronization(同步)的使用。在ArrayList中没有一个方法是同步的,而在Vector中,绝大部分方法都是同步的。所以Vector是线程安全的,而ArrayList不是线程安全的。由于Vector提供同步,所以性能上较低于ArrayList。

2)ArrayList和LinkedList

ArrayList是实现了基于动态数组的数据结构,而LinkedList是基于双向链表的数据结构

对于随机访问,ArrayList要优于LinkedList,因为LinkedList要移动指针

对于插入和删除,LinkedList较占优势,ArrayList要移动数据。

ArrayList和LinkedList都是非线程安全的容器

在实际使用中,若对数据的主要操作为索引或只在集合的末端增加、删除元素,使用Arraylist和vector效率比较高;若对数据的操作主要为指定位置的插入或删除操作,使用Linkedlist效率比较高;当在多线程中使用容器时(即多个线程会同时访问该容器,),选用vector较为安全。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值