您的位置:首页 > 职场人生

黑马程序员_集合框架基本概念

2015-04-27 19:27 246 查看
----------- android培训java培训、java学习型技术博客、期待与您交流!
------------

1、综述

为什么要使用集合类 ?集合的出现是用来存储对象,当你事先不知道要存放数据的个数,或者你需要一种比数组下标存取机制更灵活的方法时,你就需要用到集合类。所有集合类都位于java.util包下。集合中只能保存对象(保存对象的引用变量)。(数组既可以保存基本类型的数据也可以保存对象,但是数组长度固定,适用范围有限)。集合类型主要有3种:set(集)、list(列表)和map(映射)。

当我们把一个对象放入集合中后,系统会把所有集合元素都当成Object类的实例进行处理。从JDK1.5以后,这种状态得到了改进:可以使用泛型来限制集合里元素的类型,并让集合记住所有集合元素的类型(参见具体泛型的内容)。






Java的集合类主要由两个接口派生而出:Collection和Map,Collection和Map是Java集合框架的根接口,这两个接口又包含了一些接口或实现类。








Set和List接口是Collection接口派生的两个子接口,Queue是Java提供的队列实现,类似于List。





总的说来,Java API中所用的集合类,都是实现了Collection接口,他的一个类继承结构如下:


Collection<--List<--Vector

Collection<--List<--ArrayList

Collection<--List<--LinkedList

Collection<--Set<--HashSet

Collection<--Set<--HashSet<--LinkedHashSet

Collection<--Set<--SortedSet<--TreeSet




List

List接口对Collection进行了简单的扩充,它的具体实现类常用的有ArrayList和LinkedList以及Vector。你可以将任何东西放到一个 List容器中,并在需要时从中取出。ArrayList从其命名中可以看出它是一种类似数组的形式进行存储,因此它的随机访问速度极快,而 LinkedList的内部实现是链表,它适合于在链表中间需要频繁进行插入和删除操作。在具体应用时可以根据需要自由选择。前面说的Iterator只 能对容器进行向前遍历,而ListIterator则继承了Iterator的思想,并提供了对List进行双向遍历的方法。

Vector : 基于Array的List,其实就是封装了Array所不具备的一些功能方便我们使用,它不可能不受Array的限制。性能也就不可能超越Array。所以,在可能的情况下,我们要多运用Array。另外很重要的一点就是Vectorsy是线程同步的:chronized,这个也是Vector和ArrayList的唯一的区别。

ArrayList:同Vector一样是一个基于Array上的链表,但是不同的是ArrayList不是同步的。所以在性能上要比Vector优越一些,但是当运行到多线程环境中时,可需要自己在管理线程的同步问题。

LinkedList:LinkedList不同于前面两种List,它不是基于Array的,所以不受Array性能的限制。它每一个节点(Node)都包含两方面的内容:1.节点本身的数据(data);2.下一个节点的信息(nextNode)。所以当对LinkedList做添加,删除动作的时候就不用像基于Array的List一样,必须进行大量的数据移动。只要更改nextNode的相关信息就可以实现了。这就是LinkedList的优势。

总结:

1. 所有的List中只能容纳单个不同类型的对象组成的表,而不是Key-Value键值对。例如:[ tom,1,c ];

2. 所有的List中可以有相同的元素,例如Vector中可以有 [ tom,koo,too,koo ];

3. 所有的List中可以有null元素,例如[ tom,null,1 ];

4. 基于Array的List(Vector,ArrayList)适合查询,而LinkedList(链表)适合添加,删除操作。

Set

Set接 口也是Collection的一种扩展,而与List不同的时,在Set中的对象元素不能重复,也就是说你不能把同样的东西两次放入同一个Set容器中。 它的常用具体实现有HashSet和TreeSet类。HashSet能快速定位一个元素,但是你放到HashSet中的对象需要实现 hashCode()方法,它使用了前面说过的哈希码的算法。而TreeSet则将放入其中的元素按序存放,这就要求你放入其中的对象是可排序的,这就用 到了集合框架提供的另外两个实用类Comparable和Comparator。一个类是可排序的,它就应该实现Comparable接口。有时多个类具 有相同的排序算法,那就不需要在每分别重复定义相同的排序算法,只要实现Comparator接口即可。集合框架中还有两个很实用的公用 类:Collections和Arrays。Collections提供了对一个Collection容器进行诸如排序、复制、查找和填充等一些非常有用 的方法,Arrays则是对一个数组进行类似的操作。

