上一篇:Java--集合类之Vector、BitSet、Stack、Hashtable
Collection和 Map可通过多种形式实现,具体由编程要求决定。可以得出,如果访问List集合中的元素,可以通过元素的索引访问;如果访问Map集合中的元素,可以通过元素的键来访问;如果访问Set集合中的元素,只能通过元素本身来访问。
下面总结集合能做的所有事情(亦可对 Set和List 做同样的事情,尽管 List 还提供了一 些额外的功能)。Map不是从 Collection 继承的,所以要单独对待。
*这是一个“可选的”方法,有的集合可能并未实现它。若确实如此,该方法就会遇到一个 UnsupportedOperatiionException,即一个“操作不支持”
List(接口) 顺序是 List 最重要的特性;它可保证元素按照规定的顺序排列。List 继承Collection 并添加了大量方法,以便我们在 List 中部插入和删除元素(只推荐对LinkedList 这样做)。List 也会生成一个 ListIterator(列表反复器),利用它可在一个列表里朝两个方向遍历,同时插入和删除位于列表中部的元素(同样地,只建议对 LinkedList这样做)
由一个数组后推得到的,ArrayList内部封装了一个动态的、允许再分配的Object数组。用于替换原先的Vector。允许我们快速访问元素,但在从列表中部插入和删除元素时,速度却嫌稍慢。一般只应该用ListIterator对一 个ArrayList 进行向前和向后遍历,不要用它删除和插入元素;与 LinkedList相比,它的效率要低许多。
ArrayList是线程不安全的,Vector是线程安全的。但由于Vector是非常古老的集合类,性能很差,通常我们都不使用Vector,即使对线程安全有需求,ArrayList也可以通过一些手段实现线程安全。
LinkedList类是List接口的实现类,这意味着它可以根据索引随机访问集合中的元素。同时,LinkedList内部是以链表的形式保存元素,所以它的删除、插入效率很高。同时,LinkedList还实现了Deque接口,可以被当成双端队列来使用,因此既可以用作“栈”,也可以用作“队列”。
Set拥有与 Collection完全相同的接口,所以和两种不同的 List 不同,它没有什么额外的功能。相反,Set 完全就是一个Collection,只是具有不同的行为。在这里,一个Set只允许每个对象存在一个实例。 添加到 Set里 的对象必须定义equals(),从而建立对象的唯一性。一个 Set不能保证自己可按任何特定的顺序维持自己的元素。
创建一个新类并且要添加到一个HashSet中时,需要重写equals()方法和hashCode()方法,并且要保证两个对象equals()相等时hashCode()也要相等。
HashSet类有一个子类LinkedHashSet,子类在存储元素的时候会使用链表维护元素的次序,相对的,效率会较HashSet低一些。
HashSet的一个子类,也是根据hashCode()决定元素存储位置。但它同时用链表维护元素插入的顺序,这样使元素看起来像是以插入顺序保存的。也就是说当遍历LinkedHashSet时,LinkedHashSet会按元素添加顺序来遍历。
由一个“红黑树”后推得到的顺序 Set,是SortedSet接口的实现类。TreeSet可以保证元素的排序顺序。因为TreeSet是有序的,所以API提供了访问第一个、后一个、最后一个、前一个元素的方法,还提供了截取子TreeSet的方法。另外,TreeSet也不是线程同步的。
比较和排序问题:TreeSet会调用集合元素的compareTo(Object obj)方法来比较元素之间的大小关系,然后按升序排列。所以我们放进TreeSet中的对象都必须保证其所属类实现了Comparable接口(该接口中声明了compareTo()方法)。Java类库中的很多类都实现有Comparable接口。
注意,实现compareTo()方法时,必须将比较对象强制转换为相同类型。可以这样说,如果想让TreeSet正常工作,集合中只能添加同种类型的对象。当然,要保证compareTo()方法和equals()方法返回的意义相同。
定制排序:如果想要改变排序方式,可以通过Comparator接口实现。该接口是一个函数式接口。在创建一个TreeSet对象时,提供一个Comparator对象与该TreeSet集合关联,由该Comparator对象负责集合元素的排序逻辑。
Map(接口) 维持“键-值”对应关系(对),以便通过一个键查找相应的值。 Map和Set有点类似,比如:
基于散列表实现(用它代替Hashtable)。针对“键-值”对的插入和检索,这种形式具有最稳定的性能。
HashMap和Hashtable的关系类似于ArrayList和Vector的关系。Hashtable线程安全,性能比较差,现在很少使用;HashMap是线程不安全的,性能较好,可以实现线程安全,推荐使用。另外,HashMap允许使用null作为key或value,但Hashtable中key和value都不可以使用null.
为了成功地在HashMap和Hashtable中存储对象,用作key的对象必须实现equals()方法和hashCode()方法。
使用自定义类作为HashMap、Hashtable的key时,如果重写该类的equals()方法和hashCode()方法,必须保证两个方法判断标准一致, 即两个key通过equals()方法返回true时,它们的hashCode()返回值应该也相等。
是SortedMap接口的一个实现类,在一个“红-黑”树的基础上实现。每个键值对即作为红黑树的一个结点。TreeMap保存结点时,需要对节点进行排序,所以我们会得到有顺序排列的键值对。
TreeMap的排列方式类似与TreeSet的排序方式:
类似于TreeSet中判断两个元素相等的标准,TreeMap判断两个key相等的标准是:两个key通过compareTo()方法返回0即可。
是HashMap的一个子类,使用双向链表维护key-value对的次序。该链表负责维护Map的迭代顺序,迭代顺序与插入的顺序保持一致。因为需要维护元素插入顺序,性能略低于HashMap。但因为使用链表,在迭代访问Map里的全部元素时将有较好的性能。
与HashMap不同的是,HashMap的key保留了对实际对象的强引用,这意味着只要该HashMap对象不被销毁,其中所有的key所引用的对象都不会被垃圾回收器回收,HashMap也不会自动删除这些键值对;但WeakHashMap保留的是弱引用,如果WeakHashMap对象保存的key所引用的对象没有被其他强引用变量引用,这些key引用的变量可能被垃圾回收,WeakHashMap也可能自动删除这些键值对。
和HashMap的区别在于,它处理两个key相等比较独特:当且仅当两个key严格相等(key1==key2)时,IdentityHashMap才认为它们相等。
enum Season //定义枚举类
{ SPRING, SUMMER, FALL, WINTER }
public class EnumMapTest{
public static void main(String[] args){
EnumMap enum = new EnumMap(Season.class);
enum.put(Season.SUMMER,"夏日炎炎");
enum.put(Season.SPRING,"春暖花开");
System.out.println(enum);
}
}
/********输出结果:********/
{SPRING=春暖花开, SUMMER=夏日炎炎}
(各Map对应相应的Set类,性能分析也适用于上面的那些Set类)
线程相关知识可以看这篇博客。
上面讲到的ArrayList、LinkedList、HashSet、TreeSet、HashMap、TreeMap等都是线程不安全的。如果程序中有多个线程需要访问以上这些集合,就可以使用Collections提供的类方法把这些集合包装成线程安全的集合。Collections提供了如下几个静态方法:
例如需要在多线程中使用线程安全的HashMap对象:
HashMap m = Collection.synchronizedMap(new HashMap());