第2章 集合 读书笔记

2.1        集合接口

1.       将集合接口与实现方法分开

 

2.       Java库中的集合接口与迭代器接口

大多数集合类的基本接口是Collection接口,该接口配有两个基本方法:

boolean add(Object obj)  将对象添加给集合,集合发生改变返回true,反之false

Iterator iterator()  返回一个能够实现Iterator接口的对象,迭代器

 

Iterator接口配有下面3个基本方法:

Object next()  依次访问集合中各个元素

boolean hasNext()  判断是否有可访问元素

void remove()  删除上次调用next时返回的元素

访问结构:

Iterator iter = col.iterator();

while(iter.hasNext()){

Object obj = iter.next();

Do something with obj

}

可将迭代器看成是位于各个元素之间;当你调用next,迭代器便越过下一个元素,并且返回它刚越过的那个元素的引用.next方法与remove方法的调用是相互关联的.如果调用remove方法之前,没有先对next方法进行过一次调用,那么这是不合法的,会抛出一个异常.由于集合接口与迭代器接口都属于通用接口,因此可以编写能在任何种类的集合上运行的公用方法.

由于Collection接口的方法比较多,如果实现Collection接口的每个类都提供这些方法,那将是非常累赘的;为了使实现程序更容易运行,将一些基本方法变成抽象的方法,并根据它们建立一些例行方法,建立了AbstractCollection;现在实现一个具体的集合类,只需要扩展AbstractCollection,就可以了.

 

2.2        具体的集合

1.       链接式列表

数组和数组列表有一个重大的缺点,就是从数组中间删除一个元素时,被删除元素后面的所有元素都必须向前移动,插入新元素时也是一样.链接式列表就能解决这些问题;数组是将对象的引用存放在连续的存储位置中,而链接式列表则将每个对象存放在独立的链接中,链接中还存放着下一个链接的索引.Java,所有的链接列表都是双重链接.链接式列表是个有序集合,add方法将对象添加到列表的尾部,如果想插入对象到列表中间,可使用迭代器负责选位置.

Java中提供了实现Collection接口的LinkedList,来作为链接式列表.

LinkedList类的listIterator方法返回一个实现ListIterator接口的迭代器对象; 如下方法:

  get(n)  访问某个特定元素

  for(int i = 0; i< list.size();i++){

    do something with list.get(i);

  }

强烈不建议使用此方法结合循环遍历链接式列表,如上代码;因为每次访问某个元素时,都必须从列表的开头重新开始搜索,list对象根本不将位置信息放入高速缓存,因此效率极低.

 

ListIterator有如下方法:

  previous()  反向遍历一个列表

  hasPrevious() 

  add()  将新元素添加到迭代器位置的前面

  remove()

  set(object)  用一个新元素来取代通过调用next或者previous方法而返回的上一个元素.

  nextIndex()  返回下次调用next时返回的元素的整数索引

  previousIndex()  返回下次调用previous时返回的元素的索引

不能在同一行里两次调用remove方法,add方法只根据迭代器的位置来运行,remove方法与add方法不同,它根据迭代器的状态来运行.若要避免同时修改列表的异常情况,请遵循一个简单原则:可以根据需要将任意数量的迭代器添加给一个容器,让所有这些迭代器都只能阅读程序.

 

如果有大量元素,并且做插入和删除操作,建议使用LinkedList之类链接式列表;如果只有少数元素,并且想要随机访问元素,那么完全可以使用ArrayList之类的数组或集合;

2.       数组列表

List接口用于描述一个有序的集合,有两种途径可以访问各个元素,一个是通过迭代器,另一个是使用getset方法;前者非常适用链接式列表,后者非常适用数组.

 

Java集合库提供了实现List接口的ArrayList, ArrayList类封装了一个动态再分配的Object[]数组.

 

ArrayList类的方法是非同步的,Vector类的方法是同步的;如果是单个线程来访问元素,且需要一个动态数组时,建议使用ArrayList;如果是多个线程来访问元素,且需要一个动态数组时,建议使用Vector;如果用单个线程来访问Vector对象,代码将会在同步操作上浪费相当多的时间,这显然是不必要的;此外,数据量少时ArrayList效率高,反之Vector.

 

3.       散列集

散列表是个链接式列表的阵列,每个列表称为一个散列表元,散列表为每个对象计算出一个整数,称为散列码,不存在重复的多个元素;若要查找表中的某个对象的位置,只要计算出它的散列码,再将它减去以散列表元的总数为模数的值,得出的数就是拥有该元素的散列表元的索引.如果大致知道散列表最终会拥有多少个元素,那么应该将初始散列表元的数量设置为大约是预期元素数量的150%,最好是将散列表的大小设置成一个素数.Java,使用加载因子决定何时要对散列表进行再散列.

