Java类集框架详细汇总-LMLPHP

前言:

Java的类集框架比较多,也十分重要,在这里给出图解,可以理解为相应的继承关系,也可以当作重要知识点回顾;

Java类集框架详细汇总-LMLPHP

Collection集合接口

继承自:Iterable

java.util.Collection是单值集合操作的最大父接口,其中有几个核心操作方法以及常用操作方法;

boolean add(E e) 确保此集合包含指定的元素(可选操作)。
boolean addAll(Collection<? extends E> c) 将指定集合中的所有元素添加到此集合(可选操作)。
void clear() 从集合中删除所有元素(可选操作)。
boolean contains(Object o) 如果该集合包含指定的元素,则返回true。
boolean remove(Object o) 如果存在,则从此集合中删除指定元素的单个实例(可选操作)。
int size() 返回此集合中的元素数。
Object[] toArray() 返回一个包含此集合中所有元素的数组。
Iterator<E> iterator() 返回对此集合中的元素进行迭代的迭代器。

上面方法中有两个特殊的方法就是cotainsremove;都需要equals方法的支持才能删除与查询数据;否则找不到元素。

后面都是衍生出的子类方法。

List集合

最大特点:允许保存重复的元素,并在其父接口上扩充了其他的方法;

继承关系:

void add(int index, E element) Inserts the specified element at the specified position in this list (optional operation).
boolean add(E e) Appends the specified element to the end of this list (optional operation).
ListIterator<E> listIterator() Returns a list iterator over the elements in this list (in proper sequence).
static <E> List<E> of() Returns an unmodifiable list containing zero elements.
default void forEach(Consumer<? super T> action) Performs the given action for each element of the Iterable until all elements have been processed or the action throws an exception.

实例:

ArrayList子类

继承结构如下:

实例化:重复元素允许保存并且按照添加时的顺序保存;

集合操作方法:

ArrayList原理分析:重点

首先需要明确ArrayList是通过数组实现的;这样就出现了ArrayList通过什么样的方式进行的扩容操作,以及在什么情况下才会扩容?

ArrayList类中的数组是在构造方法中进行的空间开辟的;其对应的有无参和有参构造方法:

无参构造方法:使用空数组(长度为0)初始化,在第一次使用时会为其开辟空间为(初始化程度为10);

有参构造方法:长度大于0则以指定长度开辟数组空间;如果长度为0,则按照无参构造方法进行;如果负数则抛出ILLegaLArgumentException异常;

当数组扩充后利用数组复制的形式,将旧数组中的数据复制到开辟的新数组中;

其最大程度为:

ArrayList保存自定义类对象:

该操作必然包含了相关的增删改查;由于contains与remove方法的实现都需要通过对象比较俩完成;所以我们需要覆写equals方法

LinkedList子类:

继承结构如下:基于链表形式的实现

实现LinkedList的集合操作:

Vector子类:

继承结构如下:

从继承结构上可以看出,Vector子类的使用方式与ArrayList的使用方式相同;

不同点:

以下是vector操作方法,采用的方式是synchronized 同步处理;属于线程安全,但是效率没有ArrayList高;

在考虑线程并发访问的情况下才能去使用vector子类。

Set集合

主要特点是:内部不允许保存重复元素

继承结构如下:

实例化:

HashSet子类:

特点:散列存放且不允许保存重复元素 即:无序存放

继承结构如下:

HashSet保存数据:

LinkedHashSet子类:JDK1.4加入---解决HashSet无法顺序保存的数据

实现是基于链表保存的数据:增加的顺序就是集合的保存顺序,且不会保存重复的数据。

TreeSet子类:

特点:使得集合中保存的数据进行有序排列

其继承结构如下:

TreeSet子类继承AbstractSet抽象类并实现了NavigableSet接口(该接口为排序标准接口,是Set的子类)

TreeSet保存数据:

优先级:数字排序>字母排序>汉字排序

TreeSet子类排序分析:

