java集合

来源:互联网 发布:linux 网络连通 编辑:程序博客网 时间:2024/05/22 01:30
第七章 : java集合 
本章要点 : 
集合的概念 和作用
java的集合体系
Collection集合的常规用法 
使用Iterator和foreach循环遍历Collection集合
HashSet,LinkedHashSet的用法
TreeSet的用法 
EnumSet的用法
List集合的常规用法
ArrayList和Vector
固定长度的List集合
LinkedList集合的用法
Queue接口
PriorityQueue的用法
Map的概念和常规用法、
HashMap和Hashtable
TreeMap的用法
集中特殊的Map实现类
Hash算法对HashSet,HashMap性能的影响
Collections工具类的用法
Emumeration迭代器的用法

java集合的用途:
可用于存储数量不等的多个对象
可用于实现常用数据结构,如栈,队列等
可用于保存具有映射关系的关联数组

java集合的三中体系: 
Set:无序,不可重复的集合
List:有序,重复的集合
Map:具有映射关系的集合

注:从jdk1.5以后,java增加了Queue体系集合:一种队列集合实现

数组的缺点:
数组的长度不可变化,如果需要保存个数变化的数据,数组就无能为力了
数组无法保存具有映射关系的数据:也叫关联数组

集合类:负责保存,盛装其他数据--->也被称为容器类。位于java.util.包下

集合和数组的区别:
数组元素既可以是基本类型的值,也可以使对象
集合只能保存对象

java集合类的由来:主要由两个接口派生而出,Collection和Map
Collection(Set,Queue,List)
Set(EnumSet,SortedSet(TreeSet),HashSet(LinkedHashSet))
Queue(Deque(LinkedList),Priority)
List(LinkedList,ArrayList,Vector(Stack))

Map(EnumMap,IdentityHashMap,HashMap(LinkedHashMap),Hashtable(Properties),SortedMap(TreeMap),WeakHashMap)

Iterator接口与Collection集合和Map集合的区别:
Collection集合和Map集合:用于盛装其他对象
Iterator接口:用于遍历(即迭代访问)Collection集合中的元素
     Iterator对象也被称为迭代器

Iterator接口定义了单个方法:
boolean hasNext():如果被迭代的结合元素还没有被遍历,则返回true
Object next():返回集合里下一个元素
void remove():删除集合里上一次next方法返回的元素

结论1:当使用Iterator对集合元素进行迭代时,Iterator并不是吧集合元素本身传给了迭代变量,而是把集合元素的值传给了迭代变量,所以修改迭代变量的值对集合本身没有任何改变。

结论2:当使用Iterator来迭代访问Collection集合元素时,Collection集合里德元素不能被改变,只有通过Iterator的remove方法来删除上一次next方法返回的集合元素才可以,否则将会英法java.util.ConcurrentModificationException异常。

Iterator迭代器机制:快速失败(fail-fast)机制
  用途:避免共享资源而英法德潜在问题

foreach循环:foreach循环中的迭代变量的值也不是集合元素本身,系统知识一次吧集合元素的值赋给迭代变量,所以在foreach循环中修改迭代变量的值也没有任何实际意义。

Set集合:Set集合与Collection基本上完全一样,它没有提供任何额外的方法,实际上Set就是Collection,只是行为不同(Set不允许包含重复元素)

Set集合特点:如果试图把两个相同元素加入同一个Set集合中,则添加操作失败。add方法返回false,且新元素不会被加入。

Set判断两个对象相同的方法:
根据equals方法:

HashSet具有的特点:
不能保证元素的排列顺序,顺序有可能发生变化
HashSet不是同步的,如果多个线程同时访问一个Set集合,如果多个线程同时访问一个HashSet,如果有2条或者2条以上线程同时修改了HashSet集合时,必须通过代码来保证期同步。
集合元素值可以使null

