Collection体系的常用类及其背后的数据结构

Collection体系是Java常用的一种体系,里面包含着两种常用的数据结构List和Set,本篇文章将着重对这两种数据结构进行一个详细的介绍。

List

List的中文翻译是有序的集合,它是一种有序的数据结构,何为有序,即以何种顺序输入到这个集合里面,则以何种顺序输出出来。

而生命一个List集合也很简单,如下所示:

List list = new ArrayList<>();

List常用的方法:

new: new ArrayList(collection), new ArrayList()
R: size()/ isEmpty()/ contains()/ for()/ stream()
C/U: add()/ addAll()/ retainAll()
D: clear()/ remove()/ removeAll()

这里着重说一下retainAll这个方法,a.retainAll(b),这个方法的作用是传入一个集合b,然后只保留a集合和b集合中的交集元素。是否为交集则依赖于a元素的equals方法。

List的动态扩容

观看了List的源代码之后便可发现,List集合的本质上是一个定容数组,那当List集合中的元素数量超过了这个定容数组的可容纳量之后,List集合就需要对这个数组进行动态扩容。

具体做法很简单:通过新创建一个数组,设置其初始容量为原始容量的1.5倍之后,把原数组的内容复制过去即可。

Set

Set集合是一个不允许重复的集合,判断其中元素是否重复也同样依赖于元素本身的equals方法。

Set可以是有序的,也可以是无序,具体要看实现该数据结构的类的创建方法。

用List实现一个Set的add方法:

class MySet {
  List element;
  void add(Object object) {
    if (!element.contains(object)) {
      element.add(object);
    }
  }
}
 
 

但用List实现Set有一个很大的缺点,就是它的性能很差,很低效,原因就是在于它是有序的,每次判断是否重复时,需要对整个集合进行一次遍历,从而造成较高的时间复杂度。

改进方法:

使用hashCode,把object映射成一个int(这个int可能不是唯一的)
使用hashCode之后,一个元素会获得一个code,不同的code会放在不同的Hash桶内,不同的元素也有可能获得相同的code,这都要根据hashCode方法的具体实现方式来决定。

原理解释:

假如有100万个元素,把它们放在10万个Hash桶里,则每个桶里只含有十个元素,则每次新添加一个元素的时候,只要先获取它对应的code值,找到对应的Hash桶,再与桶里的十个元素进行比较即可,相比起跟100万个元素进行比较,这种方法节省了十万倍的时间。

不过这种方式也存在着它的缺点,那就是比较损耗内存空间,毕竟计算机就是这样,不是空间换时间,就是时间换空间,所以这也是无法避免的。

Java世界里的第二重要约定

有关hashCode,这里还有一个Java世界里的第二重要约定:
1.同一个对象必须始终返回同一个相同的hashCode。
2.两个对象的equals返回true,则这两个对象必须返回相同的hashCode。
3.两个对象不等,也可能返回相同的hashCode。

另外

哈希就是一种单向的映射。
而HashSet则是一种高效的集合,要记住的是,它是无序的。
若需要保障Set集合的顺序,则需要使用LinkedHashSet这个类。

你可能感兴趣的:(Collection体系的常用类及其背后的数据结构)