该类子在进行有序数据存储时依据的是Comparable接口实现排序;需要注意的是在覆写compareTo()方法时需要进行类中全部属性的比较;否则出现部分属性相同时被误判为同一个对象;导致重复元素判断失败;

关于compareTo的相关描述,可以到源码下的注释中翻译了解。

重复元素消除:(非排序的集合中的重复元素)

依靠两种方法:

  1. Hash码:public int Hashcode();

  2. 对象比较:public boolean equals(Object obj);

在进行对象比较的过程中,首先会使用hashCode()方法与集合中已经保存的代码进行匹配比较;如果代码相同则再使用equals()方法进行属性的依次比较;如果全部相同;则为相同元素;

通过hashSet 保存了重复元素,再两个方法的作用实现去重操作。

集合输出

在类框架中的对于集合的标准输出为:IteratorListIteratorEnumerationforeach;

Iterator迭代输出:

迭代输出:依次判断每个元素,判断其是否有内容,如果有内容就输出。

Iterator接口依靠Iterable接口中的iterate()方法实例化的;

Iterator常用方法:

E next() 返回迭代中的下一个元素。
default void remove() 从基础集合中移除该迭代器返回的最后一个元素(可选操作)。
boolean hasNext() 如果迭代有更多元素则返回true。

实例:

关于数据删除的问题:

在Collection中与Iterator都有remove方法;那么应该选择什么呢:

Collection不管三七二十一,就给你删除了,这样会造成Java.util.ConcurrentModificationException错误;

而Iterator在迭代的时候;都会需要依据存储的数据内容进行判断;

所以只有Iterator接口中的remove才是实现删除数据的正确方法。

如:

ListIterator双向迭代:

首先区别Iterator的作用:

Iterator完成的是从前向后单向输出

ListIterator完成的是从后向前输出

但是只有实现Iterator从前向后的输出后才能实现ListIterator从后向前的输出(注意先后顺序);

因为只有实现从前向后输出结束后,指针才执行最后;

如果顺序相反则会输出为空。

其下的扩充方法

boolean hasPrevious() 如果该列表迭代器在反向遍历列表时拥有更多元素,则返回true。
E previous() 返回列表中的前一个元素,并向后移动光标位置。

实例:执行双向迭代

ListIterator接口实现了List集合的双向迭代操作。

Enumeration枚举输出:

该类型的输出是建立在Vector集合上的;相当于依附产品;

其接口常用方法:

boolean hasMoreElements() 测试此枚举是否包含更多元素。
E nextElement() 如果该枚举对象至少还有一个要提供的元素,则返回该枚举的下一个元素。

实例:输出vector集合数据

注意Enumeration只有输出操作没有删除操作。

foreach输出:

没啥好说的,既可以实现数组输出外,也支持集合的输出;

实现自定义foreach输出:

首先需要知道实现foreach需要Iterator接口的支持;所以在Set与List集合才可以通过foreach实现输出;

如果我们自己要实现自定义类的输出,那么我们就需要实例化Iterable接口完成iterator的功能;

Map集合

map的集合形式是键值对的方式;

其常用方法:

V get(Object key) Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.
V put(K key, V value) Associates the specified value with the specified key in this map (optional operation).
static <K,V> Map<K,V> of() Returns an unmodifiable map containing zero mappings.

其中Map.of()可以将每一组数据转为map进行保存;

使用Map保存Key-Value数据:

注意点:

  1. 如果Key重复 则会报错:java.lang.IllegalArgumentException

  2. 如果Value与Key设置为null,则会报错:java.lang.NullPointerException

HashMap子类:

特点:采用散列方式保存数据,即无序排列

继承结构:

HashMap进行map集合的操作:

注意两个输出的注释:

在使用Map保存数据的时候Key与Value都不能使用null,但是使用HashMap进行保存数据可以将Key或Value设置为null,当然也可以Key=Value=null,但是这样的实现保存毫无意义。

put方法在发生覆盖钱都可以返回原始内容,这样就可以依据返回结果来判断所设置的key是否存在;