HashSet与Set用法区别:如果有两个元素通过equals方法比较返回true,但他们的hashCode()方法返回值不相等,HashSet将会把它们存储在不同位置,也就可以添加成功。

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

注意:如果需要把一个对象放入HashSet中时,如果重写该对象对应类的equals()方法,也应该重写其hashCode()方法,其规则是:如果2个对象通过equals方法比较返回true时,这两个对象的hashCode也应该相同。

注:如果HashSet中包含两个元素有相同的hashCode值,将会导致性能下降。

数组时所有能存储一组元素里最快的数据结构

为什么HashSet方法用hashCode而不用数组:
因为数组元素的索引时连续的,而且数组的长度是固定的,无法自由增加数组的长度。

HashSet中每个存储元素的”槽位slot“通常称为”桶“(bucket).

重写hashCode()方法的基本规则:
当两个对象通过equals方法比较返回true时,这两个对象的hashCode应该相等。
对象中用作equals比较标准的属性,都应该用来计算hashCode值
重写hashCode()的方式:
1.对象每个有意义的属性f(即每个用作equals()比较标准的属性)计算出一个int类型的hashCode值。

当向HashSet中添加可变对象时,必须十分小心,如果修改HashSet集合中的对象,有可能导致该对象与集合中其他对象相等,从而导致HashSet无法准确访问该对象。

HashSet子类:LinkedHashSet
LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet的性能,但在迭代访问Set里德全部元素时将有很好的性能,因为它以链表来维护内部顺序。

TreeSet是SortedSet接口的唯一实现,TreeSet可以确保集合元素处于排序状态。
TreeSet并不是根据元素的插入顺序进行排序,而是根据元素实际值来进行排序的。
TreeSet采用红黑树的数据结构对元素进行排序。
TreeSet惊醒排序的规则:支持两种排序方法:
自然排序(默认)和定制排序,
自然排序:
TreeSet会调用集合元素的compareTo(Object obj)方法来比较元素之间的大小关系,然后将集合元素按升序排列,这种方式就是自然排序

如果试图把一个对象添加进TreeSet时,则该对象的类必须实现Comparable接口,否则程序将会跑出异常。

当把一个对象加入TreeSet集合中时,TreeSet调用该对象的compareTo(Object obj)方法与容器中的其他对象比较大小,然后根据红黑树算法决定它的存储位置。如果两个对象通过compareTo(Object obj)比较相等,TreeSet即认为他们应存储同一位置。

TreeSet集合判断两个对象不想等的标准是:
两个对象通过equals方法比较返回false或通过compareTo(Object obj)比较没有返回0--->及时两个对象是同一个对象,TreeSet也会把它当成两个对象进行处理。

注意:当需要把一个对象放入TreeSet中,重写该对象对应类的equals()方法时,应保证该方法与compareTo(Object obj)方法有一致的结果。
TreeSet中重写该对象对应类的equals()方法的规则:
如果两个对象通过equals方法比较返回true时,这两个对象通过compareTo(Object obj)方法比较应返回0.

如果向TreeSet中添加一个可变对象后,并且后面程序修改了该可变对象的属性,导致它与其他对象的大小顺序发生了改变,但TreeSet不会再次调整他们的顺序,甚至可能导致TreeSet中保存着两个对象,他们通过equals方法比较返回true,compareTo(Object obj)方法比较返回0.

推荐:为了让程序更加健壮,推荐HashSet和TreeSet集合中只放入不可变对象。

定制排序:
如何实现定制排序:需要在创建TreeSet集合对象时,并提供一个Comparator对象与该TreeSet集合关联,由该Comparator对象负责集合元素的排序逻辑。

注意:使用定制排序时,TreeSet对集合元素排序时不管集合元素本身的大小,而是由Comparator对象负责集合元素的排序规则。

EnumSet类是一个专为枚举类设计的集合类,指定了枚举类型(创建EnumSet时显式或隐式的指定),EnumSet集合元素时有序的,以枚举值在Enum类内的定义顺序来决定集合元素的顺序。

