一,集合框架图
二,集合的理解和好处
集合:就是一种容器,可以动态地把多个对象的引用放入容器中。
集合其实和数组一样都是用于存储数据和对象的,但是数组的长度是固定的,在编码时就已确定,一旦指定就不能在更改,而且数组中只能存储相同类型的元素,所以为了弥补数组的这些缺陷就出现了集合。而集合就相当于对数组的包装和改进,在使用集合存储数据时,长度不用指定,可以实现自动扩容或截断,并且在没有指定泛型之前,默认保存任意类型的元素(要注意保存之后的元素会自动转型为object类),指定泛型后,只能保存对应类型元素。所以在应用层面上,集合比数组使用方便,且操作简单,但是在底层都是一样的,有些集合容器底层还是用数组实现的。
举个例子,数组就相当于是纸杯,而集合就相当于是一个可压缩的保温杯,功能更多。
示例代码: 额外添加一个元素
数组:Animal[] animal=new Animal[2];
animal[0]=new Animal();
animal[1]=new Animal();
Animal[] newarray=Arrays.copyOf(animal,animal.length*2); //1.开辟新数组 //2.复制数组
animal=newanimal; //3.将新数组的引用赋给原数组
animal[2]=new Animal(); //4.添加元素
集合:LinkedList list=new LinkedList();
list.add(0);
list.add(1);
list.add(2);//无需开劈新数组,直接添加元素
三,集合的具体分类
因为集合中每一个容器对数据的存储方式不同,因此又对集合进行了细分,此处的存储方式称之为数据结构。
所有集合类都位于java.util包下,Java的集合类主要由两个接口派生而出:Collection和Map。
四,Collection接口
1.Collection接口的特点:
(1)不提供此接口的任何直接实现类,而是提供了更具体的子接口(如set和list),子接口中有具体的实现类。
(2)注意Map不是Collection的子接口。
(3)所有实现Collection接口的类都必须提供两个标准的构造函数:
♦ 无参数的构造函数用于创建一个空的Collection
♦ 有一个Collection参数的构造函数用于创建一个新的Collection,这个新的Collection与传入的Collection有相同的元素。
并允许用户复制一个Collection.
2.Collection接口的常见方法:
Collection中的iterator()函数,它的作用是返回一个Iterator接口。通常,我们通过Iterator迭代器来遍历集合。
ListIterator是List接口所特有的,在List接口中,通过ListIterator()返回一个ListIterator对象。
(1)Iterator接口:对collection进行迭代的迭代器。
(2)Iterator接口的方法:
boolean hasNext() 判断迭代器指向的集合中是否还有下一个元素,有则返回true。
E next() 返回下一个元素,并将指针指向下一个元素的位置。初始时指向第一个元素的前一个位置。
Void remove() 从中移除迭代器返回的最后一个元素,且每次调用next()后只能调用一次此方法。
注意:1.next()方法一般和 hasNext()方法配合使用,因为只有确保有下一个元素,才能使用next()方法返回下一个元素,否则操作不当会报错:NoSuchElementException异常。
2.在进行while遍历获取元素的过程中,只能掉用iterator接口中的remove(),不能调用集合的添加删除元素的方法,否则会报错:ConcurrentModificationException 不同步异常。
这是因为在创建迭代器对象的同时,定义了一个modcount变量,初始值为0,在每次调用next()方法时,都先要调用一下checkForComodification()方法,检查 if (modCount != expectedModCount),若在迭代的过程中添加或删除了元素(更改不会),会modcount++,这样在下次循环中在调用next()方法时,检测到(modCount != expectedModCount),接着就会报错:ConcurrentModificationException。
3.调用Iterator接口的remove()方法时,必须先调用next()方法,否则会报错:IllegalStateException异常.
4.调用next()方法后,只能调用一次remove()方法,调用多次会报错:IllegalStateException异常.
1 Iterator iterator=collection.iterator(); //返回一个Iterator接口
2
3 while (iterator.hasNext()){ //判断是否有下一个元素,有则继续执行,没有则跳出循环
4 System.out.print(iterator.next()); //将指针指向下一个元素的位置,然后将此元素返回。
5 }
Collection接口方法的代码实现:
1 package K1; 2 3 import java.util.ArrayList; 4 import java.util.Collection; 5 import java.util.Iterator; 6 7 public class CollectionTest { 8 public static void main(String[] args) { 9 //因为Collection是接口,不能new对象,所以只能创建它的实现类的对象。 10 Collection collection=new ArrayList(); 11 collection.add(1); //因为Collection接口的实现类实现了Collection接口的全部方法,所以刻意直接掉用 12 collection.add("集合"); 13 collection.add('女'); 14 collection.add(null); 15 System.out.println("集合中的元素:"+collection); 16 collection.remove(1); 17 System.out.println("集合中的元素:"+collection); 18 19 Collection col=new ArrayList(); 20 col.add("蓝"); 21 col.add("周"); 22 23 System.out.println("批量添加:"+collection.addAll(col));//批量添加 24 System.out.println("集合中的元素:"+collection); 25 System.out.println("是否包含“女”:"+collection.contains('女')); 26 System.out.println("是否包含col集合:"+collection.containsAll(col)); 27 System.out.println("批量删除:"+collection.removeAll(col));//批量添加 28 System.out.println("集合中的元素:"+collection); 29 System.out.println("集合中的个数:"+collection.size()); 30 System.out.println("集合是否为空:"+collection.isEmpty()); 31 Iterator iterator=collection.iterator(); 32 System.out.print("通过迭代器遍历集合:"); 33 while (iterator.hasNext()){ 34 System.out.print(iterator.next()+","); 35 } 36 System.out.println(); 37 collection.clear(); 38 System.out.println("清空集合"); 39 System.out.println("集合中的元素:"+collection); 40 } 41 }
代码实现:
Collection接口有两个常用的子接口,下面详细介绍。
3.List接口
(1)List接口的特点: ♦List接口继承于Collection接口,可以定义一个允许重复的有序集合。
♦有序 (此处的有序指定是元素存入到集合中的顺序和取出的顺序是相同的,因为元素插入到集合中时,会有一个索引记录它的位置,因此也可以通过索引来访问指定位置的集合元素。)
♦ 允许存储重复元素
实现List接口的集合主要有:ArrayList、LinkedList、Vector、Stack。
(2)List接口补充的方法:
注意:remove(int index)方法和remove(object o)方法在使用时,当两个方法的参数都为int型时,会出现异意,所以当参数为int型时,系统一律按调用remove(int index)方法处理。若想强行使用remove(object o),则可以先将int类型强转成object类型。如:remove(new Interger(10));
3.1 ArrayList
(1)ArrayList的特点:
ArrayList是一个允许重复的有序集合,其底层是一个大小可变的动态数组。并允许插入包括null在内的任何元素。除了实现List接口中方法,此类还提供了一些方法来操作数组的大小。(此类大概等同于Vector类,除了此类是不同步的。
size() ,isEmpty(),get(),set(),iterator()和listIterator()操作都是以固定时间运行,add()操作以分摊的固定时间运行,也就是说,添加n个元素需要O(n)时间,剩余其他的所有操作都以线性时间运行。
注意,此实现是不同步的。如果多个线程同时访问一个ArrayList实例,而其中至少一个线程从结构上修改了列表,那么必须保持外部同步。(结构上的修改是指任何添加或删除一个或多个元素的操作,或者显式调整底层数组的大小,仅仅设置元素的值不是结构上的修改。)这一般通过对 自然封装该列表的对象 进行同步操作来实现完成。如果不存在这样的对象,则应该使用Collections.synchronizedList方法将该列表“包装”起来。
ArrayList擅长于随机访问。同时ArrayList是非同步的。
(2)ArrayList补充的方法:
(3)ArrayList的继承关系:
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
(4)ArrayList的底层代码分析:
ArrayList中维护了一个Object类型的数组elementData,创建对象时初始容量设置为0,当第一次添加时,将elementData容量设置为10,该容量代表了数组的大小。随着容器中的元素不断增加,当容器中的元素个数达到10个时,容器已满,此时容器就会进行扩容,扩容后的大小为扩容前大小的1.5倍,在每次向容器中增加元素的同时都会进行容量检查,当快溢出时,就会进行扩容操作。所以如果我们明确所插入元素的多少,最好指定一个初始容量值,避免过多的进行扩容操作而浪费时间、效率。
3.2LinkedList
(1)LinkedList的特点:
LinkedList是一个允许重复的有序集合,其底层是一个双向链表。并允许插入包括null在内的任何元素。所以它除了有List接口的基本操作方法外,还额外提供了在列表的开头及结尾get,remove,insert元素的方法。
这些操作使得允许将LinkedList用做堆栈,队列或双端队列。此类实现了Deque接口,为add,poll提供了先进先出队列的操作,以及其他堆栈和双端队操作。
由于实现的方式不同,LinkedList不能随机访问,但是LinkedList添加或删除的效率却比ArrayList高。它所有的操作都是要按照双重链表的需要执行。且在列表中索引的操作可以从开头或结尾遍历列表(从靠近指定索引的一端)。
与ArrayList一样,LinkedList也是非同步的。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:
List list = Collections.synchronizedList(new LinkedList(...));
LinkedList不能随机访问,并且是非同步的。
(2)LinkedList的底层结构:
(3)LinkedList的继承关系:
public class LinkedList<E>extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, java.io.Serializable
(4)LinkedList的补充方法:
3.3 Vector
与ArrayList相似,其底层也是一个可变数组5,但是Vector是同步的。所以说Vector是线程安全的动态数组。它的操作与ArrayList几乎一样。
Vector的同步体现在:大部分方法前都用synchronize
public synchronized boolean add(E e)
public synchronized E set(int index, E element)
public synchronized E get(int index)
public synchronized void trimToSize()
public synchronized boolean isEmpty()
3.4 Stack
Stack 类表示后进先出(LIFO)的对象堆栈。它通过五个操作对类 Vector 进行了扩展 ,允许将向量视为堆栈。它提供了通常的 push 和 pop 操作,以及取堆栈顶点的 peek 方法、测试堆栈是否为空的 empty 方法、在堆栈中查找项并确定到堆栈顶距离的 search 方法。
特点
基于栈结构的集合,先进后出
Stack 类是 Vector类的子类,所以该类也是线程安全的,效率低,建议使用 Deque接口的实现类
常用方法
E push(E item) 将元素压入栈底
E pop() 将元素从栈结构中弹出,并作为此函数的值返回该对象,此方法会影响栈结构的大小
E peek() 查看堆栈顶部的对象,但不从栈中移除它。
boolean empty() 测试栈是否为空。
int search(Object o) 返回对象在栈中的位置,以 1 为基数。
4.Set接口
(1)Set接口的特点:
♦Set接口继承于Collection接口,可以定义一个不允许重复的集合。
♦ 不允许存储重复元素,只允许包含一个null值。
实现Set接口的集合的实现类:HashSet,TreeSet。
(2)Set接口的特有方法:
方法同List中继承来的,无特有方法。
(3)Set接口的遍历方式:
1,迭代器
Iterator iterator=collection.iterator();
while (iterator.hasNext()){
System.out.print(iterator.next()+",");
}
2,for循环
方法1: for (Iterator iterator=collection.iterator();iterator.hasNext(); ) {
System.out.println(iterator.next());
}
方法2: for (Object ob:collection){
System.out.println(ob);
}
for循环的好处: for循环定义的变量为内部变量,用完即释放内存
(4)Set的代码实现:
1 import java.util.HashSet; 2 import java.util.Set; 3 public class CTest { 4 public static void main(String[] args) { 5 Set set=new HashSet();//因为set是接口,所以new对象只能new他的实现类 6 set.add(2); 7 set.add(2); 8 set.add("set"); 9 set.add(null); 10 set.add(null); 11 System.out.println(set); 12 } 13 14 }
通过运行的结果可以验证,Set接口的实现类是一个不允许重复的集合。
4.1HashSet
HashSet类是Set接口的实现类,所以HashSet 是一个没有重复元素的集合。
HashSet类是无序的(无序指的是元素存入到集合中的顺序和取出的顺序是不相同的,因为HashSet底层是基于Hash算法实现的,使用了hashcode,所以HashSet中存入相应的元素的位置是固定的,并不是由元素的存入顺序决定的。)
HashSet类继承HashMap类,其底层存储结构:同HashMap,是基于哈希表实现的,这也就是HashSet不能存储相同元素,且存储元素无需的真正原因。
哈希表的实现原理(HashSet如何去重): 当HashSet类要添加一个元素时,要先调用HashCode()方法,获取该元素的哈希值,并通过一些运算获取一个整数索引index(要存放在哈希表中的位置),如果哈希表中该索引的位置上没有元素,则直接添加,如果该索引处有其他元素,则需要调用equals方法判断,要存入的元素和该索引处的元素是否相同,如果不相同,则以链表(或树状)的形式追加到已有元素的后面,如果相同,则直接覆盖,并返回false。
注意:HashSet允许使用null 元素,但只允许存储一个null值。且HashSet是非同步的。
使用HashSet需要重写带添加元素的HashCode和equals方法。
4.2LinkedHashSet
LinkedHashSet继承自HashSet,其底层是基于LinkedHashMap来实现的,是一个有序(插入和取出顺序一致),没有重复元素,非同步的集合,允许插入null值。其底层结构:同LinkedHashMap,是基于哈希表和双向链表结构。
LinkedHashSet集合同样是根据元素的hashCode值来决定元素的存储位置,但是它同时使用链表维护元素的次序。这样使得元素看起来像是以插入顺序保存的,也就是说,当遍历该集合时候,LinkedHashSet将会以元素的添加顺序访问集合的元素。
4.3TreeSet
TreeSet是一个不允许重复的无序(此处说的无序是指插入和取出不一致,但输出时是按从小到大的顺序)集合,其底层是基于TreeMap(使用的是红黑树)实现的,非同步的。TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序和定制排序,其中自然排序为默认的排序方式。当我们构造TreeSet时,若使用不带参数的构造函数,则TreeSet的使用自然比较器;若用户需要使用自定义的比较器,则需要使用带比较器的参数。
注意:
1.TreeSet集合不是通过hashcode和equals函数来比较元素的.它是通过compare或者comparaeTo函数来判断元素是否相等.compare函数通过判断两个对象的属性,相同的属性判断为重复元素,不会被加入到集合中
2.TreeSet中添加的元素必须要保证实现了自然排序或定制排序,且要保证添加的元素类型都相同,否则报错:java.lang.ClassCastException: K1.Book cannot be cast to java.lang.Comparable。
自然排序就是让添加元素的类型实现complable,并重写compareTo()方法。
定制排序就是new一个匿名排序对象,然后作为参数传入给TreeSet。
3.如何去重?通过compareTo比较方法的返回值是否为0来判断是否重复。
定制排序: eg:TreeSet set=new TreeSet(new Comparator(){
public int compare(Object o1, Object o2){
Book b1=(Book) o1;
Book b2=(Book) o2;
return Double.compare(b1.getPrice(),b2.getPrice());
}
});
自然排序:
1 import java.util.Iterator; 2 import java.util.Set; 3 import java.util.TreeSet; 4 5 6 class Book implements Comparable{ 7 String name; 8 int id; 9 int price; 10 public Book(String name,int id,int price){ 11 this.name=name; 12 this.id=id; 13 this.price=price; 14 } 15 16 @Override 17 public int compareTo(Object o) { 18 Book b=(Book) o; 19 return this.id>b.id?1:(this.id==b.id?0:-1); 20 } 21 } 22 public class CTest { 23 public static void main(String[] args) { 24 Set set=new TreeSet();//因为set是接口,所以new对象只能new他的实现类 25 set.add(new Book("sdv",1,20)); 26 set.add(new Book("sre",3,50)); 27 set.add(new Book("ngh",2,40)); 28 set.add(new Book("hry",4,20)); 29 30 Iterator iterator=set.iterator(); 31 while (iterator.hasNext()){ 32 Book book=(Book)iterator.next(); 33 System.out.println(book.name+" "+book.id+" "+book.price); 34 } 35 } 36 37 }
五.Map接口
(1) Map接口的特点:
♦它是由一系列键值对组成的无序集合,提供了key到Value的映射。在Map中它保证了key与value之间的一一对应关系。也就是说一个key只能对应一个value,它不能存在相同的key值,当然value值可以相同。
♦ 同时它也没有继承Collection。
♦它的底层结构是由哈希表实现的,所以当存储的元素的key值相同时,后者会覆盖前者。
注意:key可以存储null,value也可以存储null。
(2)Map接口的方法:
(3)遍历方式:
方式一:通过调用entrySet方法
Set set=map.entrySet();//返回所有的关系
Iterator iterator=set.iterator();
while (iterator.hasNext()){
Map.Entry entry=(Entry) interator.next();
entry.getKey();
entry.getValue();
}
方式二:通过调用KeySet方法
Set set=map.keySet();//得到所有键值
for (Object key:set) {
System.out.println(key+"--"+map.get(key));
}
代码实现:
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class CollectionMap {
public static void main(String[] args) {
Map map=new HashMap();
map.put("1","春");
map.put("2","夏");
map.put("3","秋");
map.put("4","冬");
map.put("4","赵");
map.put("5","冬");
map.put(null,null);
System.out.println(map);//map底层重写了ToString方法
Set set=map.entrySet();//返回所有的关系
Iterator iterator=set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("集合中元素的个数:"+map.size());//打印集合中元素的个数
System.out.println("是否有key值为1的键值对:"+map.containsKey("1"));//查找集合中是否有key值为1的键值对
System.out.println("是否有value值为"春"的键值对"+map.containsValue("春"));//查找集合中是否有value值为"春"的键值对
System.out.println("删除值为2的键值对,并返回value值:"+map.remove("2"));//删除值为2的键值对
map.clear(); //无返回值
System.out.println("集合是否为空:"+map.isEmpty());
System.out.println(map);//map底层重写了ToString方法
}
}
【面试题】:通过size方法和isEmpty方法判断元素个数是否为0,谁效率高?
答:效率一样,因为底层都是用变量size判断的。
1.1HashMap
HashMap是无序的
以哈希表数据结构实现,查找对象时通过哈希函数计算其位置,它是为快速查询而设计的,其内部定义了一个hash表数组(Entry[] table),元素会通过哈希转换函数将元素的哈希地址转换成数组中存放的索引,如果有冲突,则使用散列链表的形式将所有相同哈希地址的元素串起来,可能通过查看HashMap.Entry的源码它是一个单链表结构。
底层结构:1.7:数组+链表
1.8:数组+链表+红黑树
源码分析jdk1.8:HashMap中维护了Node类型的数组table,当HashMap创建对象时,只是对loadFactor初始化为0.75 (追求时间和空间的同步),table还是保持默认值null
当第一次添加时,将初始化table数组的容量为16,临界值为12,
每次添加调用putVal方法:
1,获取key的二次哈希值并进行取与运算(&),得出存放的位置
2,判断该位置上是否有元素,如果没有直接存放,如果有,则需进行判断,如果和当前元素相等,直接覆盖,如果不相等,则根据结构(链状或树状(红黑树))的对应方式判断是否有相等元素,有则覆盖,没有则添加。
3,将size更新,并判断是否超过临界值,如果超过了,则需要进行2倍重新扩容(resize()),并打乱原来的顺序,重新进行排列存放。
4,当一个桶中的链表的节点数>=8时,系统也会调用resize()方法进行2倍扩容,并打乱原来的顺序,重新进行排列存放。如果桶中的链表的节点数>=8,且桶的总个数(table数组的容量)>=64,那么此链表结构变成红黑树结构。(由于树的访问速度比链表快,可能就有人疑惑为什么不直接使用树,而是在节点大于8时,才转化为树,这是因为一个树节点占用的空间是链表节点的两倍,虽然树访问速度快,但树占空间,且当节点数小于8时两者的查找速度是差不多的,于是就取了个折中的方法,先采用链状结构,当节点大于8时,在转为树状结构,为什么取8作为阈值,因为红黑树的平均查找长度是log(n),链表的平均查找长度为n/2,当节点数等于8时,红黑树的平均查找长度为log(8)=3,而链表的平均查找长度为8/2=4,此时由于查找速度的原因,很有必要将链表转为红黑树)
5,如果当一个桶中的链表的节点数<=6时,桶中的红黑树就会转化回为链表。
jdk1.7和jdk1.8的区别:
1,jdk1.7:创建HashMap对象时,则初始table数组容量为16
jdk1.8:创建HashMap对象时,没有初始table数组,仅仅只是初始加载因子。当第一次添加时才会初始化table数组,容量为16.
2,jdk1.7:table类型为Entry
jdk1.8:table类型Node(为了可以转换为树状结构(TreeNode))
3,jdk1.7:数组+链表,不管链表的总结点数为多少都不会变成树结构。
jdk1.8:数组+链表+红黑树
注意:使用HashMap要求添加元素的key重写了hashCode和equals方法。
1.2LinkedHashMap(HashMap的子类)
LinkedHashMap是继承于HashMap类,是基于HaspMap和双向链表来实现的。
LinkedHashMap是有序的,分为插入有序和访问有序两种(在new对象传入参数来设置,fasle:插入有序,true:访问有序),且默认为插入顺序,如果是访问有序,那put和get操作已存在的Entry时,都会把Entry移动到双向链表的表尾(其实是先删除在插入)。
LinkedHashMap存取数据,还是跟HashMap一样使用的Entry[]的方式,双向链表只是为了保证顺序。
注意:LinkedHashMap是线程不安全的。
举例:开始时,HashMap(哈希表)中有Entry1,Entry2,Entry3三个元素,并设置LinkedHashMap为访问有序,则更新Entry1时,会把Entry1从双向链表中删除,然后再把Entry1添加到双向链表的表尾,而Entry1在HashMap(哈希表)结构中的存储位置没有变化。过程如下图:
2.TreeMap
TreeMap 是一个有序的(大小顺序,而非插入顺序)key-value集合,非同步,基于红黑树(Red-Black tree)实现,每一个key-value节点作为红黑树的一个节点。TreeMap存储时会进行排序的,会根据key来对key-value键值对进行排序,其中排序方式也是分为两种,一种是自然排序,一种是定制排序,具体取决于使用的构造方法。
自然排序:TreeMap中添加的key元素类型必须实现Comparable接口,并重写compareTo(),并且所有的key都应该是同一个类的对象,否则会报ClassCastException异常。
定制排序:定义TreeMap时,创建一个comparator比较器对象,该对象对所有的treeMap中所有的key值进行排序,采用定制排序的时候不需要TreeMap中所有的key必须实现Comparable接口。
TreeMap判断两个元素相等的标准:两个key通过compareTo()方法返回0,则认为这两个key相等。
如果使用自定义的类来作为TreeMap中的key值,且想让TreeMap能够良好的工作,则必须重写自定义类中的equals()方法,TreeMap中判断相等的标准是:两个key通过equals()方法返回为true,并且通过compareTo()方法比较应该返回为0。
3.Hashtable
Hashtable继承自Dictionary类,Dictionary类是一个抽象类,在jdk1.0中提供用来存储键值对,作用和Map类相似,Hashtable类的数据结构与HMap是相似的。依然是采用“链地址法”实现的哈希表,保存实际数据的,依然是Entry对象。
要注意,Hashtable是线程安全的,且key和value都不能为null
六,常见面试题
1.ArrayList和LinkedList的区别
(1)ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
(2)对于随机访问get和set,ArrayList绝对优于LinkedList,因为LinkedList要移动指针。
(3)对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
这一点要看实际情况的。若只对单条数据插入或删除,ArrayList的速度反而优于LinkedList。但若是批量随机的插入删除数据,LinkedList的速度大大优于ArrayList. 因为ArrayList每插入一条数据,要移动插入点及之后的所有数据。
2. HashMap和HashTable的区别
相同点:
(1)都实现了Map、Cloneable、java.io.Serializable接口。
(2)都是存储"键值对(key-value)"的散列表,而且都是采用拉链法实现的。
不同点:
(1)历史原因:HashTable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现 。
(2)同步性:HashTable是线程安全的,也就是说是同步的,而HashMap是线程序不安全的,不是同步的 。
(3)对null值的处理:HashMap的key、value都可为null,HashTable的key、value都不可为null 。
(4)基类不同:HashMap继承于AbstractMap,而Hashtable继承于Dictionary。
Dictionary是一个抽象类,它直接继承于Object类,没有实现任何接口。Dictionary类是JDK 1.0的引入的。虽然Dictionary也支持“添加key-value键值对”、“获取value”、“获取大小”等基本操作,但它的API函数比Map少;而且Dictionary一般是通过Enumeration(枚举类)去遍历,Map则是通过Iterator(迭代M器)去遍历。 然而由于Hashtable也实现了Map接口,所以,它即支持Enumeration遍历,也支持Iterator遍历。
AbstractMap是一个抽象类,它实现了Map接口的绝大部分API函数;为Map的具体实现类提供了极大的便利。它是JDK 1.2新增的类。
(5)支持的遍历种类不同:HashMap只支持Iterator(迭代器)遍历。而Hashtable支持Iterator(迭代器)和Enumeration(枚举器)两种方式遍历。
3.Iterator与ListIterator的区别
我们在使用List,Set的时候,为了实现对其数据的遍历,我们经常使用到了Iterator(迭代器)。使用迭代器,你不需要干涉其遍历的过程,只需要每次取出一个你想要的数据进行处理就可以了。但是在使用的时候也是有不同的。List和Set都有iterator()来取得其迭代器。对List来说,你也可以通过listIterator()取得其迭代器,两种迭代器在有些时候是不能通用的,Iterator和ListIterator主要区别在以下方面:
(1)ListIterator有add()方法,可以向List中添加对象,而Iterator不能
(2)ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。Iterator就不可以。
(3)ListIterator可以定位当前的索引位置,nextIndex()和previousIndex()可以实现。Iterator没有此功能。
(4)都可实现删除对象,但是ListIterator可以使用set()方法实现对象的修改。Iierator仅能遍历,不能修改。
因为ListIterator的这些功能,可以实现对LinkedList等List数据结构的操作。其实,数组对象也可以用迭代器来实现。
4.HashSet、LinkedHashSet、TreeSet比较
Set接口
Set不允许包含相同的元素,如果试图把两个相同元素加入同一个集合中,add方法返回false。
Set判断两个对象相同不是使用==运算符,而是根据equals方法。也就是说,只要两个对象用equals方法比较返回true,Set就不会接受这两个对象。
HashSet
HashSet有以下特点:
-> 不能保证元素的排列顺序,顺序有可能发生变化。
-> 不是同步的。
-> 集合元素可以是null,但只能放入一个null。
当向HashSet结合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据 hashCode值来决定该对象在HashSet中存储位置。简单的说,HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode()方法返回值也相等。
注意,如果要把一个对象放入HashSet中,重写该对象对应类的equals方法,也应该重写其hashCode()方法。其规则是如果两个对象通过equals方法比较返回true时,其hashCode也应该相同。另外,对象中用作equals比较标准的属性,都应该用来计算 hashCode的值。
LinkedHashSet
LinkedHashSet集合同样是根据元素的hashCode值来决定元素的存储位置,但是它同时使用链表维护元素的次序。这样使得元素看起来像是以插入顺序保存的,也就是说,当遍历该集合时候,LinkedHashSet将会以元素的添加顺序访问集合的元素。
LinkedHashSet在迭代访问Set中的全部元素时,性能比HashSet好,但是插入时性能稍微逊色于HashSet。
TreeSet类
TreeSet是SortedSet接口的唯一实现类,TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序和定制排序,其中自然排序为默认的排序方式。向TreeSet中加入的应该是同一个类的对象。
TreeSet判断两个对象不相等的方式是两个对象通过equals方法返回false,或者通过CompareTo方法比较没有返回0。
自然排序
自然排序使用要排序元素的CompareTo(Object obj)方法来比较元素之间大小关系,然后将元素按照升序排列。
Java提供了一个Comparable接口,该接口里定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现了该接口的对象就可以比较大小。obj1.compareTo(obj2)方法如果返回0,则说明被比较的两个对象相等,如果返回一个正数,则表明obj1大于obj2,如果是负数,则表明obj1小于obj2。如果我们将两个对象的equals方法总是返回true,则这两个对象的compareTo方法返回应该返回0。
定制排序
自然排序是根据集合元素的大小,以升序排列,如果要定制排序,应该使用Comparator接口,实现 int compare(T o1,T o2)方法。
5,ArrayList和Vector的区别?
1) Vector的方法都是同步的(Synchronized),是线程安全的(thread-safe),而ArrayList的方法不是,由于线程的同步必然要影响性能,因此,ArrayList的性能比Vector好。
2) 当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList只增加50%的大小,这样,ArrayList就有利于节约内存空间。
6、Collection 和 Collections区别
(1)java.util.Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set。
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set