一 、 数组:
数组(array)是相同类型变量的集合,可以使用共同的名字引用它。数组可被定义为任何类型,可以是一维或多维。数组中的一个特别要素是通过下标来访问它。数组提供了一种将有联系的信息分组的便利方法。
注意:如果你熟悉C/C++,请注意, Java数组的工作原理与它们不同。
1、数组不是集合,它只能保存同种类型的多个原始类型或者对象的引用。数组保存的仅仅是对象的引用,而不是对象本身。
2、数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中的。
3、数组声明的两种形式:一、int[] arr; 二、int arr[]; 推荐使用前者,这符合Sun的命名规范,而且容易了解到关键点,这是一个int数组对象,而不是一个int原始类型。
4、在数组声明中包含数组长度永远是不合法的!如:int[5] arr; 。因为,声明的时候并没有实例化任何对象,只有在实例化数组对象时,JVM才分配空间,这时才与长度有关。
5、在数组构造的时候必须指定长度,因为JVM要知道需要在堆上分配多少空间。反例:int[] arr = new int[];
6、多维数组的声明。int[][][] arr; 是三维int型数组。
7、一维数组的构造。形如:String[] sa = new String[5];
或者分成两句:String[] sa; sa = new String[5];
8、原始类型数组元素的默认值。对于原始类型数组,在用new构造完成而没有初始化时,JVM自动对其进行初始化。默认值:byte、short、 int、long--0 float--0.0f double--0.0 boolean--false char--'"u0000'。(无论该数组是成员变量还是局部变量)
9、对象类型数组中的引用被默认初始化为null。如:Car[] myCar = new Car[10]; 相当于从myCar[0]到myCar[9]都这样被自动初始化为myCar[i] = null;
10、对象类型的数组虽然被默认初始化了,但是并没有调用其构造函数。也就是说:Car[] myCar = new Car[10];只创建了一个myCar数组对象!并没有创建Car对象的任何实例!
11、多维数组的构造。float[][] ratings = new float[9][]; 第一维的长度必须给出,其余的可以不写,因为JVM只需要知道赋给变量ratings的对象的长度。
12、数组索引的范围。数组中各个元素的索引是从0开始的,到length-1。每个数组对象都有一个length属性,它保存了该数组对象的长度。(注意和String对象的length()方法区分开来,这两者没有统一起来是很遗憾的。)
13、Java有数组下标检查,当访问超出索引范围时,将产生ArrayIndexOutOfBoundsException运行时异常。注意,这种下标检查不是在编译时刻进行的,而是在运行时!也就是说int[] arr = new int[10]; arr[100] = 100; 这么明显的错误可以通过编译,但在运行时抛出!
Java的数组下标检查是需要额外开销的,但是出于安全的权衡还是值得的,因为很多语言在使用数组时是不安全的,可以任意访问自身内存块外的数组,编译运行都不会报错,产生难以预料的后果!
在Java中,数组是一种效率最高的存储和随机访问对象引用序列的方式。不过
数组一旦创建,大小就被固定。
·只有数组才能持有基本数据类型,其它各类的容器都没有这个能力。
·数组也是一个对象,它唯一的成员是length。“[]”语法是访问数组对象的唯
一方式。
二、Arrays类
·java.util.Arrays类有一套static方法,提供操作数组的实用功能。比如:
equal()用于比较两个数组是否相等;fill()用某个值填充数组;sort()对数组排序;
binarySearch()在已排序的数组中查找元素;asList()将数组转为List容器。
·复制数组用System.arraycopy()效率最好,它对对象的复制属于浅拷贝(只复
制引用)。
·数组的排序涉及到程序设计的基本目标:将保持不变的事物(排序的算法)与会
发生变化的事物(排序的方式)相分离。使用回调技术可以达到这个目标。Java有两种
方法提供比较功能:一是实现java.lang.Comparable接口,使类有“天生”的比较能
力;二是运用策略设计模式,通过定义一个实现了java.util.Comparator接口的类创
建一个策略,来实现比较功能。
·Arrays.binarySearch()可对已排序的数组进行快速查找,但如果数组未经排
序则后果不可预料。
Java 2 容器类
·Java 2 容器类的类库主要划分为两个概念:一是Collection,一组独立的元
素(又分为两种:List,保持元素的特定顺序;Set,不能有重复元素);二是Map,一
种成对的键值对(key-value)对象。
·容器类的缺点:只能保存Object型的引用,丢失了对象的具体类型信息,且不
能保存基本数据类型。
·迭代器用来遍历并选择序列中的对象,不用考虑序列底层的结构。Java的java
.util.Iterator提供了迭代器的功能,方便遍历容器中的对象。
·在使用容器时,尽量将具体实现的容器类向上转型,使代码只与Collection、
List、Set或Map接口打交道。
三、list
·在Java2容器类库中,主要实现了两种List:一是ArrayList,优点是随机访问
速度快速;二是LinkedList,优点是插入和删除快速,并可当作栈或队列用。两者中
,一般优先考虑使用ArrayList。
·容器类返回的迭代器如果具有修改数据功能,则所有修改都会反映到容器中。
四、Set
·Set具有与Collection完全一样的接口,它不保存重复元素,不保证维护元素
的顺序。Java2容器类库中主要有三种容器类实现了Set接口:HashSet、TreeSet和
LinkedSet,其中HashSet最常用。
·HashSet使用了散列函数,专门为快速查找而设计。存入它的对象必须定义好
equals()和hashCode()。HashSet内部由HashMap来维护数据。
·TreeSet采用红黑树的数据结构排序元素,存入它的对象必须可排序,要么这
些对象实现了Comparable接口,要么在TreeSet的构造器中传入一个Comparator。TreeSet
其实是SortedSet的唯一实现。TreeSet内部由TreeMap来维护数据。
·LinkedHashSet内部使用散列以加快查询速度,同时使用链表维护元素的存储
顺序(元素插入的次序),但存入它的对象必须定义好equals()和hashCode()。
五、MAP:
·HashMap是基于散列表的实现(取代了Hashtable),插入和查询“键值对”的开
销是固定的。HashMap是最常用的Map。
·LinkedHashMap类似于HashMap,但迭代遍历时,取得的“键值对”是其插入次
序或LRU次序。
·TreeMap是基于红黑树的实现,迭代遍历时,其键或键值会被排序(次序由Comparable
或Comparator决定)。
·WeakHashMap和IdentityHashMap专为解决特殊问题而设计。
·由Map返回的Collection(keySet或values方法)背后的数据依然由Map支持,对
Collection的任何修改都会反映到Map中。
·在使用散列数据结构的容器中,如HashMap和HashSet,存入其中的元素(或键
)必须同时覆盖Object的equals()和hashCode()方法,否则起不到保证唯一的效果。
因为Object中的这两个方法都是使用对象的地址,内存地址总是不相同的。另外,
hashCode()不一定总能返回唯一的标识码,但equals()必须能够严格判断两个对象是
否相同。
·设计hashCode()时应注意的要点:1.对同一个对象产生相同的值;2.不依赖于
具有唯一性的对象信息(如对象的地址);3.必须具有意义,最好基于内容生成散列码
;4.尽量使产生的散列码分布均匀。(总的来说,为类编写正确的hashCode和equals
都很需要技巧)
·如果既想持有对某个对象的引用,又希望垃圾回收器能够释放它们,那么应该
使用java.lang.ref包中的Reference相关类。
·WeakHashMap是一种节省存储空间的容器,它允许垃圾回收器自动清理那些没
有普通引用的“键”和“值”。实际上,WeakHashMap自动使用WeakReference包装了
每一个“键”对象。
·就像Arrays类一样,Collections提供了一些方便操作Collection或List的静
态方法。特别地,它还提供了一系列ummodifiableXXX方法,返回一个“只读”的Set、
List或Map容器。
·Java容器有一种保护机制,能够防止多个进程同时修改同一个容器的内容。如
果在迭代遍历某个容器的过程中,同时又修改容器的对象,则会抛出异常。所以,应
该先对容器做完修改操作后再获取迭代器。
·Arrays.asList()方法返回的List并非java.util.ArrayList类型,不支持add、
remove、clear等修改方法,不过可以使用set方法。
·在新程序中,不应再使用过时的Vector、Hashtable和Stack容器。