EnumSet在内部以位响亮的形式存储:这种存储形式非常紧凑,高效,EnumSet对象占用内存很小,而且运行效率很好。尤其是当进行批量操作(如调用containsALL和retainALL方法)时,如其参数也是EnumSet集合,则该批量操作的执行速度也非常快。

EnumSet集合不允许加入null元素,否则抛出空指针异常。如果是测试null元素或删除都不会抛出异常,只是删除操作返回false,因为没有任何null元素被删除。

EnumSet类应该通过它提供的static方法来创建EnumSet对象。
EnumSet可以复制另一个EnumSet集合中所有元素来穿件新的EnumSet。
EnumSet可以复制另一个Collection集合中所有元素来创建新的EnumSet。要求Collection集合中所有元素必须是同一个枚举类的枚举值。

HashSet和TreeSet时Set的两个典型实现,如何选择HashSet和TreeSet?
HashSet的性能总是比TreeSet好(特别是最常用的添加,查询元素等操作),因为TreeSet需要额外的红黑树算法来维护集合元素的次序。只有当需要一个保持排序的Set时,才应该使用TreeSet,否则都应该使用HashSet。
EnumSet是所有Set实现类中性能最好的,但它只能保存同一个枚举类的枚举值作为集合元素。

注意:Set的三个实现类HashSet,TreeSet和EnumSet都是现成不安全的。必须手动保证该Set集合的同步性。通常可以通过Collection工具类的synchronizedSSortedSet方法来”包装“该Set集合。此操作最好在创建时进行,以防止对Set集合的意外非同步访问:
SortedSet s = Collextions.synchronizedSortedSet(new TreeSet(....));

List接口集合代表一个有序集合,集合中国每个元素都有其对应的顺序索引。List集合允许使用重复元素,可以通过索引来访问指定位置的集合元素。List集合默认按元素的添加顺序设置元素的索引。

List增加了一种新的遍历集合元素的方法;使用普通for循环来遍历集合元素。
List判断两个对象相等只要通过equals方法比较返回true即可。
ListIterator()方法:
返回一个ListIterator对象,继承Iterator接口,提供了专门操作List的方法。

ArrayList和Vector类都是基于数组实现的List类,所以ArrayList和Vector类封装了一个动态再分配的Object[]数组。

ArrayList和Vector的显著区别是:ArrayList是线程不安全的,但Vector集合则是线程安全的,无须程序保证该集合的同步性。所以Vector的性能比ArrayList的性能低。

Collection工具类,可以讲一个ArrayList变成线程安全的。所以还是不推荐使用Vector类。

Vector提供了一个Stack子类,用于模拟栈这种数据结构。栈通常是指后进先出(LIFO)的容器。push进栈pop出栈

LinkedList是一个基于链表实现的List类,对于顺序访问集合中的元素进行了优化,特别是当插入,删除元素时速度非常快,因为LinkedList既实现了List接口,也实现了Deque接口(双向队列)。

Arrays:是操作数组的工具类。该工具类提供了asList(Object...a)方法,该方法可以把一个数组或指定个数的对象转换成一个List集合,这个List集合既不是ArrayList实现类的实例,也不是Vector实现类的实例,而是Arrays的内部类ArrayList的实例。

Arrays.ArrayList是一个固定长度的List集合,程序只能遍历访问该集合里的元素,不可增加,删除该集合里的元素。

Queue接口:用于模拟队列这种数据结构。队列通常是指”先进先出FIFO“的容器。

队列的特性:
新元素插入(offer)到队列的尾部
访问元素(poll)操作会返回队列头部的元素
队列不允许随机访问队列中的元素

LinkedList实现类:
LinkedList不仅可以当成双向队列使用,也可以当成栈使用,因为该类里海包含了pop出栈和push入栈两个方法。还实现了List接口,可以被当成List使用。