Hash表是一种数据结构,用来查找对象。Hash表为每个对象计算出一个整数,称为Hash Code(哈希码)。Hash表是个链接式列表的阵列。每个列表称为一个buckets(哈希表元)。对象位置的计算 index = HashCode % buckets (HashCode为对象哈希码,buckets为哈希表元总数)。

当你添加元素时,有时你会遇到已经填充了元素的哈希表元,这种情况称为Hash Collisions(哈希冲突)。这时,你必须判断该元素是否已经存在于该哈希表中。

如果哈希码是合理地随机分布的,并且哈希表元的数量足够大,那么哈希冲突的数量就会减少。同时,你也可以通过设定一个初始的哈希表元数量来更好地控制哈 希表的运行。初始哈希表元的数量为 buckets = size * 150% + 1 (size为预期元素的数量)。

如果哈希 表中的元素放得太满,就必须进行rehashing(再哈希)。再哈希使哈希表元数增倍,并将原有的对象重新导入新的哈希表元中,而原始的哈希表元被删 除。load factor(加载因子)决定何时要对哈希表进行再哈希。在Java编程语言中,加载因子默认值为0.75,默认哈希表元为101。

1、HashSet:虽然Set同List都实现了Collection接口,但是他们的实现方式却大不一样。List基本上都是以Array为基础。但是Set则是在HashMap的基础上来实现的,这个就是Set和List的根本区别。HashSet的存储方式是把HashMap中的Key作为Set的对应存储项。看看HashSet的add(Object obj)方法的实现就可以一目了然了。

[java] view
plaincopy

<span style="font-family:FangSong_GB2312;font-size:14px;"> public boolean add(Object obj)

{

return map.put(obj, PRESENT) == null;

}</span>

这个也是为什么在Set中不能像在List中一样有重复的项的根本原因,因为HashMap的key是不能有重复的。

2、LinkedHashSet:HashSet的一个子类,一个链表。

3、TreeSet:SortedSet的子类,它不同于HashSet的根本就是TreeSet是有序的。它是通过SortedMap来实现的。

总结:

1. Set实现的基础是Map(HashMap);

2. Set中的元素是不能重复的,如果使用add(Object obj)方法添加已经存在的对象,则会覆盖前面的对象

Map

Map是一种把键对象和值对象进行关联的容器,而一个值对象又可以是一个Map,依次类推,这样就可形成一个多级映射。对于键对象来说,像Set一样,一 个Map容器中的键对象不允许重复,这是为了保持查找结果的一致性;如果有两个键对象一样,那你想得到那个键对象所对应的值对象时就有问题了,可能你得到 的并不是你想的那个值对象,结果会造成混乱,所以键的唯一性很重要,也是符合集合的性质的。当然在使用过程中,某个键所对应的值对象可能会发生变化,这时 会按照最后一次修改的值对象与键对应。对于值对象则没有唯一性的要求。你可以将任意多个键都映射到一个值对象上,这不会发生任何问题(不过对你的使用却可 能会造成不便,你不知道你得到的到底是那一个键所对应的值对象)。Map有两种比较常用的实现:HashMap和TreeMap。HashMap也用到了 哈希码的算法,以便快速查找一个键,TreeMap则是对键按序存放,因此它便有一些扩展的方法,比如firstKey(),lastKey()等,你还 可以从TreeMap中指定一个范围以取得其子Map。键和值的关联很简单,用pub(Object key,Object value)方法即可将一个键与一个值对象相关联。用get(Object key)可得到与此key对象所对应的值对象。

Map接口不是Collection接口的继承。Map接口用于维护键/值对(key/value pairs)。该接口描述了从不重复的键到值的映射。

Map实现类用于保存具有映射关系的数据(key-value)。Set、List和Map可以看做集合的三大类。List集合是有序集合,集合中的元素可以重复,访问集合中的元素可以根据元素的索引来访问。Set集合是无序集合,集合中的元素不可以重复,访问集合中的元素只能根据元素本身来访问(也是不能集合里元素不允许重复的原因)。Map集合中保存Key-value对形式的元素,访问时只能根据每项元素的key来访问其value。对于Set、List和Map三种集合,最常用的实现类分别是HashSet、ArrayList和HashMap三个实现类。

首先,应该从根借口Collection学习。Collection接口是List、Set和Queue接口的父接口,同时可以操作这三个接口。Collection包含的方法如下,




新建一个Demo测试一下,

[java] view
plaincopy

<span style="font-family:FangSong_GB2312;font-size:14px;">/**

*

*/

package datastructure;

import java.util.ArrayList;

