Collection集合

来源:互联网 发布:oracle java 放弃 编辑:程序博客网 时间:2024/06/10 20:07

 

单列集合

Collection   顶级接口

List 接口

set接口

ArrayList

HashSet

Linkedlist

TreeSet

Vector

 

 

Collection提供的常用集合操作

boolean add(E e)                                                   //添加单个元素boolean addAll(Collection<? extends E> c)                    //添加另一个集合中的所有元素boolean remove(Object o)                                         //移除指定内容的元素void clear()                                                       //清空所有元素boolean removeAll(Collection<?> c)                             //移除与另一个集合中所有相同的元素boolean retainAll(Collection<?> c)                              //移除去另一个集合中所有不相同的元素boolean contains(Object o)                                        //查询是否包含一个元素boolean containsAll(Collection<?> c)                            //查询是否包含另一个集合中的所有元素boolean equals(Object o)                                           //比较两个集合引用的值boolean isEmpty()                                                  //检测是否为空int size()                                                         //获取元素数量Object[] toArray()                                                 //集合转数组<T> T[] toArray(T[] a)                                             //集合转为指定类型的数组,(示例:Integer[] arr = collection.toArray(new Integer[0]));Iterator<E> iterator()                                             //返回集合内置迭代器对象//迭代器Iterator为一个接口类型,在集合的内部实现了它,该内部类为集合提供了相应的遍历方式。通过调用集合的iterator()方法获取到一个对应的迭代器。boolean hasNext()                                                 //检测是否已到集合尾E next()                                                          //取出下一个元素。void remove()                                                     //移除最近一次获取到的那个元素

 

List集合:

   List使用线性表数据结构特点在于元素进出有序,即元素存放的方式与添加进去的顺序是一致的。所以List集合体系下的所有实现类均具备使用索引访问集合中任意元素的功能。通过索引可以非常方便的操作指定的元素,这是List的最大特点所在。

 

  除了继承而来的 Collection 方法外,添加了多个用于索引操作的方法。

void add(int index, E element)                                     //在指定索引处添加元素boolean addAll(int index, Collection<? extends E> c)          //在指定索引处添加另一个集合的所有元素E get(int index)                                                   //获取指定索引处的元素int indexOf(Object o)                                               //获取一个元素内容所在的索引int lastIndexOf(Object o)                                           //反向获取一个元素内容所在的索引E remove(int index)                                                 //移除掉指定索引处的元素E set(int index, E element)                                         //设置指定索引处的元素为一个新元素List<E> subList(int fromIndex, int toIndex)                       //根据指定的开始与结束点获取一个子集合(含开始不含结束)ListIterator<E> listIterator(int index)                             //列表集合专用迭代器,

 

 

  线性表数据的特性使得List所以元素可以重复,相应的还提供了更多功能的迭代器,同样extends于迭代接口Iterator的列表专用迭代器接口ListIterator除了具备Iterator的功能外,还另外加了对索引操作的功能以及添加功能。

 

   boolean hasPrevious()                          //检测是否已到集合顶    E previous()                                   //取出上一个元素。    void add(E e)                                  //添加一个元素到最近一次获取到的那个元素的前面,    void set(E e)                                  //重新设置最近一次获取到的那个元素    int nextIndex()                                //返回下一个元素的索引    int previousIndex()                            //返回上一个元素的索引


  在List下实现了多个集合类,如: 

ArrayList:

  底层使用数组完成元素的顺序储存,一个ArrayList对象建立后其容量默认大小为10,大小不足时每次扩展50%。也可以通过构造函数指定大小。通过public void trimToSize()方法对多余占用空间释放。

 

  由于数组是固定大小的且内存排列连续紧凑,所以对于数据的增加和删除都会导致集合里大量元素的移位,而也正因为其内存排列连续的特性对数据的索引查找可以直接根据数组头加偏移量获取,极为快捷。

 

 

LinkedList:

  底层采取双向链表结链式存储数据,除了拥有List的共性外还提供了一些对于链表数据常用头尾操作方法offer添加,peek获取,poll删除,都对应first与last两种方式,以及设置了专用的逆向迭代器。

 

  由于数据是双向链表的形势储存,所以对于添加和删除操作较为快捷,也正因为如此,对于数据的索引查询须要从头尾开始遍历。

 

  Vector:同ArrayList一样,但内部实现同步机制,对象建立默认大小为10,大小不足时每次扩展100%

 

 

 

Set集合:

  Set集合采取树形结构特点在于元素不重复,元素进入集合后会按照特定的方式对比唯一性,然后放在适合的位置。

 

  在一个集合中加入新的元素,要保证它在集合中的唯一性,那么必然须要对集合原有元素逐一对比,当集合内元素数据过多时,这个比较过程将是非常可怕的。Set集合专为解决这一问题而设定。

 

  Set集合并没有提供更多于Collection接口的方法,所以Set集合方法可以直接参照Collection的方法。

 

  常用的Set集合有如下几种:

 