LinkedList与ArrayList,Vector的区别:
实现机制完全不同:ArrayList,Vector内部以数组的形式来保存集合中的元素,因此随机访问集合元素上有较好的性能。
LinkedList内部以链表的形式来保存集合中的元素,因此随机访问集合元素时性能较差,但在插入,删除元素时性能非常出色(只需要改变指针所指的地址即可)。实际上,Vrctor因为事先了线程同步功能,所以各方面性能都有所下降。
对于所有内部基于数组的集合实现:ArrayList,Vector使用随机访问的速度比使用Iterator迭代访问的性能要好,因为随机访问会被映射成数组元素的访问。

关于使用List集合有如下建议:
如果需要遍历List集合元素,对于ArrayList,Vector集合,则应该使用随机访问方法(get)来遍历集合元素,这样性能更好,对于LinkedList集合,则应该采用迭代器(Iterator)来遍历集合元素。
如果需要经常执行插入,删除操作来改变List集合的大小,则应该使用LinkedList集合,而不是ArrayList。使用ArrayList,Vector集合将需要经常重新分配内部数组的大小,其时间开销常常是使用LinkedList时时间开销的几十倍,效果很差。
如果多线程需要同时访问List集合中元素,可以考虑使用Vector这个同步实现。

PriorityQueue实现类:是一个比较标准的队列实现类。
PriorityQueue保存队列元素的顺序并不是按加入队列的顺序,而是按队列元素的大小进行重新排序。因此当调用peek方法或poll方法来取出队列中的元素时,并不是取出最先进入队列的元素,而是取出队列中最小的元素。而不是先进先出FIFO。

PriorityQueue不允许插入null元素,它还需要对队列元素进行排序,队列元素有两种排序方式:
自然排序:采用自然顺序的PriorityQueue集合中的元素必须实现了Comparator接口,而且应该是同一类的对个实例,否则可能导致ClassCastException异常。
定制排序:创建PriorityQueue队列时,传入一个Comparator对象,该对象负责对队列中所有元素进行 排序。采用定制排序时不要求队列元素实现Comparable接口。

Map
Map用于保存具有映射关系的数据,key和value都可以使任何引用类型的数据。Map的key不允许重复,即同一个Map对象的任何两个key通过equals方法比较总是返回false。

Map包含了一个keySet()方法,用于返回Map所有key组成的Set集合。

Map有时也被称为字典,或关联数组。
Map中包括一个内部类:Entry。该类封装了一个key-value对。

HashMap和Hashtable实现类:
Hashtable时一个古老的Map实现类。
Hashtable和HashMap存在两点典型的区别:
Hashtable时一个线程安全的Map实现,但HashMap时线程不安全的实现,所以HashMap比Hashtable的性能高一点:但如果有多条线程访问同一个Map对象时,使用Hashtable实现类会更好。
Hashtable不允许使用null作为key和value,否则将会引发空指针异常。但HashMap可以使用null作为key或value。

HashMap重写toString()方法,实际所有Map实现类都重写了toString()方法,调用Map对象的toString()方法总是返回如下格式字符串:
{key1=value,key2=value2...}

可以通过Collection工具类来把HashMap变成线程安全的,无须使用Hashtable实现类。

为了在HashMap,Hashtable中存储,获取对象,用作key的对象必须实现hashCode方法和equals方法。

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

HashMap,Hashtable中包含一个containsValue方法用于判断是否包含指定的value。

HashMap,Hashtable判断两个value相等的标准更简单:
只要两个对象通过equals比较返回true即可。

注意:当使用自定义类作为HashMap,Hashtable的key时,如果重写该类的equals(Object obj)和(hashCode)方法,应该保证两个方法的判断标准一致:当连个key通过而equals方法比较返回true时,两个key的hashCode值也应该完全相同,所以HashMap,Hashtable对key的要求与HashSet对集合元素的要求完全相同。

