博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java集合
阅读量:3960 次
发布时间:2019-05-24

本文共 12085 字,大约阅读时间需要 40 分钟。

目录

Java集合

(1)概述

集合是对多个数据进行存储操作的结构,简称Java容器;(内存层面的存储,不涉及到持久化的存储)

Java 集合框架主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值对映射。

img

Java集合可以分为Collection和Map两种体系;

快速失败与安全失败

在使用迭代器对集合对象进行遍历的时候,如果A线程对集合进行遍历,正好B线程对集合进行修改(增加、删除、修改)则A线程会抛出ConcurrentModificationException异常。

采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。

基于拷贝内容的优点是避免了Concurrent Modification Exception,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。

(2)Collection

img

Collection(接口):是一个顶层接口,主要用来定义集合的约定;

两个子接口

  • List:元素有序、可重复的集合;(动态数组)
  • Set:元素无序、不可重复的集合;
Queue是和List、Set并列的Collection的三大子接口。Queue的设计用来处理之前保存元素的访问次序,除了Collection基础操作以外,队列提供了额外的插入、读取、检查操作

Iterator

Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法,可用于迭代 和 等集合。

迭代器Iterator的基本操作是 next 、hasNext 和 remove。

调用 it.next() 会返回迭代器的一个元素,并且更新迭代器的状态。

调用 it.hasNext() 用于检测集合中是否还有元素。

调用 it.remove() 将迭代器返回的元素删除。(先调用next()再删除)

实例化方式

ArrayList arrayList=new ArrayList();Iterator iterator=arrayList.iterator();//通过集合的对象调用iterator()方法创建迭代器对象//每调用一次iterator()方法返回一个全新的迭代器对象

iterator()方法源码如下

public Iterator
iterator() {
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() {
}//构造器

Iterator实现遍历

@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循环实现遍历

语法: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); }}

List

实现类

ArrayList//List接口的主要实现类,线程不安全,效率高,底层使用Object[]数组存储数据LinkedList//底层使用双向链表存储数据,对于频繁的插入、删除操作效率比ArrayList高,线程不安全Vector//古老实现类,底层使用Object[]数组存储数据;线程安全,内部每个类都上锁,开销较大,访问元素效率远远低于ArrayList

ArrayList

ArrayList是实现了List接口的可扩容数组(动态数组),内部是基于数组实现的,支持泛型;

public class ArrayList
extends AbstractList
implements List
, RandomAccess, Cloneable, java.io.Serializable

ArrayList可以实现所有可选择的列表操作,允许存储所有元素包括空值;

ArrayList除了线程不安全,可以完全替代Vector;

ArrayList是线程不安全的容器,如果线程中有至少两个线程同时访问数据时,可能会导致线程安全问题,若要使用线程安全的List,应该使用java.util.Collections中通过静态方法synchronizedList(List list)创建的List对象

public static 
List
synchronizedList(List
list) {
return (list instanceof RandomAccess ? new SynchronizedRandomAccessList<>(list) : new SynchronizedList<>(list));}//实例化方式List list=Collections.synchronizedList(new ArrayList<>());

JDK7中

ArrayList实例化时

  • 若使用无参构造器,默认在底层创建长度为10的Objext[] elementData数组,当容量不够时,默认扩容为原来的1.5倍,同时将原有的数据复制到新的数组中;

若使用带参构造器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 元素
遍历 arraylist 中每一个元素并执行特定操作

LinkedList

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 接口,即可支持序列化,能通过序列化去传输。

LinkedList常用方法
方法 描述
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

Vector是List的古老实现类;

Vector和ArrayList相同,底层都是基于数组实现的的;

Vector是一个线程安全的容器,对内部每个方法都使用synchronized关键字来修饰,避免引发多线程的安全问题,但是开销较大,效率低;

Vector当容量不足时,默认扩容为原来的2倍

public Vector() {
//无参构造器默认创建长度为10的Object[] elementData数组 this(10);}
Stack(堆栈)

堆栈是先进先出的容器,Stack类继承了Vector类;

Stack中定义了通常使用的push()、pop()、peek()、search()、empty()方法;

public Stack() {
//只有一个空参构造器,创建对象时不包含任何元素;}
Deque

一个更完善、可靠性更强的栈操作应该由Deque接口和它的实现类来实现(优先使用)

//ArrayDeque是Deque的主要实现类public ArrayDeque() {
//空参构造器默认在底层创建长度为16的Object[] elements数组 elements = new Object[16];}

Set

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()方法比较),不可重复。

HashSet

//空参构造器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

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的是它维护着一条贯穿所有条目的双向链表,有着两个引用来来指向前后的数据,这条链表则定义了元素添加入集合的顺序;

TreeSet

继承于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;

(3)Map

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

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

LinkedHashMap是HashMap的子类,与HashMap不同的是它维护了一条双向链表,这个链表能够记录元素的添加顺序;

LinkedHashMap线程不安全,解决需要加锁或者使用Collections工具类获得一个线程安全的对象;

Collections.synchronizedMap(new LinkedHashMap<>());

TreeMap

底层使用红黑树实现;

TreeMap会根据key自然排序,或者通过Comparator进行定制排序;

线程不安全,解决需要加锁或者使用Collections工具类获得一个线程安全的对象;

HashTable

古老Map实现类,不存储null;

HashTable线程安全;

Properties

HashTable的子类;

(4)Collections

Collections是一个操作List、Set、Map等集合的工具类;

同步包装

同步包装将线程安全性添加到任意集合;

Collection、Set、List、Map、SortedSet、SortedMap都有对应的静态方法进行同步包装;

(5)题目

1.HashMap和HashTable的区别?

HashMap允许键和值是null,HashtTable不允许;

HashMap线程不安全,HashTable线程安全;

2.什么是迭代器?

Iterator提供了同意便利操作集合元素的接口,每个集合通过实现Iterable接口中iterator()方法返回Iterator的实例,然后进行对集合迭代操作;

需要注意:迭代时不能通过集合的方法删除元素,否则会抛出ConcurreModificationException异常,但是可以通过Iterator中的remove()方法进行删除;

转载地址:http://vrqzi.baihongyu.com/

你可能感兴趣的文章
举例说明常用字符串处理函数
查看>>
用Mindmanager整理的VB常用函数
查看>>
随风潜入夜,润物细无声
查看>>
软件生存期模型
查看>>
制定计划(问题的定义,可行性研究)
查看>>
需求分析
查看>>
软件设计
查看>>
程序编码
查看>>
软件测试
查看>>
软件维护
查看>>
软件项目管理
查看>>
面向过程的分析方法
查看>>
面向数据流的设计方法
查看>>
软件设计基础
查看>>
UML的基本结构
查看>>
UML中几种类间关系:继承、实现、依赖、关联、聚合、组合的联系与区别
查看>>
用例图(UseCase Diagram)—UML图(一)
查看>>
类图(Class diagram)—UML图(二)
查看>>
对象图(Object Diagram)—UML图(三)
查看>>
活动图(Activity Diagram)—UML图(四)
查看>>