HashSet:

  采取散列表结构依据Hash值排序元素,以减少元素对比次数,又使用Hash值与equals双层对比确保元素的唯一性。

 

  Object类成员方法public int hashCode()为每一个实例对象返回一个独有的int数值,该数值的默认计算方式依据内存地址而定,该值被称之为Hash值,代表了对象在内存中的唯一性。

 

  当一个元素进入HashSet集合后,其Hash值会被取出,在集合对应的Hash表中查找是否存在该值,如果不存在将该值加入Hash表中,并依据这个值对元素排列,如果表中已经有了相同的Hash值,那么会进一步调用两个Hash值相同元素对象的equals方法,如果equals返回为true那么添加元素失败。

 

  在一个有序的数组中加入一个新的数据,并保证这个数据的唯一性使用折半查找法是很高效的,HashSet集合的原理同出一辙,排序的依据是每个元素的Hash值。

 

  至于HashSet集合在内存中到底是以什么方式来存放这个Hash表的,不外乎数组与链表两种,待以后有时间研究明白了补充上详细资料。

 

   既然HashSet集合是通过Hash与equals双重判断的方式来实现有序排列,并保证元素唯一性的,而HashCode()与equals()两个方式又是Object类的,那么对于任何类来说都可以轻松的实现自己在HashSet集合中的比较规则。

 

  通过以上了解可以得到,覆盖一个类的HashCode()与equals()方法,即可实现该类在HashSet集合中的比较规则,但需要注意的一点必须提及。为了保证自定义Hash值不重复,在HashCode中算法过程会涉及到该类的成员变量,当一个元素进入HashSet集合后其Hash值会被取出并记录在Hash表,表中对应的位置是该对象的引用,如果这个对象在进入HashSet集合后又修改了成员变量的值进一步就会改变它的Hash值,会出现Hash表中的一个Hash值与对应的对象Hash值不同的情况。

 

  且不说以上情况下会出现本来不应该出现的重复元素。当你使用remove或contains的时候,HashSet集合也是先取出元素的Hash值,然后再去Hash表中查找相同的Hash找到后再调用equals比较,最后返回remove或contains,结果由于Hash算法的改变,元素Hash值与现在指定的根本不一样,导致找不到元素,更甚至有可能会返回一个错误的结果。

 

  至少非常肯定的是除非使用clear方法清空该集合以外你再也删除不掉这个元素了,即使你使用一个hash值与原来一样的对象,equals的比较结果也不会为真,这也是Java中一种内存泄露的情况。这种错误是非常严重的,使用HashSet集合时应多加注意,所以Hash算法涉及成员数据,建议使用final常量。   

               HashCode()返回对象在内存中的唯一性,而equals()则是比较两个对象是否相同。理论上Hash值一样的对象,那么equals返回应该为true,所以在覆盖equals方法时,如无特别需要可以直接在equals内返回hashCode().equals(Obj.hashCode())。默认在Object中,equals比较的方式是 == 运算符,也就是两个引用的值即两个对象的地址。

 

TreeSet:

  采取二叉树结构(更像红黑二叉树,但不确定,待以后研究)提供两种比较元素的方式。

  1.底层通过调用元素对象覆盖Comparable接口的compareTo方法对元素对比排序。底层使用标准的左小右大二叉树,当一个新的元素进入集合时先与堆顶元素比较,然后根据返回值选择左支或右支循环向下比较,直到树根或找到相同的元素。(当元素过多的情况时,集合内部会取树结构中的折中值第一次与新元素比较,以减少比较次数)

 

  2.在TreeSet集合对象建立的时候,通过构造函数为其传递一个比较器,比较器必须实现于Comparator接口,在此接口中有abstract方法compare,该方法接收两个Object类型的参数,在传递了比较器的TreeSet集合中添加一个元素时,集合会在底层调用该比较器,将新的元素做为参数1,集合中原有元素做为参数2,传递进比较器的覆盖compare方法中,通过compare方法的返回值依次与对应二叉树的一个分支上的元素做比较。

  当TreeSet设置了比较器后,将以比较器为最终比较方法,元素自身覆盖于Comparable接口的compareTo方法失效。

 

  虽然TreeSet的比较方式比HashSet更灵活,但做为数据结构的二叉树是基于链表结构实现的数据串连,而链表结构数据的查找,每一次都需要循环取next值,这也是链表结构最消耗资源的地方。再加上每一次对比都会调用compareTo方法,所以我认为如果仅仅只是为了保证元素唯一性TreeSet的效率不如HashSet。

 

  TreeSet也有自己的用法和特点,如下:

 

  元素依据compareTo返回值来决定元素的大小顺序,也就是说如果通过简单的修改一个类的compareTo返回值为固定负数或正数。那么TreeSet集合将失去不重复元素的特性,因为每次比较的结果都是大于或小于固定的值,所以元素进入后都会被排在右支底或左支底,所有元素变的进出有序。这也不失为TreeSet集合的另一种使用方式。

  正因为TreeSet这种数据结构的特性,所以对于TreeSet集合来说取最大或最小元素或使用一个元素做为点分割集合都是最快捷的,

 

public Iterator<E> descendingIterator()              // 专用逆向迭代器public E first()                                      //最小元素,即树结构最左下元素。public E last()                                       //最大元素,即树结构最右下元素。public E floor(E e)                                  //小于等于给定元素的最大元素。public E higher(E e)                                //大于给定元素的最小元素。 public E lower(E e)                                 //小于给定元素的最大元素。public E pollFirst()                                  //获取并移除第一个(最低)元素。public E pollLast()                                  //取并移除最后一个(最高)元素。

  如果集合内元素为0那么以上方法都会返回null。

 

 

 


  List集合元素根据equals确定对比结果,而HashSet集合在此基础之前增加了hashCode的对比结果,TreeSet集合采用元素自身的覆盖于Comparable接口的compareTo方法或实现于Comparator接口比较器中的compare方法确定对比结果。

 

原创粉丝点击