Java基础--集合

Java的集合类主要由两个接口派生而成:Collection和Map

Java基础--集合

Collection接口是List、Set和Queue接口的父接口

两种遍历集合元素的方法

  1. 使用Iterator接口遍历集合元素
  2. 使用foreach循环遍历集合元素

把一个对象丢进集合中,集合会忘记对象的类型,把集合元素当作Object类型的实例进行处理,JDK1.5之后,可以使用泛型来限制集合里的元素。

使用Iterator接口遍历集合元素

Iterator仅用于遍历集合,依附于Collection对象。

Iterator接口提供三个方法:

  1. boolean hasNext():如果还有没有被遍历的元素,返回true
  2. Object next():返回集合中下一元素
  3. void remove():删除集合里上一次next方法返回的元素

Collection集合中的元素不能被改变,只有remove()方法可以,否则引发异常

使用Iterator对集合进行迭代时,Iterator并没有把集合元素本身传给迭代变量,二是把集合元素的值传给迭代变量,修改迭代变量的值对集合元素没有影响。

Iterator迭代器采用快速失败机制,一旦迭代过程中检测到该集合已经被修改,程序立即引发异常,而不是显示修改过的结果。

使用foreach循环遍历集合元素

过程与使用Iterator接口迭代访问集合元素类似,集合元素同样不能被改变。

Set集合

Set实际上就是Collection,只是行为不同--不允许包含重复元素,没有提供任何额外方法。

判断两个对象相同使用equals方法,比较返回true,Set不会接受这两对象;返回false,会接受。

HashSet类

按哈希算法存储集合元素,存储和查找性能好。特点如下:

  • 不能保证元素的排列顺序,排列顺序可能发生变化
  • 不同步,若多个线程同时访问一个HashSet,必须通过代码保证同步
  • 集合元素可以为null

判断两个元素相等的标准:两个对象通过equals()方法比较相等,并且两个对象的hashCode()方法返回值也相等。

注意:当把一个对象放入HashSet中时,如果需要重写该对象对应类的equals()方法,也应该重写其hashCode()方法。

其规则是:如果两个对象通过equals()方法比较相等返回true,则两个对象的hashCode()值也应该相同。

hashCode()重写规则记住判断两个元素相等的标准

向其中添加可变对象时,小心修改其中的对象,可能导致该对象与集合中其他对象相等,无法准确访问对象

LinkedHashSet类

是HashSet类的子类,根据元素的hashCode值来决定元素的存储位置,但它同时使用链表维护元素的次序,使得元素看起来是以插入的顺序保存的。

遍历元素时,将按添加元素的顺序访问集合中的元素。

由于需要维护插入顺序,性能略低于HashSet的性能。

依然是HashSet,不允许集合元素重复。

TreeSet类

是SortedSet接口的实现类,可以确保集合元素处于排序状态。根据元素值的大小进行排序。采用红黑树存储集合元素。

支持两种排序:自然排序和定制排序。默认情况下,采用自然排序。

自然排序

调用集合元素的compareTo(Object obj)方法比较元素之间的大小关系,然后将集合元素按升序排列

向其中添加元素时,第一个元素无需实现Comparable接口,后面添加的元素必须实现。

大部分类在实现compareTo(Object obj)方法时,需要将被比较对象obj强制类型转换成相同类型。只有两个相同类型的实例才会比较大小

判断两个对象是否相等的唯一标准是:两个对象通过compareTo(Object obj)方法比较是否返回0。

当把一个对象放入TreeSet中,重写该对象对应类的equals()方法时,应保证该方法与compareTo(Object obj)方法有一致的结果

定制排序

想以降序排列,通过Comparator接口的帮助。

List集合

代表了一组有序、可重复的集合,每个集合元素有对应的顺序索引(这样就可以使用for循环遍历元素了)

List接口

与Set相比,增加了根据索引来插入、替换和删除集合元素的方法

判断两个对象相等的标准:通过equals()方法比较返回true

还提供了一个listIterator()方法,返回一个ListIterator对象

ListIterator接口

继承了Iterator接口,增加了如下方法:

  • boolean hasPrevious():返回该迭代器关联的集合是否还有上一个元素
  • Object previous():返回该迭代器的上一个元素
  • void add():在指定位置插入一个元素