与HashSet类似的是:尽量不要使用可变对象作为HashMap,Hashtable的key,如果确实需要使用可变对象作为HashMap,Hashtable的key,则尽量不要再程序中修改作为key的可变对象。

LinkedHashMap使用双向链表来维护key-value对的次序,该链表定义了迭代顺序,该迭代顺序与key-value对的插入顺序保持一致。

LinkedHashMap需要维护元素的插入顺序,因此性能略低于HashMap的性能。但在迭代访问Map里德全部元素时将有很好的性能,因为它以链表来维护内部顺序。

Properties类是Hashtable类的子类。Properties类可以把Map对象和属性文件关联起来,从而可以把Map对象中的key-value对写入属性文件,也可以把属性文件中的属性名=属性值加载到Map对象中。属性文件里德属性名,属性值只能是字符串类型。

SortedMap接口和TreeMap实现类
TreeMap是基于红黑树对TreeMap中所有key进行排序,从而保证TreeMap中多有key-value对处于有序状态。
TreeMap的两种排序方式:
自然排序:TreeMap的所有key必须实现Comparable接口,而且所有key应该是同一个类的对象,否则将会抛出ClassCastException异常。
定制排序:创建TreeMap时,传入一个Comparator对象,该对象负责对TreeMap中所有key进行排序,采用定制排序时不要求Map的key实现Comparable接口。
注意:TreeMap的全部key则以TreeSet的形式存储;TreeMap对key的要求与TreeSet对元素的要求基本一致。

WeakHashMap实现类:
WeakHashMap与HashMap的用法基本相似;区别在于:
HashMap的key保留对实际对象的强引用,这意味着只要该对象不被销毁,该HashMap对象所有key所引用的对象不会被垃圾回收,HashMap也不会自动删除这些key所对应的key-value对象;、
WeakinsHashMap的key值保留对实际对象的弱引用,这意味着如果该HashMap对象所有key所引用的对象没有被其他强引用变量所引用,则这些key所引用的对象可能被垃圾回收,HashMap也可能自动删除这些key所对应的key-value对象。

IdentityHashMap实现类
实现机制:与HashMap基本相似。
但它在处理两个key相等时比较独特:在IdentityHashMap中当且仅当两个key严格相等(key1==key2)时,IdentityHashMap才认为两个key相等,对于普通HashMap而言,只要key1和key2通过equals比较返回true,且他们的hashCode值相等即可。///281

EnumMap实现类
EnumMap中所有的key都必须是单个枚举类的枚举值。
创建EnumMap时必须显式或隐式的指定它的对应的枚举类。
EnumMap在内部以数组形式保存,所以这种实现实行非常紧凑,高效。
EnumMap根据key的自然顺序(即枚举值在枚举类中的定义顺序)来维护key-value对的次序。
EnumMap不允许使用null作为key值,但允许使用null作为value。如果试图使用null作为key将跑出空指针异常。如果仅仅只是查询是否包含值为null的key,或者仅仅只是使用删除值为null的key,都不会抛出异常。
与创建普通Map有所区别的是,创建EnumMap时必须指定一个枚举类,从而将该EnumMap和指定枚举类关联起来。

Map的常用实现类的效率:
HashMap和Hashtable的效率大致相同,因为他们的实现机制几乎完全一样,但HashMap通常比Hashtable要快一点,因为Hashtable额外实现同步操作。
TreeMap通常比HashMap,Hashtable要慢(尤其在插入,删除key-value对的时候更慢),因为TreeMap需要额外的红黑树操作来维护key之间的次序。但是用TreeMap有一个好处:TreeMap中的key-value对总是处于有序状态,无须专门进行排序操作。当TreeMap被填充之后,就可以调用keySetk(),取得由key组成的Set,然后是用toArray()生成key的数组。接下来是用Arrays的binarySearch()方法在已排序的数组中快读的查询对象。
HashMap正是为快速查询而设计的。
如果需要是用Map还是应该首选HashMap实现,除非需要一个总是拍好序的Map时,才是用TreeMap。

