本文共 12085 字,大约阅读时间需要 40 分钟。
集合是对多个数据进行存储操作的结构,简称Java容器;(内存层面的存储,不涉及到持久化的存储)
Java 集合框架主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值对映射。
Java集合可以分为Collection和Map两种体系;
快速失败与安全失败
在使用迭代器对集合对象进行遍历的时候,如果A线程对集合进行遍历,正好B线程对集合进行修改(增加、删除、修改)则A线程会抛出ConcurrentModificationException异常。
采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。
基于拷贝内容的优点是避免了Concurrent Modification Exception,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。
Collection(接口):是一个顶层接口,主要用来定义集合的约定;
两个子接口
Queue是和List、Set并列的Collection的三大子接口。Queue的设计用来处理之前保存元素的访问次序,除了Collection基础操作以外,队列提供了额外的插入、读取、检查操作
Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法,可用于迭代 和 等集合。
迭代器Iterator的基本操作是 next 、hasNext 和 remove。
调用 it.next() 会返回迭代器的一个元素,并且更新迭代器的状态。
调用 it.hasNext() 用于检测集合中是否还有元素。
调用 it.remove() 将迭代器返回的元素删除。(先调用next()再删除)
实例化方式
ArrayList arrayList=new ArrayList();Iterator iterator=arrayList.iterator();//通过集合的对象调用iterator()方法创建迭代器对象//每调用一次iterator()方法返回一个全新的迭代器对象
iterator()方法源码如下
public Iteratoriterator() { return new Itr();//返回一个新建的Itr类对象}//Itr类是集合中的一个内部类private class Itr implements Iterator { //继承了Iterator类 int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; Itr() { }//构造器
@Testpublic void test(){ ArrayList arrayList = new ArrayList(); arrayList.add(1); arrayList.add(2); arrayList.add(3); arrayList.add(4); arrayList.add(5); Iterator iterator=arrayList.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); }}
语法:for(集合中元素的类型 局部变量名:要遍历的集合的对象实例){
}
@Testpublic void test(){ ArrayList arrayList = new ArrayList(); arrayList.add(1); arrayList.add(2); arrayList.add(3); arrayList.add(4); arrayList.add(5); for(Object num:arrayList){ System.out.println(num); }}
实现类
ArrayList//List接口的主要实现类,线程不安全,效率高,底层使用Object[]数组存储数据LinkedList//底层使用双向链表存储数据,对于频繁的插入、删除操作效率比ArrayList高,线程不安全Vector//古老实现类,底层使用Object[]数组存储数据;线程安全,内部每个类都上锁,开销较大,访问元素效率远远低于ArrayList
ArrayList是实现了List接口的可扩容数组(动态数组),内部是基于数组实现的,支持泛型;
public class ArrayListextends AbstractList implements List , RandomAccess, Cloneable, java.io.Serializable
ArrayList可以实现所有可选择的列表操作,允许存储所有元素包括空值;
ArrayList除了线程不安全,可以完全替代Vector;
ArrayList是线程不安全的容器,如果线程中有至少两个线程同时访问数据时,可能会导致线程安全问题,若要使用线程安全的List,应该使用java.util.Collections中通过静态方法synchronizedList(List list)创建的List对象
public staticList synchronizedList(List list) { return (list instanceof RandomAccess ? new SynchronizedRandomAccessList<>(list) : new SynchronizedList<>(list));}//实例化方式List list=Collections.synchronizedList(new ArrayList<>());
JDK7中
ArrayList实例化时
若使用带参构造器public ArrayList(int initialCapacity),可以创建自定义容量的数组
public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); }}
JDK8中
使用无参构造器创建对象时,并没有创建长度为10的数组,而是创建一个空的Objext[] elementData数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = { };public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}
当第一次调用add()方法添加数据时,才在底层中创建长度为10的Objext[] elementData数组
源码分析如下
private int size;//size默认值为0private static final int DEFAULT_CAPACITY = 10;//默认底层数组的容量为10//add()方法public boolean add(E e) { ensureCapacityInternal(size + 1);//先调用了ensureCapacityInternal方法 elementData[size++] = e;//将数据存储到集合中 return true;}//ensureCapacityInternal()方法private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));//递归调用了calculateCapacity方法}//calculateCapacity()方法private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //如果此时的数组为默认创建的长度为0的数组,则返回DEFAULT_CAPACITY和minCapacity中的较大值 return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity;}/***/
方法 | 描述 |
---|---|
将元素插入到指定位置的 arraylist 中 | |
添加集合中的所有元素到 arraylist 中 | |
删除 arraylist 中的所有元素 | |
复制一份 arraylist | |
判断元素是否在 arraylist | |
通过索引值获取 arraylist 中的元素 | |
返回 arraylist 中元素的索引值 | |
删除存在于指定集合中的 arraylist 里的所有元素 | |
删除 arraylist 里的单个元素 | |
返回 arraylist 里元素数量 | |
判断 arraylist 是否为空 | |
截取部分 arraylist 的元素 | |
替换 arraylist 中指定索引的元素 | |
对 arraylist 元素进行排序 | |
将 arraylist 转换为数组 | |
将 arraylist 转换为字符串 | |
() | 设置指定容量大小的 arraylist |
返回指定元素在 arraylist 中最后一次出现的位置 | |
保留 arraylist 中在指定集合中也存在的那些元素 | |
查看 arraylist 是否包含指定集合中的所有元素 | |
将 arraylist 中的容量调整为数组中的元素个数 | |
删除 arraylist 中指定索引之间存在的元素 | |
将给定的操作内容替换掉数组中每一个元素 | |
删除所有满足特定条件的 arraylist 元素 | |
遍历 arraylist 中每一个元素并执行特定操作 |
LinkedList底层是一个双向链表,所有操作都可以视为双向性的,允许存储任何元素(包括Null);
LinkedList线程不安全,如果要解决线程安全问题,需要外部加锁或者使用java.util.Collections中通过静态方法synchronizedList(List list)创建的List对象;
List list=Collections.synchronizedList(new LinkedList());
以下情况使用 LinkedList :
LinkedList 继承了 AbstractSequentialList 类。
LinkedList 实现了 Queue 接口,可作为队列使用。
LinkedList 实现了 List 接口,可进行列表的相关操作。
LinkedList 实现了 Deque 接口,可作为队列使用。
LinkedList 实现了 Cloneable 接口,可实现克隆。
LinkedList 实现了 java.io.Serializable 接口,即可支持序列化,能通过序列化去传输。
方法 | 描述 |
---|---|
public boolean add(E e) | 链表末尾添加元素,返回是否成功,成功为 true,失败为 false。 |
public void add(int index, E element) | 向指定位置插入元素。 |
public boolean addAll(Collection c) | 将一个集合的所有元素添加到链表后面,返回是否成功,成功为 true,失败为 false。 |
public boolean addAll(int index, Collection c) | 将一个集合的所有元素添加到链表的指定位置后面,返回是否成功,成功为 true,失败为 false。 |
public void addFirst(E e) | 元素添加到头部。 |
public void addLast(E e) | 元素添加到尾部。 |
public boolean offer(E e) | 向链表末尾添加元素,返回是否成功,成功为 true,失败为 false。 |
public boolean offerFirst(E e) | 头部插入元素,返回是否成功,成功为 true,失败为 false。 |
public boolean offerLast(E e) | 尾部插入元素,返回是否成功,成功为 true,失败为 false。 |
public void clear() | 清空链表。 |
public E removeFirst() | 删除并返回第一个元素。 |
public E removeLast() | 删除并返回最后一个元素。 |
public boolean remove(Object o) | 删除某一元素,返回是否成功,成功为 true,失败为 false。 |
public E remove(int index) | 删除指定位置的元素。 |
public E poll() | 删除并返回第一个元素。 |
public E remove() | 删除并返回第一个元素。 |
public boolean contains(Object o) | 判断是否含有某一元素。 |
public E get(int index) | 返回指定位置的元素。 |
public E getFirst() | 返回第一个元素。 |
public E getLast() | 返回最后一个元素。 |
public int indexOf(Object o) | 查找指定元素从前往后第一次出现的索引。 |
public int lastIndexOf(Object o) | 查找指定元素最后一次出现的索引。 |
public E peek() | 返回第一个元素。 |
public E element() | 返回第一个元素。 |
public E peekFirst() | 返回头部元素。 |
public E peekLast() | 返回尾部元素。 |
public E set(int index, E element) | 设置指定位置的元素。 |
public Object clone() | 克隆该列表。 |
public Iterator descendingIterator() | 返回倒序迭代器。 |
public int size() | 返回链表元素个数。 |
public ListIterator listIterator(int index) | 返回从指定位置开始到末尾的迭代器。 |
public Object[] toArray() | 返回一个由链表元素组成的数组。 |
public T[] toArray(T[] a) | 返回一个由链表元素转换类型而成的数组。 |
Vector是List的古老实现类;
Vector和ArrayList相同,底层都是基于数组实现的的;
Vector是一个线程安全的容器,对内部每个方法都使用synchronized关键字来修饰,避免引发多线程的安全问题,但是开销较大,效率低;
Vector当容量不足时,默认扩容为原来的2倍
public Vector() { //无参构造器默认创建长度为10的Object[] elementData数组 this(10);}
堆栈是先进先出的容器,Stack类继承了Vector类;
Stack中定义了通常使用的push()、pop()、peek()、search()、empty()方法;
public Stack() { //只有一个空参构造器,创建对象时不包含任何元素;}
一个更完善、可靠性更强的栈操作应该由Deque接口和它的实现类来实现(优先使用)
//ArrayDeque是Deque的主要实现类public ArrayDeque() { //空参构造器默认在底层创建长度为16的Object[] elements数组 elements = new Object[16];}
Set集合中存储的数据无序、不可重复;
实现类
HashSet//作为Set集合的主要实现类,线程不安全,可以存储null; LinkedHashSet//继承HashSet,是HashSet的子类;TreeSet//
Set特性
无序性:不等于随机性;存储数据在底层由数据的哈希值决定;
不可重复性:保证集合中不会有相同的元素,当添加的数据重复时,add()方法返回值为null;
hashSet.add(6);System.out.println(hashSet.add(6));
添加元素的过程(以HashSet为例)
当向HashSet集合中添加元素a时,先调用a的hashCode()方法,计算a的哈希值,由哈希值决定a在集合中存储的位置,并与其他元素比较(先比较哈希值,若相同再equals()方法比较),不可重复。
//空参构造器public HashSet() { //底层使用HashMap作为容器,以哈希值和数据作为一对键值对 map = new HashMap<>();}//add()方法public boolean add(E e) { return map.put(e, PRESENT)==null;}
HashSet是线程不安全的容器,当多个线程访问数据时需要加锁或者通过Collections工具类的方法来获得线程安全的对象;
HashSet hashSet=new HashSet();Set set = Collections.synchronizedSet(hashSet);
LinkedHashSet继承于HashSet;
LinkedHashSet线程不安全,当要解决线程安全问题的时候需要加锁或者通过Collections工具类来获得线程安全的对象;
public LinkedHashSet() { //空参构造器,直接调用父类构造器 super(16, .75f, true);}//调用的父类中的构造器HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<>(initialCapacity, loadFactor);//构造器中调用LinkedHashMap}//LinkedHashMap的构造器public LinkedHashMap(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor);//依旧调用父类HashMap中的构造器 accessOrder = false;}//HashMap的构造器public HashMap(int initialCapacity, float loadFactor) {
LinkedHashSet不同于HashSet的是它维护着一条贯穿所有条目的双向链表,有着两个引用来来指向前后的数据,这条链表则定义了元素添加入集合的顺序;
继承于AbstractSet类,实现NavigableSet接口;
TreeSet的线程不安全,解决线程安全可以通过加锁或者通过Collections工具类的方法来获得线程安全的对象;
TreeSet treeSet= Collections.synchronizedSortedMap(new TreeSet<>(...))
TreeSet的底层实现
//空参构造器public TreeSet() { this(new TreeMap());//创建一个TreeMap的对象}//TreeSet实际由TreeMap实现,底层是红黑树存储结构;
TreeSet可以根据key来自然排序,当添加数据时,要求是同类的对象,自然排序比较两个对象是否相同的标准是compareTo()方法返回0,而不是equals();
TreeSet也可以通过Comparator接口进行定制排序,比较两个对象是否相同的标准为compareTo()返回0;
Map 接口存储一组键值对象,提供key(键)到value(值)的映射。
实现类
HashMap//主要实现类,线程不安全,效率高,可以存储null; LinkedHashMap//HashMap的子类 TreeMap//一个基于NavigableMap接口的红黑树HashTable//古老实现类,线程安全,不存储null; Properties//HashTable的子类,常用来处理配置文件,key和value都是String类型;
Map结构
Map中的key无序、不可重复,使用set存储(key所在类会重写equals()和hashCode());
Map中value无序、可重复,使用Collection存储(value所在类会重写equals());
一个键值对key-value会构成一个entry对象;
Map中entry无序、不可重复,使用Set存储;
HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。
HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度;
HashMap允许空的key-value键值对,最多允许一条记录的键为 null;
HashMap 是无序的,即不会记录插入的顺序。
线程不安全,可以使用Collections工具类来获得一个线程安全的对象
HashMap hashMap = new HashMap();Map map= Collections.synchronizedMap(hashMap);
JDK7
HashMap hashMap = new HashMap();//实例化以后底层创建的是长度为16的一维数组Entry[] table;/*当调用put(key,value)添加数据时,先调用key所在类的hashCode()方法计算哈希值,根据哈希值决定此entry再数组中存放的位置,如果该位置上为空,则添加成功;如果不为空,比较key与已存在数据的哈希值,若不同则添加成功,如果相同继续调用key的equals()方法与已有的数据比较,比较返回false则添加成功,返回true则用新添加的value替换原有的数据**///当存储空间不足时,默认扩容至原来的2倍,再将原有的数据复制过来;//JDK7及之前HashMap底层结构是数组+链表
JDK8
HashMap hashMap = new HashMap();//实例化时底层没有创建长度为16的一维数组Entry[] table/*1.JDK8中底层数组是Node[]而不是Entry[];2.实例化时并不创建数组,当第一次调用put()方法时,底层才创建长度为16的数组;3.JDK8中HashMap的底层结构是数组+链表+红黑树4.当数组的某一个索引位置(key)上元素以链表的形式存放的数据个数大于8并且当前数组的长度大于64时,此时索引上的所有数据转换为使用红黑树存储;**/
HashMap常用方法
方法 | 描述 |
---|---|
删除 hashMap 中的所有键/值对 | |
复制一份 hashMap | |
判断 hashMap 是否为空 | |
计算 hashMap 中键/值对的数量 | |
将键/值对添加到 hashMap 中 | |
将所有键/值对添加到 hashMap 中 | |
如果 hashMap 中不存在指定的键,则将指定的键/值对插入到 hashMap 中。 | |
删除 hashMap 中指定键 key 的映射关系 | |
检查 hashMap 中是否存在指定的 key 对应的映射关系。 | |
检查 hashMap 中是否存在指定的 value 对应的映射关系。 | |
替换 hashMap 中是指定的 key 对应的 value。 | |
将 hashMap 中的所有映射关系替换成给定的函数所执行的结果。 | |
获取指定 key 对应对 value | |
获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值 | |
对 hashMap 中的每个映射执行指定的操作。 | |
返回 hashMap 中所有映射项的集合集合视图。 | |
() | 返回 hashMap 中所有 key 组成的集合视图。 |
返回 hashMap 中存在的所有 value 值。 | |
添加键值对到 hashMap 中 | |
对 hashMap 中指定 key 的值进行重新计算 | |
对 hashMap 中指定 key 的值进行重新计算,如果不存在这个 key,则添加到 hasMap 中 | |
对 hashMap 中指定 key 的值进行重新计算,前提是该 key 存在于 hashMap 中。 |
LinkedHashMap是HashMap的子类,与HashMap不同的是它维护了一条双向链表,这个链表能够记录元素的添加顺序;
LinkedHashMap线程不安全,解决需要加锁或者使用Collections工具类获得一个线程安全的对象;
Collections.synchronizedMap(new LinkedHashMap<>());
底层使用红黑树实现;
TreeMap会根据key自然排序,或者通过Comparator进行定制排序;
线程不安全,解决需要加锁或者使用Collections工具类获得一个线程安全的对象;
古老Map实现类,不存储null;
HashTable线程安全;
HashTable的子类;
Collections是一个操作List、Set、Map等集合的工具类;
同步包装将线程安全性添加到任意集合;
Collection、Set、List、Map、SortedSet、SortedMap都有对应的静态方法进行同步包装;
1.HashMap和HashTable的区别?
HashMap允许键和值是null,HashtTable不允许;
HashMap线程不安全,HashTable线程安全;
2.什么是迭代器?
Iterator提供了同意便利操作集合元素的接口,每个集合通过实现Iterable接口中iterator()方法返回Iterator的实例,然后进行对集合迭代操作;
需要注意:迭代时不能通过集合的方法删除元素,否则会抛出ConcurreModificationException异常,但是可以通过Iterator中的remove()方法进行删除;
转载地址:http://vrqzi.baihongyu.com/