增加了向前迭代的功能,还可以通过add方法向List集合中添加元素(Iterator只能删除元素)

ArrayList和Vector

基于数组实现的List类,封装了一个动态的、允许再分配的Object[]数组,两者功能相似,但Vector比较古老,有很多缺点,一般常用ArrayList。

ArrayList是线程不安全的,多个线程修改一个ArrayList集合时,必须手动保持同步。Vector线程安全,性能比ArrayList低。

Vector还有一个子类Stack,使用栈的时候少用,尽量用LinkedList。

Arrays.ArrayList

固定长度的List集合,只能遍历该集合里的元素。不是ArrayList的实现类的实例,也不是Vector实现类的实例。

Queue集合

模拟队列的数据结构

一个PriorityQueue实现类和一个Deque接口

PriorityQueue实现类

比较标准的队列实现类,PriorityQueue保存队列元素的顺序并不是按照加入队列的顺序,而是按照队列的大小重新排序。

先弹出的数据是最小的元素,违反了先进先出的规则。不允许插入null元素,有两种排序方式:自然排序、定制排序

定制排序时不要求元素实现Comparator接口

Deque接口

Queue的子接口,代表一个双端队列,允许从两端操作队列元素,可以当作双端队列也可以当作栈来使用。

Deque的一个实现类:ArrayDeque,基于数组的实现,创建时指定一个numElements参数,用于指定Object[]数组的长度;不指定默认底层数组长度为16。使用栈的数据结构推荐使用ArrayDeque或LinkedList。

LinkedList实现类

List接口的实现类,可以根据索引访问数据,还实现了Deque接口,也可以当作双端队列来使用。

内部以链表的形式保存数据,因此随机访问集合元素时性能较差,但在插入、删除元素时性能非常出色。

ArrayList、ArrayDeque内部以数组的形式来保存集合中的元素,因此随机访问集合元素是性能较好。

Vector也是以数组的形式存储数据的,但因它实现了线程同步功能,性能有所下降。

需要遍历List集合元素,ArrayList、Vector随机访问方法方法好;LinkedList集合,采用迭代器比较好。

经常执行插入、删除操作,采用LinkedList,ArrayList、Vector需要经常重新分配内部数组大小,时间开销大。

多个线程同时访问List集合元素,可考虑使用Collections将集合包装成线程安全的集合。

Map

又称为字典、关联数组

保存具有映射关系的数据,所有的Key放在一起就像一个Set集合,所有的value放在一起又类似于一个List,知识索引不再使用整数,二是另一个对象-key。

Map提供了一个Entry内部类来封装key-value对,而计算Entry存储时只考虑Entry封装的key。Entry包含如下三个方法:

Object getKey():返回该Entry里包含的key值

Object getValue():返回该Entry里包含的value值

Object setValue(V value):设置该Entry里包含的value值,并返回新设置的value值

Hashtable和HashMap实现类

Hashtable,线程安全,HashMap,线程不安全,性能后者大于前者。多个线程访问同一Map对象时,hashtable实现类会更好。

hashtable不允许使用null作为key和value,如果试图把null值放进hashtable中,将会引发NullPointerException异常;但hashMap可以使用null作为key或value。所以hashMap里最多只有一个key-value对的key为null,但可以有无数多个key-value对的value为null。

Hashtable,HashMap判断两个key相等的标准是:两个key通过equals()方法比较返回true,两个key的hashCode值也相等。

判断两个value相等的标准是:通过equals()方法比较返回true。

尽量不要使用可变对象作为Hashtable,HashMap的key。

LinkedHashMap实现类

HashMap的子类,使用双向链表维护key-value对的次序,迭代顺序与key-value对的插入顺序保持一致,使用==判断元素相等。

Collections--操作集合的工具类

查找、替换操作

同步控制

提供了多个synchronizedXxx()方法,该方法可以将指定集合包装成线程同步的集合,解决线程安全问题。

Collection c=Collections.synchronizedCollection(new ArrayList());

List list=Collections.synchronizedList(new ArrayList());

设置不可变集合