LinkedHashMap比HashMap慢一点,因为它需要维护链表来保持Map中key的顺序。IdentityHashMap性能没有特别之处,因为它采用与HashMap基本相似的实现,只是它是用==而不是equal是来判断元素相等。EnumMap性能最好,但它 只能是用同一个枚举类的枚举值作为key。

HashSet和HashMap的性能选项
hash表里可以存储元素的位置被称为桶bucket。
通常情况下,单个桶里存储一个元素,此时有最好的性能.
hash算法可以根据hashCode值计算出桶的存储位置,接着从桶中取出元素。但hash表的状态为open:当发生hash冲突的情况下,单个桶会存储多个元素,这些元素以链表存储,必须按顺序搜索。

HashSet和Hashtable,HashMap都是使用hash算法累决定其元素(对HashMap则是key)的存储,因此HashSet,HashMap的hash表包含如下属性:
容量:hash表中桶的数量
初始化容量:创建hash表时桶的数量。HashMap和HashSet都允许在构造器中指定初始化容量。
尺寸:当前散列表中记录的数量
负载因子:负载因子等于size/capacity。负载因子为0,表示空的hash表,0.5表示半满的散列表,,以此类推,轻负载的散列表具有冲突少,适宜插入与查询的特点(但是使用Iterator迭代元素时会变慢)。
负载极限:是一个0-1的数值,负载极限决定了hash表中的最大填满程度。当hash表中的负载因子到达指定的负载极限时,hash表会自动成倍的增加容量,并将原有的对象重新分配,放入新的桶内,这称为rehashing。

HashSet,HashMap,Hashtable的构造器允许指定一个负载极限,默认的都为0.75.

默认负载极限的相关说明:是时间和空间成本上的一种折中:
较高的负载极限可以降低hash表所占用的内存空间,但会增加查询数据的时间开销。而查询是最频繁的操作(HashMap的get()与put())方法都要用到查询
较低的负载极限会增加查询数据的性能,但会降低hash表所占用的内存空间开销。
通常情况下,程序员无须改变HashSet和HashMap的负载极限值。

是用足够大的初始化容量创建HashSet和HashMap,Hashtable,可以更有效的增加记录,,但将初始化容量设置的太高可能会浪费空间,因为通常不要将初始化容量设置的太高。

操作集合的工具类:Collections
java提供了一个操作Set,List和Map等集合的工具类:Collections。
排序操作:
用于对List集合元素进行排序:
reverse,shuffle,sort,swap,rotate
同步控制:
Collections类中提供了多个synchronizedXxx方法,该方法返回指定集合对象对应的同步对象,从而可以解决多线程并发访问集合时的线程安全问题。
java中常用的集合框架中推荐使用的三个实现类:HashSet,ArrayList和HashMap都是线程不安全的。Collections 提供了多个静态方法用于创建同步集合。

设置不可变集合:
Collections提供如下三类方法来返回一个不可变的集合:
emptyXxx(),singletonXxx(),unmodifiableXxx().
上面三类方法的参数是原来的集合对象,返回值是该集合的只读版本。通过上面Collections提供三类方法,可以生成只读的Collection或Map。

本章小结:
本章详细介绍了java集合框架的相关知识,本章从java的集合框架体系开始讲起,概述了java集合框架的三个主要体系:Set,List和Map,并简述了集合在编程中的重要性。本章细致的讲述了Set,List。Queue,Map接口及各实现类的详细用法,并深入分析了各种实现类实现机制的差异,并给出选择集合实现类时的原则。本章从原理上剖析了Map结构和Set,List之间的区别和联系。本章最后还通过梭哈游戏来示范了Collections工具类的基本用法。



























































0 0
原创粉丝点击