HashMap数据扩充操作原理分析:

1) 首先观察构造方法:

设置数据扩充阈值;

然后跳转DEFAULT_LOAD_FACTOR查看:

作用:容量的扩充阈值

通过源码可以发现,每个Hashmap在对象实例化的时候都已经考虑到了数据存储的扩充问题;

2) 观察HashMap中的put方法

在使用put方法进行数据保存的时候会调用putVal方法,同时会将key进行哈希处理(生成hash码)

而putVal()方法为了方便数据保存会将数据封装为一个Node节点类对象,而在使用putVal()方法的操作过程会调用reasize()方法进行扩容;

3)容量扩充

当保存的数据超过既定的存储容量则会进行扩容,原则如下:

常量地址:DEFAULT_INITIAL_CAPACITY;作为初始化的容量配置,而后1向左移4为-》16;

常量的默认大小为16个元素,也就是说默认可以保存的最大的内容是16;

当保存的数据内容超过了设置的阈值DEFAULT_LOAD_FACTOR = 0.75f

相当于容量x阈值 = 16*0.75 = 12;即保存到12个元素的时候就会进行容量扩充;

扩充的模式是2倍扩充:即每一次扩充2倍的容量。

4)大数据下的数据存储方式:

在JDK1.8之后来到大数据时代,这就触发了HashMap在大数据量中的访问效率问题;

其中提供了一个重要的常量:TREEIFY_THRESHOLD

在使用HashMap进行保存的时候,如果保存的数据个数没有超过阈值8,那么会按照链表的形式进行数据的存储;而超过了这个阈值,则会将链表转为红黑树以实现树的平衡;并且利用左旋与右旋保证数据的查询性能。

LinkedHashMap子类:

特点:基于链表形式实现偶对的存储,可以保证存储顺序与数据增加的顺序相同;

继承结构:

使用LinkedHashMao子类存储数据:

运行可以发现集合的保存的顺序与数据增加顺序相同;同时LinkedHashMap子类允许保存的Key或value内容为null;

Hashtable子类:

其继承结构如下:

使用Hashtable子类保存数据:

HashMap与Hashtable的区别:

HashMap中的 方法都是异步操作(非线程安全),HashMap中允许保存null数据

Hashtable中的方法都是同步操作(线程安全),但是效率慢,Hashtable不允许保存Null数据;否则会出现NUllpointException;

TreeMap子类:

特点:TreeMap属于有序的Map集合类型;它可以按照key进行排序;所以需要Comaprable接口配合;

继承结构如下:

TreeMap子类进行数据Key的排序:

Map.Entry内部接口:

在JDK1.9开始可以利用Map接口中创建Map.entry内部接口实例;

在程序继续宁Map.Entry对象构建的时,只传入Key和Value就会自动利用KeyValueHolder子类实例化Map.Entry接口对象。

Iterator输出Map集合:

集合数据输出的标准形式是基于Iterator接口完成的;Collection接口直接提供iterator方法可以获得iterator接口实例;但由于Map接口中保存的数据是多个Map.Entry接口封装的二元偶对象,所以就必须采用Map集合的迭代输出;

  1. 使得Map接口中的entrySet(),将Map集合变为Set集合;

  2. 取得Set接口实例后就可以利用iterator方法取得iterator的实例化对象;

  3. 使用iterator迭代找到每一个Map.Entry对象,并进行Key与Value的分。

Iterator和foreach输出Map集合:

自定义Key类型:

采用自定义类的形式实现,但是作为Key类型的类由于存在数据查找的需求,所以必须在类中覆写hashcode()与equals()方法。

在存储大量的数据中,key还是可能出现重复的问题,这个问题叫Hash冲突;

解决的方式:

链地址法(拉链法)、开放寻址、再哈希、建立公共溢出区;

喜欢,在看


本文分享自微信公众号 - JAVA乐园(happyhuangjinjin88)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

04-13 00:37