import java.util.Collection;

import java.util.Iterator;

/**

* @author wangpeng

*/

public class TestCollection {

/**

* 使用ArrayList来具体实现

*/

public static void main(String[] args) {

Collection<String> c=new ArrayList<String>();

c.add("java_01");

c.add("java_02");

c.add("java_03");

c.add("java_04");

sop(c);//[java_01, java_02,java_03,java_04]

sop(c.size());//4

sop(c.toArray());//[Ljava.lang.Object;@dc8569,什么情况??

sop(c.isEmpty());//false

sop(c.remove("java_01"));//true

sop("java_02".equals("java_02"));//true

sop(c.hashCode());//1048427999

/*

* Iterator是对 collection 进行迭代的迭代器

* 包含hasNext(),next(),remove()方法

*/

Iterator<String> it=c.iterator();

while(it.hasNext()){

sop(it.next());

}

for(Iterator<String> i=c.iterator();i.hasNext();){

sop(i.next());

}

c.clear();

sop(c);//[]

}

public static void sop(Object object){

System.out.println(object);

}

}</span>

上面使用两种方法遍历集合,while循环以及增强的for循环。

2.List

|--List:元素是有序的,元素可以重复。因为该集合体系有索引。

|--ArrayList:底层的数据结构使用的是数组结构。特点:查询速度很快。但是增删稍慢。线程不同步。

|--LinkedList:底层使用的链表数据结构。特点:增删速度很快,查询稍慢。线程不同步。

|--Vector:底层是数组数据结构。线程同步。被ArrayList替代了。因为效率低。


|--Set:元素是无序,元素不可以重复。、

List:

特有方法。凡是可以操作角标的方法都是该体系特有的方法。



add(index,element);

addAll(index,Collection);



remove(index);



set(index,element);



get(index):

subList(from,to);

listIterator();

int indexOf(obj):获取指定元素的位置。

ListIterator listIterator();

List集合特有的迭代器。ListIterator是Iterator的子接口。在迭代时,不可以通过集合对象的方法操作集合中的元素。因为会发生ConcurrentModificationException异常。迭代器时会改变集合的元素,不是提取出集合元素在操作,而是“同步”的。所以,在迭代器时,只能用迭代器的放过操作元素,可是Iterator方法是有限的,只能对元素进行判断,取出,删除的操作,如果想要其他的操作如添加,修改等,就需要使用其子接口,ListIterator。该接口只能通过List集合的listIterator方法获取。

[java] view
plaincopy

<span style="font-family:FangSong_GB2312;font-size:14px;">package datastructure;

import java.util.ArrayList;

import java.util.List;

/**

* @author wangpeng

*

*/

public class ListDemo {



/**

* @param args

*/

public static void main(String[] args) {

ArrayList<String> l=new ArrayList<String>();

l.add("List_1");

l.add("List_2");

sop(l);

l.add(0, "List_01");

sop(l);

sop(l.get(2));

sop(l.lastIndexOf("List_2"));

l.set(1, "List_02");

sop(l);

sop(l.subList(0, 1));

}

public static void sop(Object object){

System.out.println(object);

}

/*

* 输出结果

* [List_1, List_2]

[List_01, List_1, List_2]

List_2

2

[List_01, List_02, List_2]

[List_01]

*/

}</span>

3.Vector

import java.util.*;

/*

枚举就是Vector特有的取出方式。

发现枚举和迭代器很像。

其实枚举和迭代是一样的。

因为枚举的名称以及方法的名称都过长。

所以被迭代器取代了。

枚举郁郁而终了。

[java] view
plaincopy

<span style="font-family:FangSong_GB2312;font-size:14px;">*/

class VectorDemo

{

public static void main(String[] args)

{

Vector v = new Vector();



v.add("java01");

v.add("java02");

v.add("java03");

v.add("java04");



Enumeration en = v.elements();



while(en.hasMoreElements())

{

System.out.println(en.nextElement());

}

}

}

</span>

Comparable接口和Comparator接口

在“集合框架”中有两种比较接口:Comparable接口和Comparator接口。像String和Integer等Java内建类实现 Comparable接口以提供一定排序方式,但这样只能实现该接口一次。对于那些没有实现Comparable接口的类、或者自定义的类,您可以通过 Comparator接口来定义您自己的比较方式。

Comparable接口

在java.lang包中,Comparable接口适用于一个类有自然顺序的时候。假定对象集合是同一类型,该接口允许您把集合排序成自然顺序。