Java的集合库提供了一个HashSet,用于根据散列表来实现一个散列集;构造函数如下:

  HashSet(int initialCapacity)

  HashSet(int initialCapacity,float loadFactor)

只有在不在乎集合中的各个元素的顺序时,才能使用散列集.Java增加了一个类LinkedHashSet,用于跟踪添加给散列集的元素顺序;其迭代器会按照元素的插入顺序来访问各个元素.

 

散列函数

hashCode方法是在Object类中定义的,用于计算字符串的散列码;每个对象都有一个默认的散列码,该散列码是由对象的内存地址派生而来的;默认的散列函数不是很有用,因为带有相同内容的对象可能产生不同的散列码.

 

4.       树集

树集是个有序集合,可以按照任何顺序将元素插入该集合,该元素将被纳入它的相应的排序位置;当迭代通过该集合时,各个值将自动按照排序后的顺序出现;将元素添加给树集的速度要比将它添加给散列表慢,但是仍然比添加到数组或链接式列表中的正确位置快.

Java集合库中提供了TreeSet.

 

对象的比较

树集假设插入的元素是实现Comparable接口的,它定义了一个单一方法compareTo(Object other);但是使用Comparable接口来定义排序有着明显的局限性,即只能实现该接口一次,如排序规则变化后很难适用.在这种情况下,将一个Comparator对象传递给TreeSet构造函数, Comparator接口拥有方法compare(Object a,Object b),对两个对象进行比较;这样当需要比较两个元素时,便可以使用此比较器.

 

5.       映像

映像用于存放许多关键字/值对;

Java库提供了用于映像的通用两个实现方法,即散列映像和树状映像;散列映像用于对关键字进行散列,HashMap,而树状映像则使用关键字的综合排序顺序,在搜索树中对关键字进行组织,TreeMap;散列函数或比较函数只能作用于关键字,与关键字相关的值不能进行散列或者比较.

每当将一个对象添加到映像中时,必须提供一个关键字,put(key,object);若要检索一个对象,必须使用该关键字,没有值返回null, get(object);remove()方法用于从映像中删除元素,size()方法用于返回映像中的项目的数量.

Set keySet()  关键字集

Collection values()  值集

Set entrySet()  关键字/值对集,内部对象是Map.Entry.

 

6.       专用的映像类

弱散列映像

Java集合库提供了WeakHashMap;垃圾收集器负责跟踪活的对象,只要映像对象是活的,那么它里面的所有散列表元就都是活的,并且它们不会被回收; WeakHashMap类的对象与垃圾收集器相互合作,用于在对关键字的唯一引用是来自散列表项目的引用时,删除关键字/值对.

 

链接式散列映像

Java库中提供了LinkedHashMap,它能够记住按照什么顺序将关键字/值对插入该映像的;这样就可以避免在HashMap中让关键字使用随机顺序,同时又不会产生TreeMap带来的问题;当各个项目被插入时,它们就会在双重链接式列表中被合并在一起.

 

全同散列映像

Java库中提供了IdentityHashMap,它的关键字的散列值不是由HashCode方法计算的,而是由System.identityHashCode方法进行计算;对于对象比较,它使用==,而不是使用equals.

 

2.3        集合框架

所谓框架就是一个类的集合,是构成创建高级功能的基础.

Java集合库构成了集合类的框架,它定义了许多用于实现集合的接口和抽象类,并且它描述了某些机制,比如迭代协议等;使用集合类,不必懂得关于框架的知识,但是想要实现用于多个集合类型的通用算法,或者想要添加新的集合类型的话,那么懂得框架方面的知识是很有帮助的.

CollectionMap是两个用于容器的基本接口;List是个有序集合;特征接口,RandomAccess,该接口没有配备任何方法,只是来测试某个集合是否支持有效的随机访问,ArrayListVector类实现了此接口.

Java库提供了主要抽象类:

AbstractCollection

AbstractList

AbstractSequentialList

AbstractSet

AbstractMap

如果想实现自己的集合类,也许要扩展上面某个类,以便选择例行操作的实现方法.

Java库提供了下面6个具体类:

LinkedList  链接式列表

ArrayList  数组列表

HashSet  散列集

TreeSet  树集

HashMap  散列映像

TreeMap  树状映像

还有一些旧的容器:

Vector

Stack

Hashtable

Properties

 

1.       视图与包装器

映像类的keySet方法返回的是实现Set接口的一个类的对象,同时该类的各个方法将负责对原始映像进行操作,这种集合成为视图.

视图一个非常有用的应用,就是同步式视图,下面6个方法来获取同步式集合:

Collections.synchronizedCollection

Collections.synchronizedList

Collections.synchronizedSet

Collections.synchronizedSortedSet

Collections.synchronizedMap

Collections.synchronizedSortedMap

