集合
Java中集合相关类和接口都在java.util包中
集合框架中的接口:
1. Collection:集合层次中的根接口,JDK没有提供这个接口直接的实现类。
2. Set:不能包含重复的元素。SortedSet是一个按照升序排列元素的Set。
3. List:是一个有序的集合,可以包含重复的元素。提供了按索引访问的方式。
4. Map:包含了key-value对,Map不能包含重复的key。SortedMap是一个按照升序排列key的Map。
Collection:
1. Collection接口是构造类集框架的基础。它声明所有类集都将拥有的核心方法。
2. 调用add( )方法可以将对象加入类集。注意add( )带一个Object类型的参数。因为Object是所有类的超类,所以任何类型的对象可以被存储在一个类集中。
3. 原生类型不能直接加入集合,如果想存储这些对象,可以使用原生类型包装器。
4. 通过调用addAll( )方法将一个类集的全部内容增加到另一个类集中。
5. 可以通过调用remove( )方法将一个对象删除。
6. 删除一组对象,可以调用removeAll( )方法。
7. 调用retainAll( )方法可以将除了一组指定的元素之外的所有元素删除。
8. 为了清空类集,可以调用clear( )方法。
9. 通过调用contains( )方法,可以确定一个类集是否包含了一个指定的对象。
10. 为了确定一个类集是否包含了另一个类集的全部元素,可以调用containsAll( )方法
11. 当一个类集是空的时候,可以通过调用isEmpty( )方法来予以确认。
12. 调用size( )方法可以获得类集中当前元素的个数。
13. toArray( )方法返回一个数组,这个数组包含了存储在调用类集中的元素。通过在类集和数组之间提供一条路径,可以充分利用这两者的优点。
14. 一个更加重要的方法是iterator( ),该方法对类集返回一个迭代程序。当使用一个类集框架时,迭代程序对于成功的编程来说是至关重要的
List:
1. List接口扩展了Collection并声明存储一系列元素的类集的特性。使用一个基于零的下标,元素可以通过它们在列表中的位置被插入和访问,可以包含重复元素。
2. List增加了方法add(int, Object)和addAll(int, Collection)。
3. 调用indexOf( )或lastIndexOf( )可以得到一个对象的下标。
4. 通过调用subList( )方法,可以获得列表的一个指定了开始下标和结束下标的子列表。
5. 集合中存放的是对象的引用,而不是对象本身。
6. ArrayList底层采用数组实现,而使用不带参数的构造方法生成ArrayList对象时,实际上会在底层生成一个长度为10的Object类型数组。
7. 如果增加的元素个数超过了10个,那么ArrayList底层会新生成一个数组,长度为原数组的1.5倍+1,然后将原数组的内容复制到新数组当中,
并且后续增加的内容都会放到新数组中。当新数组无法容纳增加的元素时,重复该过程。
8. 对于ArrayList元素的删除操作,需要将被删除元素的后续元素向前移动,代价比较高。
9. 集合当中只能放置对象的引用,无法放置原生数据类型,我们需要使用原生数据类型的包装类才能加入到集合当中。
10. 集合当中放置的都是Object类型,因此取出来的也都是Object,那么必须使用强制类型转换将其转换为真正的类型。JDK1.5以后可以通过泛型,编译时即确定传入类型。
11. 关于 ArrayList 与 LinkedList 的比较分析
1). ArrayList 底层采用数组实现,LinkedList 底层采用双向链表实现。
2). 当执行插入或者删除操作时,采用 LinkedList 比较好。
3). 当执行搜索操作时,采用 ArrayList 比较好(根据下标查找快?还是因为空间连续,所以查找数据快?)。
4). 当 ArrayList 对容量的需求超过当前数组的大小时,需要进行扩容。扩容过程中,会进行大量的数组复制操作,而数组复制时,最终将调用 System.arraycopy() 方法。LinkedList 由于使用了链表的结构,
因此不需要维护容量的大小,然而每次的元素增加都需要新建一个 Entry 对象,并进行更多的赋值操作,在频繁的系统调用下,对性能会产生一定的影响,在不间断地生成新的对象还是占用了一定的资源。
而因为数组的连续性,因此总是在尾端增加元素时,只有在空间不足时才产生数组扩容和数组复制。
5). ArrayList 是基于数组实现的,而数组是一块连续的内存空间,如果在数组的任意位置插入元素,必然导致在该位置后的所有元素需要重新排列,因此其效率较差,尽可能将数据插入到尾部。LinkedList 不会
因为插入数据导致性能下降。
6). ArrayList 的每一次有效的元素删除操作后都要进行数组的重组,并且删除的元素位置越靠前,数组重组时的开销越大,要删除的元素位置越靠后,开销越小。LinkedList 要移除中间的数据需要遍历完半个List。
12. 当向ArrayList中添加一个对象时,实际上就是将该对象放置到了ArrayList底层所维护的数组中;当向LinkedList中添加一个对象时,实际上LinkedList内部会生成一个Entry对象,
该Entry对象的结构为:
Entry {
Entry previous;
Object element;
Entry next;
}
其中Object类型的元素element就是我们向LinkedList中添加的元素,然后Entry又构造好了向前向后的引用previou、next,最后将生成的这个Entry对象加入到了链表当中。换句话说,LinkedList中所维护的
是一个个的Entry对象。
13. Arrays.asList()返回一个集合列表
14. 本质上,ArrayList是对象引用的一个变长数组。
15. 在已知集合容量的情况下,可以通过调用ensureCapacity( )方法来人工地增加ArrayList的容量,避免运行时集合长度变化,提高效率
16. 如果想要减小在ArrayList对象之下的数组的大小,以便它有正好容纳当前项的大小,可以调用trimToSize( )方法。
17. 一般将数据结构分为两大类:线性数据结构和非线性数据结构。线性数据结构有线性表、栈、队列、串、数组和文件;非线性数据结构有树和图。
18. 对于执行List的类集,也可以通过调用ListIterator来获得迭代函数。列表迭代函数提供了前向或后向访问类集的能力,并可让你修改元素
Set:
1. Set扩展了Collection并说明了不允许复制元素的类集的特性。因此,如果试图将复制元素加到集合中时,add( )方法将返回false。它本身并没有定义任何附加的方法。
2. SortedSet接口扩展了Set并说明了按升序排列的集合的特性。当没有项包含在调用集合中时,其中的几种方法引发NoSuchElementException异常。当对象与调用集合中的元素不兼容时,
引发ClassCastException异常。如果试图使用null对象,而集合不允许null时,引发NullPointerException异常。
3. SortedSet定义了几种方法,使得对集合的处理更加方便。调用first( )方法,可以获得集合中的第一个对象。调用last( )方法,可以获得集合中的最后一个元素。调用subSet( )方法,可以获得排序集合
的一个指定了第一个和最后一个对象的子集合。如果需要得到从集合的第一个元素开始的一个子集合,可以使用headSet( )方法。如果需要获得集合尾部的一个子集合,可以使用 tailSet( )方法。
4. 当使用HashSet时,hashCode()方法会得到调用,判断已经存储在集合中的对象的hash code值是否与增加的对象的hash code值一致;如果不一致直接加进去;如果一致,再进行equals方法的比较,
equals方法如果返回true,表示对象已经加进去了,就不会再增加新的对象,否则加进。
5. 因为对象可能会加入到set中,所以一般重写equals方法,那么也要重写hashCode方法,反之亦然。
6. HashSet底层是HashMap实现的。当使用add方法将对象添加到Set当中时,实际上是将该对象作为底层所维护的Map对象的key,而value则都是同一个Objet对象 (该对象我们用不上)。
7. 散列法的优点在于即使对于大的集合,它允许一些基本操作如add( ),contains( ),remove( )和size( )方法的运行时间保持不变。
8. 散列表又称为哈希表。散列表算法的基本思想是: 以结点的关键字为自变量,通过一定的函数关系(散列函数)计算出对应的函数值,以这个值作为该结点存储在散列表中的地址。
9. 当散列表中的元素存放太满,就必须进行再散列,将产生一个新的散列表,所有元素存放到新的散列表中,原先的散列表将被删除。在Java语言中, 通过负载因子(load factor)来决定何时
对散列表进行再散列。HashSet类的缺省负载因子是0.75。
10. 负载因子越高(越接近1.0),内存的使用效率越高,元素的寻找时间越长。负载因子越低(越接近0.0),元素的寻找时间越短,内存浪费越多。
11. TreeSet为使用树来进行存储的Set接口提供了一个工具,对象按升序存储。访问和检索是很快的。在存储了大量的需要进行快速检索的排序信息的情况下,TreeSet是一个很好的选择。
Map:
1. Map的keySet()方法会返回key的集合,因为Map的键是不能重复的,因此keySet()方法的返回类型是Set;而Map的值是可以重复的,因此values()方法的返回类型是 Collection,可以容纳重复的元素。
2. HashMap底层维护一个数组,我们向HashMap中放置的对象实际上是存储在该数组当中的,数组中存储的是Entry对象,里面包含:key,value,next,hash。其中next指向链接中的下一个对象
3. 当向HashMap中put一对键值时,它会根据key的hashCode值计算出一个位置,该位置就是此对象准备往数组中存放的位置。如果该位置没有对象存在,就将此对象直接放进数组;如果该位置已经有对象
存在了,则顺着此存在的对象的链开始寻找(Entry类中有一个Entry类型的next成员变量指向了该对象的下一个对象)。如果此链上有对象的话,再去使用equals方法进行比较,如果对此链上的每个对象的
equals方法比较都为false,则将该对象的放到数组当中,然后将该数组中该位置以前存在的元素链接到此对象的后面。
4. 关键字和值都是对象。关键字必须是唯一的。但值是可以重复的。有些映射可以接收null关键字和null值。而有的则不行
5. 映射不是类集,但可以获得映射的类集“视图”。为了实现这种功能,可以使用entrySet( )方法,它返回一个包含了映射中元素的集合(Set)
6. Map接口的entrySet( )方法,调用该方法返回一个包含映射输入的集合(Set)。这些集合元素的每一个都是一个Map.Entry对象。 对象里面包含:key,value,next,hash。
7. 排序映射允许对子映射(换句话说,就是映射的子集)进行高效的处理。使用headMap( ),tailMap( )或subMap( )方法可以获得子映射。调用firstKey( ) 方法可以获得集合的第一个关键字。而调用lastKey( )
方法可以获得集合的最后一个关键字。
9. HashMap类使用散列表实现Map接口。这允许一些基本操作如get( )和put( )的运行时间保持恒定,即便对大型集合,也是这样的。
10. TreeMap提供了按排序顺序存储关键字/值对的有效手段,同时允许快速检索。应该注意的是,不像散列映射,树映射保证它的元素按照关键字升序排序。
11. Comparator接口定义了两个方法:compare( )和equals( )。这里给出的compare( )方法按顺序比较了两个元素:
int compare(Object obj1, Object obj2) obj1和obj2
是被比较的两个对象。当两个对象相等时,该方法返回0;当obj1大于obj2时,返回一个正值;否则,返回一个负值。如果用于比较的对象的类型不兼容的话,该方法引发一个ClassCastException异常。
12. Map的最底层也是数组。
13. HashMap 实际上是一个 链表的数组。前面已经介绍过,基于 HashMap 的链表方式实现机制,只要 HashCode() 和 Hash() 方法实现得足够好,能够尽可能地减少冲突的产生,那么对 HashMap 的操作几乎
等价于对数组的随机访问操作,具有很好的性能。但是,如果 HashCode() 或者 Hash() 方法实现较差,在大量冲突产生的情况下,HashMap 事实上就退化为几个链表,对 HashMap 的操作等价于遍历链表,
此时性能很差。
14. HashMap 的一个功能缺点是它的无序性,被存入到 HashMap 中的元素,在遍历 HashMap 时,其输出是无序的。如果希望元素保持输入的顺序,可以使用 LinkedHashMap 替代。LinkedHashMap 继承自
HashMap,具有高效性,同时在 HashMap 的基础上,又在内部增加了一个链表,用以存放元素的顺序。
15. HashMap 通过 hash 算法可以最快速地进行 Put() 和 Get() 操作。TreeMap 则提供了一种完全不同的 Map 实现。从功能上讲,TreeMap 有着比 HashMap 更为强大的功能,它实现了 SortedMap 接口,
这意味着它可以对元素进行排序。TreeMap 的性能略微低于 HashMap。如果在开发中需要对元素进行排序,那么使用 HashMap 便无法实现这种功能,使用 TreeMap 的迭代输出将会以元素顺序进行。
LinkedHashMap 是基于元素进入集合的顺序或者被访问的先后顺序排序,TreeMap 则是基于元素的固有顺序 (由 Comparator 或者 Comparable 确定)。
16. LinkedHashMap 是根据元素增加或者访问的先后顺序进行排序,而 TreeMap 则根据元素的 Key 进行排序。
工具类:
1. 类集框架定义了几种能用于类集和映射的算法。在Collections类中,这些算法被定义为静态方法。
2. 集合同步的处理:
1). synchronizedList( ) 和synchronizedSet( )被用来获得各种类集的同步(安全线程)拷贝
2). 同步类集的迭代函数必须在synchronized块内使用。
3. min( )和max( ) ,获取集合中的最大值和最小值。
4. Java 2在java.util中新增加了一个叫做Arrays的类。这个类提供了各种在进行数组运算时很有用的方法。
5. asList( )方法返回一个被指定数组支持的List
6. 没有一个类集类是同步的。但是所有的从以前版本遗留下来的类都是同步的。当然,通过使用由Collections提供的算法也很容易实现类集同步
历史遗留:
1. Vector实现动态数组。这与ArrayList相似,但两者不同的是:Vector是同步的。
2. HashTable是同步的。
3. 属性(Properties)是Hashtable的一个子类。它用来保持值的列表,在其中关键字和值都是字符串(String)。
4. getProperty(“name”,“default value”)。如果“name”值没有找到,则返回“default value”。
5. Properties的一个最有用的方面是可以利用store( )和load( )方法方便地对包含在属性(Properties)对象中的信息进行存储或从盘中装入信息。
在任何时候,都可以将一个属性(Properties)对象写入流或从中将其读出。这使得属性列表特别方便实现简单的数据库。
使用迭代函数循环通过类集的内容,步骤如下:
1. 通过调用类集的iterator( )方法获得对类集头的迭代函数。
2. 建立一个调用hasNext( )方法的循环,只要hasNext( )返回true,就进行循环迭代。
3. 在循环内部,通过调用next( )方法来得到每一个元素