一、list和Set都是Collection的子接口。主要有下面两点区别
1. List是列表(接口),是可以允许出现重复值的,
2. Set是集合,不允许出现重复值
以下是JDK中的英文解释
* Unlike sets, lists typically allow duplicate elements. More formally,
* lists typically allow pairs of elements <tt>e1</tt> and <tt>e2</tt>
* such that <tt>e1.equals(e2)</tt>, and they typically allow multiple
* null elements if they allow null elements at all. It is not inconceivable
* that someone might wish to implement a list that prohibits duplicates, by
* throwing runtime exceptions when the user attempts to insert them, but we
* expect this usage to be rare.<p>
List JDK中文解释:
有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。
与 set 不同,列表通常允许重复的元素。更确切地讲,列表通常允许满足 e1.equals(e2) 的元素对 e1 和 e2,并且如果列表本身允许 null 元素的话,通常它们允许多个 null 元素。难免有人希望通过在用户尝试插入重复元素时抛出运行时异常的方法来禁止重复的列表,但我们希望这种用法越少越好。
注意:尽管列表允许把自身作为元素包含在内,但建议要特别小心:在这样的列表上,equals 和 hashCode 方法不再是定义良好的。
Set JDK中文解释:
一个不包含重复元素的 collection。更确切地讲,set 不包含满足 e1.equals(e2)
的元素对 e1
和 e2
,并且最多包含一个 null 元素。正如其名称所暗示的,此接口模仿了数学上的 set 抽象。
在所有构造方法以及 add、equals 和 hashCode 方法的协定上,Set 接口还加入了其他规定,这些规定超出了从 Collection 接口所继承的内容。出于方便考虑,它还包括了其他继承方法的声明(这些声明的规范已经专门针对 Set 接口进行了修改,但是没有包含任何其他的规定)。
对这些构造方法的其他规定是(不要奇怪),所有构造方法必须创建一个不包含重复元素的 set(正如上面所定义的)。
注:如果将可变对象用作 set 元素,那么必须极其小心。如果对象是 set 中某个元素,以一种影响 equals 比较的方式改变对象的值,那么 set 的行为就是不确定的。此项禁止的一个特殊情况是不允许某个 set 包含其自身作为元素。
Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
Map
├Hashtable
├HashMap
└WeakHashMap
二、ArrayList与Vector 的区别:
Vector ArrayList同样是数组的封装
ArrayList与Vector 是差不多的,Vector是同步的,而ArrayList是不同步的,可以使用下面的方法来得到一个同步了的List
List list = Collections.synchronizedList(new ArrayList());
《Thinking in Java》上很明确地说了:Vector是一个java1.0/1.1遗留的类,是为了与老代码兼容而保留的。有了ArrayList以后,应该尽量使用ArrayList而不是Vector。
Vector可以同步,而ArrayList不能同步,是不对的
正确的说法应该是:ArrayList(包括所有新一代的容器)可以选择性地使用同步,不需要时就不使用,而不是像Vector那样没有选择。
vector的同步是指方法上的同步,也就是说在他自己的方法里进行了同步,两个线程同时调用一个方法的时候不会出现问题,但如果一个线程里要想多次操作vector而且不想在这个过程中被其他线程打扰的话,仍然需要手动加锁来控制.
所以除了必须要写java1.0/1.1兼容的代码,否则完全可以不考虑Vector
以前这么写List list = new Vector();
现在这么写List list = new ArrayList();
用ArrayList 代替了Vector 因为后者的性能比前者好;
但是两个都是实现了List借口的
同理Map map = new HashTable();(以前)
Map map = new HashMap();(现在)
HashMap和Hashtable主要有以下三点不同:
1. 第一个不同之处在于它们的继承关系有所不同。
public class Hashtable extends Dictionary
public class HashMap extends AbstractMap
由上面的代码可以看出Hashtable是基于陈旧的Dictionary类的。在Java 1.2引入Map借口后Hashtable也改进为可以实现 Map。HashMap是Map接口的一个实现,继承于较新的AbstractMap类。 HashMap可以算作是Hashtable的升级版本,整体上HashMap对Hashtable类优化了代码。比如说,消除了hardcoding,增加了code reuse等等。当然这点不同,并不会对我们选择使用哪个产生影响。
2. null值问题
在HashMap中,null可以作为key,这样的key只有一个,可以有一个或多个key所对应的value为null。而在Hashtable中,null不可以作为key,也不可以作为value。否则会抛出java.lang.NullPointerException。Hashtable的put方法的源代码如下:
public synchronized Object put(Object key, Object value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
Object old = e.value;
e.value = value;
return old;
}
}
}
从这段代码可以看出,在调用Hashtable的put方法时,首先会对put的value是否为空进行判断,如果为空,则会抛出NullPointerException,处理终止。如果对Hashtable中put一个null的key,代码在执行到 int hash = key.hashCode(); 的时候,因为key对象为空,同样会抛出NullPointerException,处理终止。所以在编码时,
诸如 hashtable.put("1", null);//value为null
或者 hashtable.put(null, "one");//key为null 都是错误的。
当然,平时在编码时,put的key或者value不会那么明显就是个直接的字符串或者其他对象类型。它们可能是从DB或DTO中取得的或者通过一些计算或转化得到的。这个时候要注意put的key或value是否有可能为null,如果有null的可能性,应该先进行判断并处理。以避免异常的产生。
另外还要注意一个问题,因为Hashmap可以存入null。所以当get()方法返回null值时,既可以表示 Hashmap中没有该key,也可以表示该key所对应的value为null。因此,在Hashmap中不能由get()方法来判断Hashmap中是否存在某个key, 而应该用containsKey()方法来判断。
看下面这段代码
public static void main(String[] args) {
Map map = new HashMap(); map.put("1", null);
System.out.println(map.get("1")); System.out.println(map.get("2"));
System.out.println(map.containsKey("1"));
System.out.println(map.containsKey("2")); }
输出结果
null
null
true
false
从上面这段试验代码的输出结果可以看出,如果通过get方法是无法判断key值1,2是否存在的,因为它们的返回的值都是null。正确的做法应该使用containsKey()方法来判断,从输出结果看,前者为true,后者为false。
3.Hashtable的方法是同步的,而Hashmap方法不是。
Hashtable是synchronized,你可以不用采取任何特殊的行为就可以在一个多线程的应用程序中用一个Hashtable,而Hashmap的读写是unsynchronized, 在多线程的环境中要注意使用。这两者的不同是通过在读写方法上加synchronized关键字来实现的.有兴趣的话,大家可以看下Hashtable的源代码,在需要线程安全的方法前都加上了synchronized关键字。下面举了最常用的几个方法的定义。
hashtable
public synchronized Object get(Object key)
public synchronized Object put(Object key, Object value)
public synchronized Object remove(Object key)
public synchronized void clear()
从Hashmap的源代码可以看出没有这个关键字
hashMap
public V put(K key, V value)
public V get(Object key)
有人可能会问, 既然能synchronized,能线程安全好啊。为什么不要呢,这里其实还是一个效率的问题
。对于线程安全的方法,系统要进行加锁,减锁操作。性能会有很大的影响。由于很多程序是在单线程或者说是线程安全的情况下工作的, 所以用synchronized就显得有些多余了。 当既要同步又要可以让null作为键或者值的时候,一个简便的方法就是利用Collections类的静态的 synchronizedMap()方法,
Map synMap = Collections.synchronizedMap(map); 它创建一个线程安全的Map对
象,并把它作为一个封装的对象来返回。
4. 总结
通过上述的内容,我们了解了Hashmap和Hashtable的几个主要的不同点。 Hashmap可以使用null作为key和value,而Hashtable不行。Hashtable是同步的,Hashmap是异步的。但是,因为在需要时,Hashmap可以利用Collections类的静态的 synchronizedMap()方法来实现同步,其次Hashmap的功能比Hashtable的功能更多,而且它不是基于一个陈旧的类的,所以才有人认为,在各种情