《thinking in java》学习手记(四)

1、数组
       数组与其它容器的区别在于:它可以持有privitive对象,效率高。
       尽量先考虑使用数组。
       数组的复制System.arrayCopy()。这个拷贝是浅拷贝(即对对象数组而言,拷贝的是reference数组。
2、Arrays类
       这个类有四个静态方法:数组是否相等equals();填充数组fill();对数组进行排序sort();在一个已排序的数组中查找数组binarySearch();此外还有一个asList()方法。
3、数组元素的比较
       有两种方式可以实现数组元素的比较:
       1)实现java.lang.Comparable接口。它只有一个方法compareTo()。这个方法能接收一个对象为参数,如果现有对象比参数对象小则返负数,相等则返0,比参数对象大则返正数。只要实现这个接口方法,该类就可以成功完成equals、sort等比较大小的操作。
       2)经常会有这样一些类,这些类并没有实现Comparable接口。我们不能强制将这些类重写一遍,以加进CompareTo方法。我们可以通过实现另一个接口:Comparator来解决问题。这个接口有两个方法:compare和equals。而equals一般不需要特别实现,主要是compare。
       这种方式就是:类 和 比较方法 完全分开。(与前一种方式有显著差别)
       比方说:类String有自定义的compare方法,”A” “b” “C”的排序将为: ACb,可能不是我们期望的AbC。这时我们可以这样:
import java.util.*;
public class ArraysTest {
       public static void main(String[] args) {
              // TODO Auto-generated method stub
             String[] s = { "A", "b", "C" };
              Arrays.sort( s, new AC());
              System.out.println( Arrays.asList(s));
       }
}
 
class AC implements Comparator{
       public int compare( Object obj1,Object obj2){
              String s1 = (String)obj1;
              String s2 = (String)obj2;
              return s1.toLowerCase().compareTo(s2.toLowerCase());
       }    
}
4、查找
       必须注意一个问题:数组的查找先通过sort之后才有意义。但在这样的情况中,即便经过sort,查找也是没有意义的:例如String的compare比较器是临时指定的,而不是String类自身的,就象上面那个例程一样。因为binarySearch用的比较器只能是类默认的比较器。
       比如”A” “b” “C”可以排成AbC,但查找b就会找不到。
       解决的办法是把binarySearch的第三个参数加上,即指定比较器,用法同例程的sort()。
       同时,设有五个串abcde,如果找“1”,则返-1。这个-1的含义是这样的:1应该被插在第一个位置,但实际上没查到。
5、容器类
       容器类是java中比较复杂的一个类。其容器类要解决“怎样持有对象”的问题。而它把这个问题分成两类:
       1)Collection:一组有一定规律的独立元素。分为List和Set。
       2)Map:一组以“键——值”形式出现的pairs。本质上,它也是Collection。它的键可以提取出来成为Set,它的值也可以提取为Collection,当然pair本身也可以提取成Set。
       Map可以很容易地扩展成多维。这只要将值设成Map就行了。
       选择不同容器类,应注意下面两个主要问题:重复性;插入顺序。
6、数组的工具在Arrays中,而Collection的工具则在Collections中。
7、容器的缺点——不知道对象的类型
       例如你定义了Dog和Pig,都可以放进同一容器内。但提取的时候,一定要做强制类型转换,同时使用try关键字。
       但有时,即便你不知道对象类型,一样能正常使用。例如toString()方法。
8、迭代器的好处是:提供一种脱钩机制,让你不加区别地遍历集合,而不管集合的具体实现。这是一种设计模式。
       应尽可能多地使用迭代器。例如有的集合类型是用下标访问,有的不能用下标访问,这时如果集合类型需要发生改变,则必须改动相应代码。而如果一直使用迭代器,则一般情况下只需要变动变量定义。
9、容器分类学
       容器分类图应再三温习。这个图相当重要。
       与存放对象有关的接口主要是:Collection(Set、List)、Map这几种。在理想情况下,我们应只同这些接口打交道。在绝大多数情况下这也够用了。
       例如,我们应该定义:List x = new LinkedList();注意前面是接口定义。这种定义方式可以很方便地实现容器转型。如改成x = new ArrayList(),而无需惊动其它代码。这就是这种方式的优雅之处。
       从这些例子中,我们可以看到:有数字下标的是List,没有下标的是Set(所以它无序,不支持随机访问),有对象下标的是Map。
