一、什么是集合类
集合类是Java数据结构的实现。Java的集合类是java.util包中的重要内容,它允许以各种方式将元素分组,并定义了各种使这些元素更容易操作的方法。Java集合类是Java将一些基本的和使用频率极高的基础类进行封装和增强后再以一个类的形式提供。集合类是可以往里面保存多个对象的类,存放的是对象,不同的集合类有不同的功能和特点,适合不同的场合,用以解决一些实际问题。
二、集合类的组成分布
Java中的集合类可以分为两大类:一类是实现Collection接口;另一类是实现Map接口。
Collection是一个基本的集合接口,Collection中可以容纳一组集合元素(Element)。
Map没有继承Collection接口,与Collection是并列关系。Map提供键(key)到值(value)的映射。一个Map中不能包含相同的键,每个键只能映射一个值。
Collection有两个重要的子接口List和Set。List表达一个有序的集合,List中的每个元素都有索引,使用此接口能够准确的控制每个元素插入的位置。用户也能够使用索引来访问List中的元素,List类似于Java的数组。Set接口的特点是不能包含重复的元素。对Set中任意的两个元素element1和element2都有elementl.equals(element2)= false。另外,Set最多有一个null元素。此接口模仿了数学上的集合概念。
Collection接口、List接口、Set接口以及相关类的关系如下图所示。
三、List的组成分布
List的实现类有:ArrayList,Vector,LinkedList类。
ArrayList:作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[] elementData存储
LinkedList:对于频繁的插入和删除操作,使用此类效率比ArrayList高;底层使用双向链表存储
Vector:作为List接口的古老实现类:线程安全的,执行效率低;底层使用Object[] elementData存储
四、ArrayList的源码分析
jdk 7情况下
ArrayList list = new ArrayList();//底层创建了长度是10的Object[]数组elementData
list.add(123);//elementData[0] = new Integer(123);
list.add(11);
/*
如果此次的添加导致底层elementData数组变量不够,则扩容。
默认情况下,扩容为原来容量的1.5倍,同时需要将原有数组中的数
据复制到新的数组中。
*/
结论:建议开发中使用带参的构造器:ArrayList list = new ArrayList(int capacity)
jdk 8中ArrayList的变化:
ArrayList list = new ArrayList();//底层Object[] elementData初始化为{},并没有创建长度为10的数组
list.add(123);
/*
第一次调用add()时,底层才创建了长度为10的数组,并将数组123添加到elementData数组中
*/
后续的添加和扩容操作与jdk 7 无异。
小结:
jdk 7中ArrayList的对象的创建类似于单例模式的饿汉式,而jdk 8中的ArrayList的对象的创建类似于单例的懒汉式,延迟了数组的创建,节省空间。
五、LinkedList的源码分析
LinkedList list = new LinkedList();//内部声明了Node类型的first和last属性,默认值为null。
list.add(123);//将123封装到Node中,创建了Node对象。
其中,Node定义为:体现了LinkedList的双向链表的说法
private static class Node<E>{
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev,E element,Node<E> next){
this.item = element;
this.next = next;
this.prev = prev;
}
}
小结:
LinkedLis是实现了基于双链表的数据结构,在插入、删除集合中任何位置的元素所花费的时间都是一样的,但它在索引一个元素的时候比较慢。
六、Vector的源码分析
private void grow(int minCapacity) {
// 获取数组的真实容量(实际存放成员的个数)
int oldCapacity = elementData.length;
// 由于capacityIncrement = 0 ,因此newCapacity = oldCapacity + oldCapacity
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 将数组容量扩容至原来的2倍,并保留原数据
elementData = Arrays.copyOf(elementData, newCapacity);
}
jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组,在扩容方面,默认扩容为原来数组长度的二倍。所以在一般情况下我们可以把Vector当做线程安全的ArrarList使用,每次扩容增大一倍容量,效率低于ArrayList。
七、List的常用方法
public class ListTest {
/**
* 总结:常用元素
* 增:add(Object obj)
* 删:remove(int index)/remove(Object obj)
* 改:set(int index,Object obj)
* 查:get(int index)
* 插:add(int index,Object obj)
* 长度:size()
* 遍历:1.Iterator迭代器方式
* 2.增强for循环
* 3.普通的循环
*/
@Test
public void test1(){
ArrayList list = new ArrayList();
list.add(123);
list.add(456);
list.add("AA");
list.add(false);
System.out.println(list);
//void add(int index,Object Obj):在index位置插入obj元素
list.add(1,"BB");
System.out.println(list);
//boolean addAll(int index,Collection objs):从index位置开始将objs中的所有元素添加到集合
List list1 = Arrays.asList(1,2,3);
list.addAll(list1);
System.out.println(list.size());
//Object get(int index):获取指定位置上的元素
System.out.println(list.get(0));
//int indexOf(Object obj):返回obj在集合中首次出现的位置
//int LastIndexOf(Object obj):返回obj在集合中末次出现的位置
//Object remove(int index):移除指定index位置的元素,并返回此元素
//Object set(int index,Object obj):设置指定index位置的元素为obj
//List subList(int fromIndex,int toIndex):返回从fromIndex到toIndex位置的左闭右开区间的集合
List list2 = list.subList(0,3);
System.out.println(list2);
}