likes
comments
collection
share

java 基础--深入理解集合

作者站长头像
站长
· 阅读数 71

java基础

集合:

  • 总结了很久,发现还是从接口的角度去总结条理比较清楚所以这篇文章主要从接口的角度分析集合,*

1、单列集合(collection接口)

Collection接口是所有单列集合的父接口,它也有一个父接口:“Iterator”,这是一个迭代器接口,里面有三个抽象方法(抽象方法是没有实体的,它只是用来指定具体实现的):

  • 1、Boolean hasNext():用来指定一个判断方法,用来判断集合中下一个元素是否为空
  • 2、next():用来指定一个迭代方法,返回集合中的下一个元素,并指向这个元素。
  • 3、remove():移除当前指向的元素

所有的单列集合都继承了迭代器中的方法,所以当从单列集合中获取元素时只能通过迭代器遍历,无法直接取出指定元素————当然,不是绝对的,比如ArrayList,下面会逐一解释。

list集合(List接口)

list集合是典型的单列集合,其显著特征:

  • 1、有序(本篇文章中的“有序”表示按存储先后顺序排序)*
  • 2、允许元素重复(继承List接口的实现类例如:ArrayList、Vector、LinkedList都是数组结构或者链表结构、元素之间没有依赖关系,元素间互不干扰当然就可以重复)list集合和数组很相似,但list集合中可以储存不同类型的数据,其储存空间是灵活可变的,并且在Java中集合只能够储存包装类型数据,大部分List集合其底层的结构仍然是数组!!!!!

接下来分别说一下几种list集合实现类之间的区别: 首先,数组是连续的,所以在地址上数组类型的集合没有什么区别

  • ArrayList:数组结构,不定长,没有线程锁,线程不安全。数组结构当然可以使用下标进行索引,

仍然是数组结构所带来的几个特点 :从数组中间位置删除一个元素需要付出很大的代价,因为这个元素后面的所有元素都会发生变化。而单纯的数组其效率要高于List。因为list还要维护一些其他的类容

  • Vector:与ArrayList最大的区别在于是否线程安全,其也是数组结构,由于其带有线程锁所以 "安全性 > ArrayList < 效率"

ok,数组结构的List就这两个, 那么有趣的地方来了,为什么同样的数组结构,集合可以储存不同类型的数据呢?同是数组结构这两个家伙为什么长度可变,这些数组做不到的事情他们是怎么做到的?

1、最基础的一点,集合中只能存储包装类型,数组中只能储存单一类型类型,注意!单一类型也可以是包装类型! 那么包装类型是什么?粗暴的理解:包装类型就是一个对象! 综合上面两点:再明白面向对象的定义,封装、继承、多态:Java中所有对象的父类是谁?————是object!那么当一个数组是object类型时它就可以存储任何类型的数据!

所以我们得出新的结论:大部分List集合其底层的结构是Object类型的数组!!!!! 2、数组结构其长度是固定不可变的,但我们可以将一个短的数组储存到一个更长的相同类型的数组中,那么我们可以在储存时判断一下,如果这个数组装不下了就把这个数组里的数据copy到一个更长的数组中去,再把原来的数据删除,这就是数组型list集合中所做的 事情。

  • LinkedList集合(LinkList实现类)

这是一个链表式的集合,和之前两者有本质上的不同: 链表结构:

  1. 存储空间不连续
  2. 数据更新不会对其他元素有影响
  3. 数据读取只能依靠遍历实现,
  4. 不存在存储长度的限制

LinkedList也是线程不安全的,基于它的结构在使用中要通过迭代器来获取元素,对于存、删数据来说效率高于数组类型List 对于查询数据低于数组类型List。

Set集合(Set接口)

第一个特点:set中的元素不能重复!第二个特点:set集合是“无序”的,即不是按照存储方式排序,并不是没有顺序!它自然有特定的排序方式,(笔者认为要想做到真正乱序难度巨大根本没有必要)有序指的是存储顺序与添加顺序相同,并且可以通过下标访问,List就是这样。

无序刚好相反,指的是存储顺序与添加顺序无关,没有下标,当然也不可能通过下标访问,Set就是如此。

  1. HashSet:无序,

1.排序方式是依靠元素的HasCode来选择位置,(通过对象的HashCode方法获取哈希值)2.内部实现是HashMap,这就表示我们在使用HashSet存储对象时其实是存储了一个K,V。3.存储方式仍然是数组!简单点说,就是将一堆K\V结构的数据存在数组里面,只是它的存储位置不是按先后顺序排列的!4.比较两个元素是否相等是依靠HasCode与两个元素的地址共同判断的,HasCode相同、地址相同才表示数据一致。如果地址值相同但HashCode值不同HashSet仍然会将其储存在两个不同位置,计算方式是 “当前集合的最后一位的位置 & HashCode”得到存储位置。既然是根据哈希值来定位寻址,那么产生一个问题:“哈希冲突”第四点说了三种情况:

—————— 哈希值不相同-地址不相同:直接存储—————— 哈希值不相同-地址相同:直接存储—————— 哈希值相同-地址相同:不存储那么第四种—————— 哈希值相同-地址不相同:产生哈希冲突,这种情况下我们可以认为最终存储的值是不一样的,但哈希值的范围是有限的,那么出现了一种情况:不同的值得出的哈希值一样,但这个数据又是有意义的,而HashSet中的数据又是根据哈希值定位了,这两个数据就在一个位置上了,为了避免后面的不覆盖前面的,我们在这个位置设置一个链表。就相当我们去电影院,同一个位置,有两个以上的人买了一样的票,那怎办呢?老板就说在这个位置吊个绳子吧,于是就把他们串在这了。哈希冲突的解决办法也相似,就在数组的当前位置存一个链表的首地址以及一个数据,剩下的数据就挂在后面。每来一个新的数据就把首位上的挤下去。

  • TreeSet: 是基于TreeMap实现的,支持两种排序方式(自然排序和定制排序)既然又是Map那特点就比较清晰了1、元素不重复2、同样是K、V结构的数据,也同样是以将元素保存在Key中来保证元素的唯一性。3、排序的方式有两种,自然排序和定制排序。通过一个比较器接口(Comparator)来比较元素的大小进行排序。这个接口需要我们在自定义类中自己实现其中的比较方法(compareTo)4、与HashSet中不同的是存储的结构,HashSet中采用数组加上链表的形式存储数据,相同哈希值的数据会得到保存,但在TreeSet中相同的数据会被覆盖。

两种set型集合都是以map来进行实现的,所以详细的数据存储过程会在map中解释。