10、List
       其特点就是有序。它会按一定顺序来保存元素,并且可以实现在List中的增删等操作(对ArrayList不适合,因速度慢)。可以制造ListIterator对象,以便双向遍历。
       实际上有两种List:ArrayList:擅长随机访问。
       LinkedList:链表,擅长插入、删除等操作,适合顺序访问。能当成栈、队列、双向队列使用。
11、Set
       Set接口与Collection完全一样,并且其中的元素是无序的。
       加入Set的每一个元素都是唯一的。因此,构成每一个Set的元素(Object)必须实现equals方法。
       有如下三种实现:
       1)HashSet。这是为优化查询而实现的Set,没有其它意义,仅仅是查询快而已。不保存插入顺序。
       2)TreeSet。是一个有序的Set。这样就可以从Set中提取一个有序序列。这里的有序并非象List那样的索引顺序,而是元素自身的先后顺序。这在对集合排序时是相当有用的。
       3)LinkedHashSet。既有HashSet的优点,又保存插入顺序。这样用Iterator来遍历时,将得到一个按插入顺序排序的序列。
       如下方法可以得到类名:如 instance 是一个 TreeSet
       instance.getClass():得到class com.p9pip.util.TreeSet
       instance.getClass().getName():得到: com.p9pip.util.TreeSet
       instance.getClass().getName().replaceAll(“//w+//.”,””):得到TreeSet。
       其中replaceAll中用到了正则表达式,一个爽字!如果自己实现,非得写一个函数不可。
12、SortedSet(TreeSet的基类)
       有一些单独的专为排序实现的方法:
       1)Comparator comparator():返回比较器。如果用Object的,则返回null。
       2)Object first():返回最小的元素。
       3)Object last():返回最大的元素。
       4)SortedSet subSet(fromElement, toElement):返回Set的子集。其中的元素从fromElement到toElement(不包括toElement)。
       5)SortedSet headSet(toElement):返回小于toElement的所有元素。
       6)SortedSet tailSet(fromElement):返回大于等于fromElement的所有元素。
       TreeSet只不过继承了SortedSet抽象类……
13、Map
       站在更为抽象的角度,ArrayList和Map都是同一类型的对象,它们都是用下标来查找。而前者的下标是数字,后者是对象。同时,前者效率较低,但占用空间相对较小。后者效率较高,但占用空间却较大。
       几种Map类的差异在于:效率,持有和表示对象pair的顺序,持有对象的时间长短,如何决定键的相等性。
       主要有下面几种Map类:
       1)HashMap:利用Hash算法实现Map。这个类用来代替Hashtable。构造函数中可以提供capacity和load factory。
       2)LinkedHashMap:很象HashMap,但结合了链表的特性。这样:常规查询时性能较HashMap稍低,而用Iterator遍历时较HashMap为高。
       3)TreeMap:基于红黑树的数据结构实现。一个有序的Map实现。它有subMap方法,从而得到一棵子树。为此,元素必须实现Comparable接口,或另外提供比较器Comparator。
       4)WeakHashMap和IdentityHashMap:不常用。
       必须注意:对于Map的值(value)可能产生变化的情形,不能使用primitive的wrapper类(如Integer等)。因这些值一旦赋值,就只能读取(想想Integer等类中有哪些方法就明白了)。必须另外实现相应的类。
14、SortedMap(TreeMap的基类)
       与SortedSet极为相似,包括方法。
15、Hashtable、Vector、Stack属于老版本留下来的类
16、选择实现
       容器与容器的区别,在于相应接口背后的数据结构实现。
16.1 如何挑选List
       1)处理固定数量的元素,用数组;
       2)处理大量插入删除,用LinkedList;
       3)处理顺序读取,用ArrayList。
       一般而言,先用ArrayList,一旦发现有大量插删操作,将其直接改为LinkedList即可。这二者性能相差相当大。
16.2 如何挑选Set
       一般选HashSet就够用了。须加快遍历速度,则用LinkedHashSet。须排序,用TreeSet。
       这三者性能十分接近。
16.3 如何挑选Map
       一般选HashMap就够用了。须排序选用TreeMap。至于LinkedHashMap,一般不用。
       这三者性能十分接近。

你可能感兴趣的:(J2EE)