一、《疯狂Java讲义》整合片
二、CSDN解释篇
三、慕课篇
(Map、Set、List、Queue)
0x01 Collection 接口
Collection接口比较宽泛,该集合就是单一对象的集合(对比Map容器,是key-value这样的一对元素构成的键值对的集合)。一般作为工具使用,比如在“向上转型”中,定义一个Collection对象接受任何实现类, 该接口提供了很多有用的API,比如排序、查找和替换、同步控制等。子类有Set、List、Queue。
0x02 Map 接口
Map顾名思义,映射。主要存储一组组具有映射关系的数据,映射关系主要用key:value的键值对形式表示,一组键值对构成了Map的内部类Entity,所以可以把Map当做由Entity构成的集合。在Map接口的实现类中,例如TreeMap是通过红黑树实现,而红黑树在构建过程中比大小的时候,是根据Entity的key来比较的。所以Map内部也可以人为划分为KeySet和ValuesCollections(所谓人为划分,是因为实际存储是绑定在一起的,并不是分开的,分开是逻辑层面的)
HashMap 将Entity通过计算Entity.key的HashCode来存储在散列表中,通过数组实现,如果冲突,则通过挂链表来解决。
Hashtable 是比较古老的,甚至命名也没有遵守Java的驼峰命名法。古老的好像都是线程安全的。(Vector也是)。子类Properties类可以实现很方便的读写Hashtable,写在电脑里的文件常称之为配置文件,例如Windows系统中的*.ini
LinkedHashMap 与HashMap功能接近,通过链表实现。
0x03 Set 接口
之所以将Set接口放在Map接口后面讲,是因为Java在设计集合系统时,就是这样的一个顺序,也就是先设计了Map,有了HashMap、LinkedHashMap、TreeMap等,然后将所有的Value设置为NULL,于是相应的Set就诞生了:HashSet、LinkedHashSet、TreeSet等。之前一直被Java集合章节中繁杂的集合搞混淆,学了忘,忘了学。现在终于知道原因了。Map和Set有太多太多相似的地方,原来是因为底层实现根本就是一样的!Set是只有Key值,value值为NULL的一个特殊的Map。
0x04 List 接口
List说白了,就是表。这个表可以是数组实现的ArrayList,也可以是链表实现的LinkedList。比较古老的实现是Vector,现在不推荐使用,包括它的子类Stack,尽管它是线程安全的。
之所以将LinkedList标红,是因为LinkedList不但继承了List接口,而且继承了Deque(double-ended queue),所以既拥有List的特征(快速查询、迭代等),又有双端队列的特征(用作队列、栈等)
0x05 Queue 接口
这个接口是后来高版本的JDK添加的,可以方便的实现队列和栈及其相关的数据结构。实现类之一PriorityQueue优先队列,严格意义来说,已经破坏了队列的FIFO特性,每次插入新的数据都进行排序,但是打印的时候可能会显示的不是排好序的,这是该容器toString方法的问题,如果一个个poll出来,可以发现是排好序的。
Deque双端队列比较强大,拥有两个智能指针。学过数据结构的应该都知道,也应该都用C/C++亲自实现过,这里不多说。
总结
前面简要的宏观的介绍了几个常用的容器,Java中4大集合系统(Map、Set、List、Queue),常用的主要是前三种。我总结的比较肤浅,还有很多重要的特征,比如Map、Set、List的各个实现类之间的性能比较等,HashMap/HashSet插入自定义对象时,复写了equals方法、hashCode方法的不同情况对应不同结果,TreeMap/TreeSet插入自定义对象,复写equals方法、compareTo 方法的不同情况对应不同结果。很多书在这里着墨较多,以后有时间再补充。这里简要的总结几点:1.HashCode方法是根据key计算存储到数组的哪一个单元,删除操作时,先计算位置,再用equals方法比较,相同则删。查询也是先计算hashcode,找到位置后再通过equals方法比较,相同则true否则false
2.compareTo就是自定义一套比较规则,用来红黑树组件过程中比较大小时使用。如果出现修改,则删除修改的元素会出现异常,删除其它元素则不会。
3.一般都通过定义final的方法,避免在使用像HashMap、HashSet、TreeMap、TreeSet集合时,修改了集合中的元素,导致异常。
Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
Map
├Hashtable
├HashMap
└WeakHashMap
Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set。
所有实现Collection接口的类都必须提供两个标准的构造函数:无参数的构造函数用于创建一个空的Collection,有一个 Collection参数的构造函数用于创建一个新的Collection,这个新的Collection与传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection。
如何遍历Collection中的每一个元素?不论Collection的实际类型如何,它都支持一个iterator()的方法,该方法返回一个迭代子,使用该迭代子即可逐一访问Collection中每一个元素。典型的用法如下:
Iterator it = collection.iterator(); // 获得一个迭代子
while(it.hasNext()) {
Object obj = it.next(); // 得到下一个元素
}
由Collection接口派生的两个接口是List和Set。
让我们转到对框架实现的研究,具体的集合类遵循命名约定,并将基本数据结构和框架接口相结合。除了四个历史集合类外,Java 2 框架还引入了六个集合实现,如下表所示。关于历史集合类如何转换、比如说,如何修改Hashtable 并结合到框架中,请参阅历史集合类 。
接口 | 实现 | 历史集合类 |
Set |
HashSet |
|
TreeSet |
||
List |
ArrayList |
Vector |
LinkedList |
Stack |
|
Map |
HashMap |
Hashtable |
TreeMap |
Properties |
这里没有 Collection 接口的实现。历史集合类,之所以这样命名是因为从 Java 类库 1.0 发行版就开始沿用至今了。
如果从历史集合类转换到新的框架类,主要差异之一在于所有的操作都和新类不同步。您可以往新类中添加同步的实现,但您不能把它从旧的类中除去。
Collection collection = new ArrayList();(这样写的好处在于,以后如果要理性不同的集合,可以省略很多麻烦。因为都是用Collection接口里的方法,)
boolean |
确保此 collection 包含指定的元素(可选操作)。 |
|
boolean |
将指定 collection 中的所有元素都添加到此 collection 中(可选操作)。 |
|
void |
移除此 collection 中的所有元素(可选操作)。 |
|
boolean |
如果此 collection 包含指定的元素,则返回 true。 |
|
boolean |
如果此 collection 包含指定 collection 中的所有元素,则返回true。 |
|
boolean |
比较此 collection 与指定对象是否相等。 |
|
int |
返回此 collection 的哈希码值。 |
|
boolean |
如果此 collection 不包含元素,则返回 true。 |
|
Iterator |
返回在此 collection 的元素上进行迭代的迭代器。 |
|
boolean |
从此 collection 中移除指定元素的单个实例,如果存在的话(可选操作)。 |
|
boolean |
移除此 collection 中那些也包含在指定 collection 中的所有元素(可选操作)。 |
|
boolean |
仅保留此 collection 中那些也包含在指定 collection 的元素(可选操作)。 |
|
int |
返回此 collection 中的元素数。 |
|
Object[] |
返回包含此 collection 中所有元素的数组。 |
|
|
返回包含此 collection 中所有元素的数组;返回数组的运行时类型与指定数组的运行时类型相同。 |
*All方法参数的类型都为Collection ,大多数方法都是返回boolean类型值,Collection 接口用于表示任何对象或元素组。想要尽可能以常规方式处理一组元素时,就使用这一接口。(如,可以直接add(100),可以是普通数据类型)。
容器类对象在调用remove,contains等方法时需要比较对象是否相等地,这会涉及到对象类型的equals方法和hashcode方法。即,相等的对象应该有相等的hashcode.当然,如果是自定义的类型,需要重写这两个方法。
iterator接口
boolean |
如果仍有元素可以迭代,则返回 true。 |
E |
返回迭代的下一个元素。 |
void |
从迭代器指向的集合中移除迭代器返回的最后一个元素(可选操作)。 |
Set
Set接口同样是Collection接口的一个子接口,它表示数学意义上的集合概念。Set中不包含重复的元素,即Set中不存两个这样的元素e1和e2,使得e1.equals(e2)为true。由于Set接口提供的数据结构是数学意义上集合概念的抽象,因此它需要支持对象的添加、删除,而不需提供随机访问。故Set接口与Collection的接口相同,在此对里面的方法不作介绍。
boolean |
如果 set 中尚未存在指定的元素,则添加此元素(可选操作)。 |
|
boolean |
如果 set 中没有指定 collection 中的所有元素,则将其添加到此 set 中(可选操作)。 |
|
void |
移除 set 中的所有元素(可选操作)。 |
|
boolean |
如果 set 包含指定的元素,则返回 true。 |
|
boolean |
如果此 set 包含指定 collection 的所有元素,则返回 true。 |
|
boolean |
比较指定对象与此 set 的相等性。 |
|
int |
返回 set 的哈希码值。 |
|
boolean |
如果 set 不包含元素,则返回 true。 |
|
Iterator |
返回在此 set 中的元素上进行迭代的迭代器。 |
|
boolean |
如果 set 中存在指定的元素,则将其移除(可选操作)。 |
|
boolean |
移除 set 中那些包含在指定 collection 中的元素(可选操作)。 |
|
boolean |
仅保留 set 中那些包含在指定 collection 中的元素(可选操作)。 |
|
int |
返回 set 中的元素数(其容量)。 |
|
Object[] |
返回一个包含 set 中所有元素的数组。 |
|
|
返回一个包含 set 中所有元素的数组;返回数组的运行时类型是指定数组的类型。 |
按照定义,Set 接口继承 Collection 接口,而且它不允许集合中存在重复项。所有原始方法都是现成的,没有引入新方法。具体的Set 实现类依赖添加的对象的 equals()
方法来检查等同性。
“集合框架”支持 Set 接口两种普通的实现:HashSet 和TreeSet。在更多情况下,您会使用 HashSet 存储重复自由的集合。考虑到效率,添加到 HashSet 的对象需要采用恰当分配散列码的方式来实现hashCode() 方法。虽然大多数系统类覆盖了 Object 中缺省的hashCode()
实现,但创建您自己的要添加到 HashSet 的类时,别忘了覆盖 hashCode()。当您要从集合中以有序的方式抽取元素时,TreeSet 实现会有用处。为了能顺利进行,添加到TreeSet 的元素必须是可排序的。 “集合框架”添加对 Comparable 元素的支持,在排序的“可比较的接口”部分中会详细介绍。我们暂且假定一棵树知道如何保持java.lang 包装程序器类元素的有序状态。一般说来,先把元素添加到 HashSet,再把集合转换为TreeSet 来进行有序遍历会更快。
为优化 HashSet 空间的使用,您可以调优初始容量和负载因子。TreeSet 不包含调优选项,因为树总是平衡的,保证了插入、删除、查询的性能为log(n)。
HashSet 和 TreeSet 都实现 Cloneable 接口。
为演示具体 Set 类的使用,下面的程序创建了一个 HashSet,并往里添加了一组名字,其中有个名字添加了两次。接着,程序把集中名字的列表打印出来,演示了重复的名字没有出现。接着,程序把集作为TreeSet 来处理,并显示有序的列表。
import java.util.*;
public class SetExample {
public static void main(String args[]) {
Set set = new HashSet();
set.add("Bernadine");
set.add("Elizabeth");
set.add("Gene");
set.add("Elizabeth");
set.add("Clara");
System.out.println(set);
Set sortedSet = new TreeSet(set);
System.out.println(sortedSet);
}
}
运行程序产生了以下输出。请注意重复的条目只出现了一次,列表的第二次输出已按字母顺序排序。
[Gene, Clara, Bernadine, Elizabeth]
[Bernadine, Clara, Elizabeth, Gene]
List 接口
List 接口继承了 Collection 接口以定义一个允许重复项的有序集合。该接口不但能够对列表的一部分进行处理,还添加了面向位置的操作。
有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。
与 set 不同,列表通常允许重复的元素。更正式地说,列表通常允许满足 e1.equals(e2) 的元素对 e1 和 e2,并且如果列表本身允许 null 元素的话,通常它们允许多个 null 元素。难免有人希望通过在用户尝试插入重复元素时抛出运行时异常的方法来禁止重复的列表,但我们希望这种用法越少越好。
List 接口在 iterator、add、remove、equals 和 hashCode 方法的协定上加了一些其他约定,超过了 Collection 接口中指定的约定。为方便起见,这里也包括了其他继承方法的声明。
List 接口提供了 4 种对列表元素进行定位(索引)访问方法。列表(像 Java 数组一样)是基于 0 的。注意,这些操作可能在和某些实现(例如 LinkedList 类)的索引值成比例的时间内执行。因此,如果调用方不知道实现,那么在列表元素上迭代通常优于用索引遍历列表。
List 接口提供了特殊的迭代器,称为 ListIterator,除了允许 Iterator 接口提供的正常操作外,该迭代器还允许元素插入和替换,以及双向访问。还提供了一个方法来获取从列表中指定位置开始的列表迭代器。
List 接口提供了两种搜索指定对象的方法。从性能的观点来看,应该小心使用这些方法。在很多实现中,它们将执行高开销的线性搜索。
List 接口提供了两种在列表的任意位置高效插入和移除多个元素的方法。
注意:尽管列表允许把自身作为元素包含在内,但建议要特别小心:在这样的列表上,equals 和 hashCode 方法不再是定义良好的。
某些列表实现对列表可能包含的元素有限制。例如,某些实现禁止 null 元素,而某些实现则对元素的类型有限制。试图添加不合格的元素会抛出未经检查的异常,通常是 NullPointerException 或 ClassCastException。试图查询不合格的元素是否存在可能会抛出异常,也可能简单地返回 false;某些实现会采用前一种行为,而某些则采用后者。概括地说,试图对不合格元素执行操作时,如果完成该操作后不会导致在列表中插入不合格的元素,则该操作可能抛出一个异常,也可能成功,这取决于实现的选择。此接口的规范中将这样的异常标记为“可选”。
面向位置的操作包括插入某个元素或 Collection 的功能,还包括获取、除去或更改元素的功能。在 List 中搜索元素可以从列表的头部或尾部开始,如果找到元素,还将报告元素所在的位置。
void add(int index, Object element)
boolean addAll(int index, Collection collection)
Object get(int index)
int indexOf(Object element)
int lastIndexOf(Object element)
Object remove(int index)
Object set(int index, Object element)
List 接口不但以位置友好的方式遍历整个列表,还能处理集合的子集:
ListIterator listIterator()
ListIterator listIterator(int startIndex)
List subList(int fromIndex, int toIndex)
处理 subList() 时,位于 fromIndex 的元素在子列表中,而位于 toIndex 的元素则不是,提醒这一点很重要。以下 for-loop 测试案例大致反映了这一点:
for (int i=fromIndex; i
此外,我们还应该提醒的是 ― 对子列表的更改(如 add()、remove() 和 set() 调用)对底层 List 也有影响。
boolean |
向列表的尾部追加指定的元素(可选操作)。 |
|
void |
add(int index, E element) 在列表的指定位置插入指定元素(可选操作)。 |
|
boolean |
addAll(Collection extends E> c) 追加指定 collection 中的所有元素到此列表的结尾,顺序是指定 collection 的迭代器返回这些元素的顺序(可选操作)。 |
|
boolean |
addAll(int index, Collection extends E> c) 将指定 collection 中的所有元素都插入到列表中的指定位置(可选操作)。 |
|
void |
从列表中移除所有元素(可选操作)。 |
|
boolean |
如果列表包含指定的元素,则返回 true。 |
|
boolean |
如果列表包含指定 collection 的所有元素,则返回 true。 |
|
boolean |
比较指定的对象与列表是否相等。 |
|
E |
返回列表中指定位置的元素。 |
|
int |
返回列表的哈希码值。 |
|
int |
返回列表中首次出现指定元素的索引,如果列表不包含此元素,则返回 -1。 |
|
boolean |
如果列表不包含元素,则返回 true。 |
|
Iterator |
返回以正确顺序在列表的元素上进行迭代的迭代器。 |
|
int |
返回列表中最后出现指定元素的索引,如果列表不包含此元素,则返回 -1。 |
|
ListIterator |
返回列表中元素的列表迭代器(以正确的顺序)。 |
|
ListIterator |
返回列表中元素的列表迭代器(以正确的顺序),从列表的指定位置开始。 |
|
E |
移除列表中指定位置的元素(可选操作)。 |
|
boolean |
移除列表中出现的首个指定元素(可选操作)。 |
|
boolean |
从列表中移除指定 collection 中包含的所有元素(可选操作)。 |
|
boolean |
仅在列表中保留指定 collection 中所包含的元素(可选操作)。 |
|
E |
set(int index, E element) 用指定元素替换列表中指定位置的元素(可选操作)。 |
|
int |
返回列表中的元素数。 |
|
List |
返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图。 |
|
Object[] |
返回以正确顺序包含列表中的所有元素的数组。 |
|
|
返回以正确顺序包含列表中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。 |
其中set方法返回的是被替换的内容。
Linked 改快读慢
Array 读快改慢
Hash 两都之间
Collection是集合接口
|————Set子接口:无序,不允许重复。
|————List子接口:有序,可以有重复元素。
区别:Collections是集合类
Set和List对比:
Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。
Set和List具体子类:
Set
|————HashSet:以哈希表的形式存放元素,插入删除速度很快。
List
|————ArrayList:动态数组
|————LinkedList:链表、队列、堆栈。
Array和java.util.Vector
Vector是一种老的动态数组,是线程同步的,效率很低,一般不赞成使用。
Map 接口
Map
接口不是 Collection
接口的继承。而是从自己的用于维护键-值关联的接口层次结构入手。按定义,该接口描述了从不重复的键到值的映射。
我们可以把这个接口方法分成三组操作:改变、查询和提供可选视图。
改变操作允许您从映射中添加和除去键-值对。键和值都可以为 null
。但是,您不能把Map
作为一个键或值添加给自身。
Object put(Object key, Object value)返回值是被替换的值。
Object remove(Object key)
void putAll(Map mapping)
void clear()
查询操作允许您检查映射内容:
Object get(Object key)
boolean containsKey(Object key)
boolean containsValue(Object value)
int size()
boolean isEmpty()
最后一组方法允许您把键或值的组作为集合来处理。
public Set keySet()
public Collection values()
public Set entrySet()
因为映射中键的集合必须是唯一的,您用 Set
支持。因为映射中值的集合可能不唯一,您用Collection
支持。最后一个方法返回一个实现 Map.Entry
接口的元素 Set
。
Map.Entry 接口
Map
的 entrySet()
方法返回一个实现Map.Entry
接口的对象集合。集合中每个对象都是底层 Map
中一个特定的键-值对。
通过这个集合迭代,您可以获得每一条目的键或值并对值进行更改。但是,如果底层 Map
在Map.Entry
接口的setValue()
方法外部被修改,此条目集就会变得无效,并导致迭代器行为未定义。
HashMap 类和 TreeMap 类
“集合框架”提供两种常规的 Map
实现:HashMap
和TreeMap
。和所有的具体实现一样,使用哪种实现取决于您的特定需要。在Map
中插入、删除和定位元素,HashMap
是最好的选择。但如果您要按顺序遍历键,那么TreeMap
会更好。根据集合大小,先把元素添加到 HashMap
,再把这种映射转换成一个用于有序键遍历的 TreeMap
可能更快。使用HashMap
要求添加的键类明确定义了 hashCode()
实现。有了TreeMap
实现,添加到映射的元素一定是可排序的。我们将在排序中详细介绍。
为了优化 HashMap
空间的使用,您可以调优初始容量和负载因子。这个TreeMap
没有调优选项,因为该树总处于平衡状态。
HashMap
和 TreeMap
都实现Cloneable
接口。
Hashtable
类和 Properties
类是Map
接口的历史实现。我们将在Dictionary 类、Hashtable 类和 Properties 类中讨论。
映射的使用示例
以下程序演示了具体 Map
类的使用。该程序对自命令行传递的词进行频率计数。HashMap
起初用于数据存储。后来,映射被转换为TreeMap
以显示有序的键列列表。
import java.util.*;
public class MapExample {
public static void main(String args[]) {
Map map = new HashMap();
Integer ONE = new Integer(1);
for (int i=0, n=args.length; i
用 Bill of Rights 的第三篇文章的文本运行程序产生下列输出,请注意有序输出看起来多么有用!
无序输出:
{prescribed=1, a=1, time=2, any=1, no=1, shall=1, nor=1, peace=1, owner=1, soldier=1, to=1, the=2, law=1, but=1, manner=1, without=1, house=1, in=4, by=1, consent=1, war=1, quartered=1, be=2, of=3}
有序输出:
{a=1, any=1, be=2, but=1, by=1, consent=1, house=1, in=4, law=1, manner=1, no=1, nor=1, of=3, owner=1, peace=1, prescribed=1, quartered=1, shall=1, soldier=1, the=2, time=2, to=1, war=1, without=1}
Java集合框架是最常被问到的Java面试问题,要理解Java技术强大特性就有必要掌握集合框架。这里有一些实用问题,常在核心Java面试中问到。
1、什么是Java集合API
Java集合框架API是用来表示和操作集合的统一框架,它包含接口、实现类、以及帮助程序员完成一些编程的算法。简言之,API在上层完成以下几件事:
● 编程更加省力,提高城程序速度和代码质量
● 非关联的API提高互操作性
● 节省学习使用新API成本
● 节省设计新API的时间
● 鼓励、促进软件重用
具体来说,有6个集合接口,最基本的是Collection接口,由三个接口Set、List、SortedSet继承,另外两个接口是Map、SortedMap,这两个接口不继承Collection,表示映射而不是真正的集合。
2、什么是Iterator
一些集合类提供了内容遍历的功能,通过java.util.Iterator接口。这些接口允许遍历对象的集合。依次操作每个元素对象。当使用 Iterators时,在获得Iterator的时候包含一个集合快照。通常在遍历一个Iterator的时候不建议修改集合本省。
3、Iterator与ListIterator有什么区别?
Iterator:只能正向遍历集合,适用于获取移除元素。ListIerator:继承Iterator,可以双向列表的遍历,同样支持元素的修改。
4、什么是HaspMap和Map?
Map是接口,Java 集合框架中一部分,用于存储键值对,HashMap是用哈希算法实现Map的类。
5、HashMap与HashTable有什么区别?对比Hashtable VS HashMap
两者都是用key-value方式获取数据。Hashtable是原始集合类之一(也称作遗留类)。HashMap作为新集合框架的一部分在Java2的1.2版本中加入。它们之间有一下区别:
● HashMap和Hashtable大致是等同的,除了非同步和空值(HashMap允许null值作为key和value,而Hashtable不可以)。
● HashMap没法保证映射的顺序一直不变,但是作为HashMap的子类LinkedHashMap,如果想要预知的顺序迭代(默认按照插入顺序),你可以很轻易的置换为HashMap,如果使用Hashtable就没那么容易了。
● HashMap不是同步的,而Hashtable是同步的。
● 迭代HashMap采用快速失败机制,而Hashtable不是,所以这是设计的考虑点。
6、在Hashtable上下文中同步是什么意思?
同步意味着在一个时间点只能有一个线程可以修改哈希表,任何线程在执行hashtable的更新操作前需要获取对象锁,其他线程等待锁的释放。
7、什么叫做快速失败特性
从高级别层次来说快速失败是一个系统或软件对于其故障做出的响应。一个快速失败系统设计用来即时报告可能会导致失败的任何故障情况,它通常用来停止正常的操作而不是尝试继续做可能有缺陷的工作。当有问题发生时,快速失败系统即时可见地发错错误告警。在Java中,快速失败与iterators有关。如果一个iterator在集合对象上创建了,其它线程欲“结构化”的修改该集合对象,并发修改异常 (ConcurrentModificationException) 抛出。
8、怎样使Hashmap同步?
HashMap可以通过Map m = Collections.synchronizedMap(hashMap)来达到同步的效果。
9、什么时候使用Hashtable,什么时候使用HashMap
基本的不同点是Hashtable同步HashMap不是的,所以无论什么时候有多个线程访问相同实例的可能时,就应该使用Hashtable,反之使用HashMap。非线程安全的数据结构能带来更好的性能。
如果在将来有一种可能—你需要按顺序获得键值对的方案时,HashMap是一个很好的选择,因为有HashMap的一个子类 LinkedHashMap。所以如果你想可预测的按顺序迭代(默认按插入的顺序),你可以很方便用LinkedHashMap替换HashMap。反观要是使用的Hashtable就没那么简单了。同时如果有多个线程访问HashMap,Collections.synchronizedMap()可以代替,总的来说HashMap更灵活。
10、为什么Vector类认为是废弃的或者是非官方地不推荐使用?或者说为什么我们应该一直使用ArrayList而不是Vector
你应该使用ArrayList而不是Vector是因为默认情况下你是非同步访问的,Vector同步了每个方法,你几乎从不要那样做,通常有想要同步的是整个操作序列。同步单个的操作也不安全(如果你迭代一个Vector,你还是要加锁,以避免其它线程在同一时刻改变集合).而且效率更慢。当然同样有锁的开销即使你不需要,这是个很糟糕的方法在默认情况下同步访问。你可以一直使用Collections.sychronizedList来装饰一个集合。
事实上Vector结合了“可变数组”的集合和同步每个操作的实现。这是另外一个设计上的缺陷。Vector还有些遗留的方法在枚举和元素获取的方法,这些方法不同于List接口,如果这些方法在代码中程序员更趋向于想用它。尽管枚举速度更快,但是他们不能检查如果集合在迭代的时候修改了,这样将导致问题。尽管以上诸多原因,oracle也从没宣称过要废弃Vector。
我们是使用数组来保存数据,但是他的长度一旦创建,就已经确定了,当我们要动态传入穿值,数组就有些局限了,集合类就孕育而生;
所谓集合,就是来保存,盛装数据,也可称为容器类;使用的类 在java.util 包里
一、Collection(一组对立的元素)
List(有顺序)
Set(不能有重复元素)
Queue(保存队列先进先出的顺序)
二、Map(键值对(key-value))
【Collection & Map 区别】
1、Collection
每个位置只能保存一个元素
2、Map
保存的是键值对(key-value),可以通过过key,来找到value
层次关系
Collection 的父接口,可以使用foreach 进行遍历,还有迭代器iterator()方法进行遍历
最基本的集合接口,代表一组Object 集合
实现Collectiion 接口,不能包含重复的元素
判断两个对象是否相同:equals() 方法,新加入的元素和已有元素相比equals都返回false,否则拒绝添加
需要注意的是,Set 底层实现都是Map,Map 的value 为空;
HashSet
使用Hash 算法来存储元素,具有良好的存取和查找性能;
然后根据元素的Hashcode 值来决定元素在Hashset 的为位置(会重新进行排序)
Hashset 判断两个元素是否相同:
1、equals 为true
2、hashcode 相同
LinkedHashSet
和HashSet 一样,是利用hashCode 来觉得元素的存储位置,但是使用链表维护元素的次序
当遍历的时候,是按照插入的顺序遍历的
LinkedHashSet 需要维护元素的插入顺序,因此性能会略低于HashSet的性能,但是在迭代访问所有元素是性能会很高(链表适合遍历)
SortedSet
排序,compareTo() 方法进行比较,插入的元素类型需要一样,否则出现ClassCastException
TreeSet
SortedSet 的实现类,保证元素处于排序状态
EnumSet
为枚举类设计的集合类
有顺序,可重复,因此可以通过索引 来指定位置的集合元素
基于数组的List,封装了动态增长的Object[] 数组
和ArrayList 几乎一样,比较古老,线程安全
是Vector 的子类,栈 的结构(后进先出)
实现List,Deque;实现List,可以进行队列操作,可以通过索引来随机访问集合元素;实现Deque,也可当作双端队列,也可当作栈来使用
Queue 内部是队列的数据结构(先进先出),新插入的元素会在尾部;插入之后,会慢慢向顶部移动;
类似生活中的排队
除了实现Queue 接口,PriorityQueue 还对插入的元素进行重新排序(Comparator)
双端队列,Deque 可以从两端来添加啊,删除元素,因此,Deque 可以当作队列使用,也可当作栈来使用
基于数组的双端队列,和ArrayList 类似,底层都是采用一个动态可分配的Object[] 的数组
保存映射关系 的数据(key-value)
key:不允许重复,即两个任意的key 通过equals 比较返回总是false
value:存储方式类似List(可以重复,根据索引来查找)
需要注意的是:Java 中先实现Map,然后通过包装所有value 为null 来实现Set 集合
和Hashset 一样,利用key 的hashCode 值进行排序;
判断两个key 相等,equals() 方法返回true,两个hashCode 也必须相等
使用双向链表来维护key-value 的次序,该链表负责维护Map 的迭代顺序,与插入时的顺序一样
古老的Map 实现类
Properies
将Map 写入文件
SortedMap
TreeMap
是一个红黑树结构,对key 进行排序(compareTo),
自然排序:默认的排序
定制排序:也可在构造方法中传入Comparetor 对象,自定义排序
WeakHashMap
"弱引用",可能会被垃圾回收
IdentityHashMap
key 的比对 "=="
EnumMap
枚举类实现的Map
遍历的几种方式
iterator
foreach
listIterator(List 特有的)
1、HashSet的性能总是比TreeSet好(特别是最常用的添加、查询元素等操作),因为TreeSet需要额外的红黑树算法来维护集合元素的次序。只有当需要一个保持排序的Set时,才应该使用TreeSet,否则都应该使用HashSet
2、对于普通的插入、删除操作,LinkedHashSet比HashSet要略慢一点,这是由维护链表所带来的开销造成的。不过,因为有了链表的存在,遍历LinkedHashSet会更快
3、EnumSet是所有Set实现类中性能最好的,但它只能保存同一个枚举类的枚举值作为集合元素
4、HashSet、TreeSet、EnumSet都是"线程不安全"的,通常可以通过Collections工具类的synchronizedSortedSet方法来"包装"该Set集合。
SortedSet s = Collections.synchronizedSortedSet(new TreeSet(...));
1、java提供的List就是一个"线性表接口",ArrayList(基于数组的线性表)、LinkedList(基于链的线性表)是线性表的两种典型实现
2、Queue代表了队列,Deque代表了双端队列(既可以作为队列使用、也可以作为栈使用)
3、因为数组以一块连续内存来保存所有的数组元素,所以数组在随机访问时性能最好。所以的内部以数组作为底层实现的集合在随机访问时性能最好。
4、内部以链表作为底层实现的集合在执行插入、删除操作时有很好的性能
5、进行迭代操作时,以链表作为底层实现的集合比以数组作为底层实现的集合性能好
6、当要大量的插入,删除,应当选用LinkedList;当需要快速随机访问则选用ArrayList;
1、HashMap和Hashtable的效率大致相同,因为它们的实现机制几乎完全一样。但HashMap通常比Hashtable要快一点,因为Hashtable需要额外的线程同步控制
2、TreeMap通常比HashMap、Hashtable要慢(尤其是在插入、删除key-value对时更慢),因为TreeMap底层采用红黑树来管理key-value对
3、使用TreeMap的一个好处就是: TreeMap中的key-value对总是处于有序状态,无须专门进行排序操作
4、HahMap 是利用hashCode 进行查找,而TreeMap 是保持者某种有序状态
5、所以,插入,删除,定位操作时,HashMap 是最好的选择;如果要按照自然排序或者自定义排序,那么就选择TreeMap
作者: i爱慕客
链接:http://www.imooc.com/article/1080
来源:慕课网