(1) int compareTo(Object o): 比较当前实例对象与对象o,如果位于对象o之前,返回负值,如果两个对象在排序中位置相同,则返回0,如果位于对象o后面,则返回正值 。

利用Comparable接口创建您自己的类的排序顺序,只是实现compareTo()方法的问题。通常就是依赖几个数据成员的自然排序。同时类也应该覆盖equals()和hashCode()以确保两个相等的对象返回同一个哈希码。

Comparator接口

若一个类不能用于实现java.lang.Comparable,或者您不喜欢缺省的Comparable行为并想提供自己的排序顺序(可能多种排序方式),你可以实现Comparator接口,从而定义一个比较器。

(1)int compare(Object o1, Object o2): 对两个对象o1和o2进行比较,如果o1位于o2的前面,则返回负值,如果在排序顺序中认为o1和o2是相同的,返回0,如果o1位于o2的后面,则返回正值

“与Comparable相似,0返回值不表示元素相等。一个0返回值只是表示两个对象排在同一位置。由Comparator用户决定如何处理。如果两个不相等的元素比较的结果为零,您首先应该确信那就是您要的结果,然后记录行为。”

(2)boolean equals(Object obj): 指示对象obj是否和比较器相等。

“该方法覆写Object的equals()方法,检查的是Comparator实现的等同性,不是处于比较状态下的对象。”

Vector 还是ArrayList,哪一个更好,为什么?

要回答这个问题不能一概而论,有时候使用Vector比较好;有时是ArrayList,有时候这两个都不是最好的选择。你别指望能够获得一个简单肯定答案,因为这要看你用它们干什么。下面有4个要考虑的因素:

(1)API

(2)同步处理

(3)数据增长性

(4)使用模式

下面针对这4个方面进行一一探讨

API

在由Ken Arnold等编著的《Java Programming Language》(Addison-Wesley, June 2000)一书中有这样的描述,Vector类似于ArrayList.。所有从API的角度来看这两个类非常相似。但他们之间也还是有一些主要的区别的。

同步性

Vector是同步的。这个类中的一些方法保证了Vector中的对象是线程安全的。而ArrayList则是异步的,因此ArrayList中的对象并不是线程安全的。因为同步的要求会影响执行的效率,所以如果你不需要线程安全的集合那么使用ArrayList是一个很好的选择,这样可以避免由于同步带来的不必要的性能开销。

数据增长

从内部实现机制来讲ArrayList和Vector都是使用数组(Array)来控制集合中的对象。当你向这两种类型中增加元素的时候,如果元素的数目超出了内部数组目前的长度它们都需要扩展内部数组的长度,Vector缺省情况下自动增长原来一倍的数组长度,ArrayList是原来的50%,所以最后你获得的这个集合所占的空间总是比你实际需要的要大。所以如果你要在集合中保存大量的数据那么使用Vector有一些优势,因为你可以通过设置集合的初始化大小来避免不必要的资源开销。

使用模式

在ArrayList和Vector中,从一个指定的位置(通过索引)查找数据或是在集合的末尾增加、移除一个元素所花费的时间是一样的,这个时间我们用O(1)表示。但是,如果在集合的其他位置增加或移除元素那么花费的时间会呈线形增长:O(n-i),其中n代表集合中元素的个数,i代表元素增加或移除元素的索引位置。为什么会这样呢?以为在进行上述操作的时候集合中第i和第i个元素之后的所有元素都要执行位移的操作。这一切意味着什么呢?

这意味着,你只是查找特定位置的元素或只在集合的末端增加、移除元素,那么使用Vector或ArrayList都可以。如果是其他操作,你最好选择其他的集合操作类。

比如,LinkList集合类在增加或移除集合中任何位置的元素所花费的时间都是一样的—O(1),但它在索引一个元素的使用却比较慢-O(i),其中i是索引的位置.使用ArrayList也很容易,因为你可以简单的使用索引来代替创建iterator对象的操作。LinkList也会为每个插入的元素创建对象,所以你要明白它也会带来额外的开销。

最后,在《Practical Java》一书中Peter Haggar建议使用一个简单的数组(Array)来代替Vector或ArrayList。尤其是对于执行效率要求高的程序更应如此。因为使用数组(Array)避免了同步、额外的方法调用和不必要的重新分配空间的操作。
参考博客
http://www.cnblogs.com/eflylab/archive/2007/01/19/625086.html http://baike.baidu.com/view/1848852.htm http://hi.baidu.com/sengtang2012/item/f68ec012f0653e5ff0090e02 http://www.sctarena.com/Article/Article.asp?nid=3161
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: