文章目录
一、集合框架类关系分析
下图转载自:
https://blog.csdn.net/weixin_43314519/article/details/107473547
查阅过jdk1.8的源码确认过,上图中应有三处错误:
1.LinkedHashSet和HashSet之间的关系是继承,它们之间的线应该是实线
2.SortedSet和SortedMap都是接口,而且TreeSet和TreeMap不直接实现它们,SortedSet和Treeset、SortedMap和TreeMap之间没有任何直接关系。正确的关系应该是如下图所示:
下面三张图来自重庆大学的《java程序设计》
1、Collection接口
1)collection接口
被一个抽象类
AbstractCollectionList和三个接口
Set、Queue、List所实现。
依赖于Iterator,以便得到遍历器进行集合的遍历
Iterator<E> iterator();
被Map所依赖,Map中的value可以被转化成Collection,使得整个集合框架更加的统一。
Collection<V> values();
2)AbstractCollection抽象类
实现Collection接口,被AbstractList和AbstractSet所继承。
3)SortedSet接口
实现了Set接口,被接口NavigableSet接口继承,NavigableSet接口被具体类TreeSet所实现。
4)AbstractList和AbstractSet
这两个抽象类是大部分List和Set具体类的父类,它们继承了AbstractCollection的同时,为了具有相应的特性(Set、List),也实现了Set和List接口。
5)继承基类List的类
Vector、ArrayList、AbstractSequentialList
6)继承基类Set的类
HashSet、TreeSet
2、Map接口
和Collection的框架结构大致统一,所以就不复述了。
二、集合的意义和使用
1、意义
用来存储和操作对象组。
集合PK数组
1、集合可以扩容,数组不可。
2、数组既可以存储对象又可以存储基本类型,集合只可以存储对象。
3、集合可以保存有映射关系的数据,数组不可。
4.集合和数组的toString()有所差异,以Integer[]和ArrayList为例。
2、使用
集合框架中有非常多的接口、抽象类、具体类,我们如何去挑选一个具体类或者说我们如何自己建立一个自己的类去实现或者继承接口、抽象类呢?这时,我们主要是从下面几个方向去思考这个问题:
1)线程安全PK性能高效
Collection中所提供的具体实现类基本都是线程不安全的。
线程安全的实现类只有:Vector、HashTable。
为什么大部分类都要被设计成线程不安全的呢?
因为线程安全的代价是非常昂贵的,性能会远不如线程不安全!
线程不安全的集合类如何转化成线程安全的呢?
jdk1.8中有个专门的类来解决这个问题:
java.util.Collections
,它内部的方法可以将各种线程不安全的集合类转化成线程安全的。如:
Collection c=Collections.synchronizedCollection(c);
我们只需要在多线程的时候选择线程安全的集合类就可以了,在其他情况下,为了性能高效我们应该选择线程不安全的实现类。
2)链表PK数组
1、链表的增添和删除的时间复杂度是O(1),查询的时间复杂度是O(n)
2、数组的增添(append)的时间复杂度是O(1),(insert)的时间复杂度是o(n),删除是O(n),查询是O(1)。
所以我们在查询次数多于增删时,选择数组,反之,选择链表。
注:LinkedList实现了deque接口(如下图,该接口中有着一系列的首位操作抽象方法),并且其自身有着First、last两个指针,所以可以很方便的对集合的首尾进行查询、增添、删除操作。如
addFirst()
、removeFirst()
这是LinkedHashSet和LinkedHashMap没有的功能(如果让它们实现该接口它们也能拥有这个功能).当然,我们在自定义集合类时,如果经常进行删除和增添操作,那么我们肯定得选择链表,如果经常对链表的首尾进行操作,我们就可以实现java.util.deque
接口
3)重复pk单一
set集合中,元素都是不重复的,然而在List中元素是可以重复的。Map<Key,Value>中,Key也是不可重复的。
4)有序pk无序
注意:这里所说的有序和无序是指其遍历的顺序是否与进入集合的顺序一致。
Set为无序,List为有序。如下图
HashSet、LinkedHashSet、TreeSet之间的比较
HashSet是无序的,TreeSet中为自然顺序(从小到大),LinkedHashSet中FIFO(先进先出)
ArrayList、LinkedList、Vector都是先进先出的有序集合(与自然顺序无关,除非用了比较器Comparator)
补充:如何让集合中的数据按照你所期待的方式排序呢?
对某个集合类使用Comparator比较器,或让加入的类实现Comparable接口即可。
说明:o1-o2>0
或者this-o>0
return 1为从小到大,return -1为从大到小
注:用比较器实现排序:
1.集合工具类Collections中Collections.sort()方法`
注意:只有List才可以被sort
2.集合类本身的sort方法。
(有些集合类不能被排序,例如hashset)
三、集合与数组的相互转化
1、数组转化成集合
使用Arrays.asList()
思考:为什么没有asMap()和asSet()
数组不存在映射关系,所以没有asMap(),数组中的数是可以重复的,所以没有asSet()
下面是代码演示:从源码 public static <T> List<T> asList(T... a)
我们发现,aslist的参数为泛型的变长参数,我们都知道,基本数据类型是不可泛型化的,那么为什么int数组也可以被加入其中呢?因为是以int []的形式加入的。
注:如果不太熟悉泛型变长参数可以参考https://blog.csdn.net/m0_51801058/article/details/114239230?spm=1001.2014.3001.5501
该方法源码如下:
易错
值得注意的是,该方法中返回的ArrayList并不是java.util.ArrayList,而是Arrays中的内部类!!
思考:为什么不直接返回java.util.ArrayList呢?
如果直接返回java.util.ArrayList,那么就必须得在该类中增加相应的构造函数(如下源代码),还有一些其他相应的方法,显然不符合开闭原则:
一个软件实体如类,模块和函数应该对修改关闭,对扩展开放
ArrayList(E[] array) {
a = Objects.requireNonNull(array);
}
经过了上面的介绍,当我们遇见下面的异常时就能很快反应出原因了:
这是因为在Arrays中的ArrayList中并没有覆盖其父类AbstractList
的如下add()方法:
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
那么,我们如何正确add呢,显然,我们需要把它转化成java.util下的List实现类,在这里以LinkedList为例:
2、集合转换成数组
Collection.toArray()
集合转化成数组显然会考虑到数组长度的问题,尝试了原数组长度大于、小于、等于的情况,发现无论哪种情况原数组的长度最终总会与Collection中对象的个数相等。这是jvm保证的,数组之间的赋值是会直接传递引用。如下图程序正是说明这一点:d赋值给c,最终c、d都指向同一地址,c==d=true
,e即使与d的值相等,但是它们两个所指向的内存空间不同,所以e==d=fals
。