要确保没有任何线程是通过原来的非同步方法来访问数据结构的,只要将已建立的集合传递给包装方法,而不要保存对原始对象的任何引用.如下:

Map map = Collections. synchronizedMap(new HashMap());

Collections类拥有另一组有潜在用处的包装器集,用于产生不能修改的集合视图,包装器对现有集合增加一次运行期检查,如发现对集合进行修改的试图,将抛出异常.

 

子范围

列表使用subList方法获取一个进入列表的子范围视图;任何应用于子范围的操作,能够自动反映到整个列中.如果是个有序集和映像,使用排序顺序而不是元素的位置来建立子范围.

SortedSet接口:

subSet(from,to)

headSet(to)

tailSet(from)

SortedMap接口:

subMap(from,to)

headMap(to)

tailMap(from)

 

轻量级集合包装器

Collections.nCopies(n,anObject)

将返回一个不可修改的对象,用于实现List接口,并且产生一种它拥有n个元素的错觉,每个元素看上去都像是个anObject;对存储器要求非常小;可以使用这种列表对具体容器进行初始化.

 

Collections.singleton(anObject)

将返回一个不可修改的单个元素的集的对象,用于实现Set接口的包装器对象,不需要付出建立散列表或者散列树所需要的开销

 

关于选项操作的最后说明

视图通常没有任何局限性,它可以是只读的,可能无法改变大小,也可能支持删除操作,但是不支持插入操作,这与映像的关键字视图的情况相同.

 

2.       批量操作

交集,批量操作之一.

首先,建立一个新集,用于存放查找到的结果;

Set result = new HashSet(a);

接着,保留恰好也在集合b中的所有元素;

result.retainAll(b);

 

3.       与老的API之间的关系

由于Java平台的API大部分是在集合框架建立之前设计的,因此有时必须在老数组和向量与更新型的集合之间进行转换.

如下:

Vector values = …;

HashSet staff = new HashSet(values);

 

String[] values = …;

HashSet staff = new HashSet(Arrays.asList(values));

 

Object[] values = staff.toArray();

String[] values = (String[])staff.toArray(); //此转换是错误的

String[] values = (String[])staff.toArray(new String[0]);

 

2.4        算法

通用的集合接口具备一个很大的优点,即只需要实现算法一次即可.

1.       排序与混排

Collections类中的排序方法可以用于为实现List接口的集合进行排序;Collections.sort(list)方法根据元素的自然顺序 对指定列表按升序进行排序,前提list列表元素要实现Comparable接口;如果想按照降序对列表进行排序,可以如下:

Collections.sort(list,Collections.reverseOrder());

 

Collectios类有一个算法是shuffle,它的作用与排序正好相反,它可以随机混排列表中各个元素的顺序,方法是shuffle(list).

2.       对分搜索

Collections. binarySearch(list,key[,comparator])使用对分(二进制)搜索算法来搜索指定列表,以获得指定对象;key是对象的索引,返回值>=0,反之负值.

3.       简单算法

Collections提供的方法如下:

min()  取集合最小值

max()  取集合最大值

copy()  将所有元素从源列表拷贝到目标列表中的相同位置

fill()  将列表的所有位置设置为相同值

replaceAll()  用新值取代旧值

indexOfsubList()  返回第1个等于s的索引

lastIndexOfsubList()  返回最后1个等于s的索

reverse()  将列表中的元素顺序变反

rotate()  对列表中元素的位置进行旋转移位

4.       编写你自己的算法

编写自己的算法,要尽可能地使用接口,而不是使用具体的实现代码;方法的参数尽可能使用集合接口.

 

2.5        旧的集合

1.       Hashtable

Hashtable类的各个方法是同步的;如果不需要同步,不需要与旧代码兼容,应该使用HashMap.

2.       枚举接口

老的集合使用Enumeration接口来遍历各个元素序列.

3.       属性集

属性集是个类型非常特殊的映像结构.它有下面3个特殊的特性:

关键字和值都是字符串;

表可以保存到一个文件,也可以从一个文件那里加载;

有一个默认辅助表.

用于实现一组属性的Java平台类称为Properties.

简单代码如下:

Properties settings = new Properties();

settings.put(“name”,” China ”);

settings.store(System.out,”Environment settings”);

如果能够小心地只放入字符串而不是放入其他东西,那么Properties类确实能够起到很好的作用;但是在将既有好处也有副作用的分层结构用于自己的程序之前,应该三思而后行.

4.      

Java中拥有Stack,它是Vector的子类,pushpop方法.

5.       位集合

Java中拥有BitSet,用于存放一个位序列,可以对各个位进行读取、设置或清除等操作.

爱拉托逊斯筛选法用于查找素数的一种方法,已经成为测试编译程序性能时使用的一种流行方法.

 

 

 

 

 

                                                          2007-08-16

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值