zoukankan      html  css  js  c++  java
  • [Java语言] HashMap,HashSet,Hashtable,Vector,ArrayList 的关系 <转>

    这么几个比较常用的但是比较容易混淆的概念同出于 java.util 包。本文仅作几个类的浅度解析。
    (本文基于JDK1.7,源码来自openjdk1.7。)

    1. ├── Collection
    2. │ ├── List
    3. │ │ ├── ArrayList
    4. │ │ ├── Vector
    5. │ │ └── LinkedList and so on;
    6. │ Set
    7. │ ├── HashSet
    8. │ └── LinkedHashSet and so on;
    9. └── Map
    10. ├── Hashtable
    11. ├── HashMap
    12. └── WeakHashMap and so on;
    复制代码

    Collection和Map

    按理来说和这两者没有什么特别关系。然而本文中仍然将这两个混在一起:

    第一,HashSet,HashMap,Hashtable长得太像了,没有理由不写再一起。 第二,HashSet是个Set,其实骨子里是个Map。 List

    继承List的类,泛型为Integer时,注意和其他的类型不同。(因为Index是Integer)

    线程安全

    简单来说,List是个一维数组。Vector和ArrayList区别在于:

    Vector是个线程安全的,而ArrayList不是。这点看源码可以看出来,Vector中各种synchronized,甚至连size属性都丧心病狂地synchronized了。

    同样,Iterator指定Vector和ArrayList,如果中间发生变化则会抛出ConcurrentModificationException异常。
    不仅仅是List,很多地方的Iterator都有这个异常,

    自增大小

    ArrayList没办法设置自增大小,但是仍然可以自增。
    Vector可以设置自增大小。

    ArrayList的自增函数:

    Vector的自增函数:

    1. private void grow(int minCapacity) {
    2. // overflow-conscious code
    3. int oldCapacity = elementData.length;
    4. int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
    5. capacityIncrement : oldCapacity);
    6. //在这里,如果没有设置自增大小的话,那么默认自增大小则是原来的Vector大小。
    7. if (newCapacity - minCapacity < 0)
    8. newCapacity = minCapacity;
    9. if (newCapacity - MAX_ARRAY_SIZE > 0)
    10. newCapacity = hugeCapacity(minCapacity);
    11. elementData = Arrays.copyOf(elementData, newCapacity);
    12. }
    复制代码

    ArrayList自增函数:

    1. private void grow(int minCapacity) {
    2. // overflow-conscious code
    3. int oldCapacity = elementData.length;
    4. int newCapacity = oldCapacity + (oldCapacity >> 1);
    5. //新长度则是旧长度的大小加上一个移位运算(一半的大小)。
    6. if (newCapacity - minCapacity < 0)
    7. newCapacity = minCapacity;
    8. if (newCapacity - MAX_ARRAY_SIZE > 0)
    9. newCapacity = hugeCapacity(minCapacity);
    10. // minCapacity is usually close to size, so this is a win:
    11. elementData = Arrays.copyOf(elementData, newCapacity);
    12. }
    复制代码

    所以可以简单的认为Vector默认自增一倍的长度,ArrayList则是默认自增一半的长度。

    同样,对他们的源码分析:
    Vector可以设置自增大小,而ArrayList则无法设置(没有这个构造方法)。

    1. public Vector(int initialCapacity, int capacityIncrement)
    复制代码

    最后,可以 List list=new ArrayList<>();,然后list=new Vector<>();,使用基类List可以实现ArrayList和Vector的快速赋值。

    Map

    Map可以当成是简单的“Key-Value”数据库,例如memcached和redis。

    Hashtable基于Directory,有种说法是Directory已经过时,所以更推荐使用Map。

    线程安全

    Hashtable也是线程安全的,和Vector一样,连size函数都丧心病狂地同步了。

    1. public synchronized int size() {
    2. return count;
    3. }
    复制代码

    HashMap不是线程安全的,当然也可以用其他的黑科技实现同步,例如SynchronizedMap和ConcurrentHashMap。
    那个以后再说……

    空值

    HashMap允许在Key和Value中都出现null值。例如:

    1. v.toString()
    2. {null=null, 1=66, 2=null, 3=4d, 4=0f, 5=null}
    复制代码

    而Hashtable则不允许K/V的任何一个出现null值,但是允许空字符串。

    1. Hashtable<String,String> v=new Hashtable<String,String>();
    2. v.put("3s",null);
    复制代码

    直接报错:java.lang.NullPointerException。
    这个和内部实现有关,Hashtable需要用到hashCode,所以必须要确保K/V不为null。

    相关代码写死在源码里:

    1. public synchronized V put(K key, V value) {
    2. // Make sure the value is not null
    3. if (value == null) {
    4. throw new NullPointerException();
    5. }
    6. //下面对hashCode做点事情。
    复制代码

    Set

    HashSet本质上是一个Collection,类似于List,是列表/集合,不是K-V的Map,但是它骨子里是一个HashMap……

    这么说可能会更易于理解:
    HashSet对外是“类”的集合,实际上是内部维护了一个HashMap进行实现。

    实际上存储的是两个:hashCode和类本身(字符串/自定义类等)。

    HashSet进行add的时候,会先进行验证hashCode:
    (HashSet进行add操作实际上是对Map的put操作)

    1. public V put(K key, V value) {
    2. if (table == EMPTY_TABLE) {
    3. inflateTable(threshold);
    4. }
    5. if (key == null)
    6. return putForNullKey(value);
    7. int hash = hash(key);//本函数里有hashCode
    8. int i = indexFor(hash, table.length);
    9. for (Entry<K,V> e = table[i]; e != null; e = e.next) {
    10. Object k;
    11. if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
    12. V oldValue = e.value;
    13. e.value = value;
    14. e.recordAccess(this);
    15. return oldValue;
    16. }
    17. }
    18. modCount++;
    19. addEntry(hash, key, value, i);
    20. return null;
    21. }
    复制代码

    一个对HashSet的自定义类的操作:

    1. class Name
    2. {
    3. private String first;
    4. private String last;
    5. public Name(String first, String last)
    6. {
    7. this.first = first;
    8. this.last = last;
    9. }
    10. public boolean equals(Object o)
    11. {
    12. if (this == o)
    13. {
    14. return true;
    15. }
    16. if (o.getClass() == Name.class)
    17. {
    18. Name n = (Name)o;
    19. return n.first.equals(first)
    20. && n.last.equals(last);
    21. }
    22. return false;
    23. }
    24. }
    25. public class HashSetTest
    26. {
    27. public static void main(String[] args)
    28. {
    29. Set<Name> s = new HashSet<Name>();
    30. s.add(new Name("abc", "123"));
    31. System.out.println(
    32. s.contains(new Name("abc", "123")));
    33. }
    34. }
    复制代码

    代码来自java中HashSet详解

    粗看上去是会返回true的,实际返回false。因为 HashSet 判断两个对象相等的标准除了要求通过 equals() 方法比较返回 true 之外,还要求两个对象的 hashCode() 返回值相等。而上面程序没有重写 Name 类的 hashCode() 方法,两个 Name 对象的 hashCode() 返回值并不相同,因此 HashSet 会把它们当成 2 个对象处理,因此程序返回 false。如果想返回true,需要重写equals方法和hashCode方法。

    至于hashCode,就是另一篇文章才解释得清的了。

    集合操作

    Set其实做集合操作更加简单。例如:

    1. import java.util.*;
    2. public class SetOperation {
    3. public static void main(String[] args) {
    4. Set<Integer> result = new HashSet<Integer>();
    5. Set<Integer> set1 = new HashSet<Integer>(){{
    6. add(1);
    7. add(3);
    8. add(5);
    9. }};
    10. Set<Integer> set2 = new HashSet<Integer>(){{
    11. add(1);
    12. add(2);
    13. add(3);
    14. }};
    15. result.clear();
    16. result.addAll(set1);
    17. result.retainAll(set2);
    18. System.out.println("交集:"+result);
    19. result.clear();
    20. result.addAll(set1);
    21. result.removeAll(set2);
    22. System.out.println("差集:"+result);
    23. result.clear();
    24. result.addAll(set1);
    25. result.addAll(set2);
    26. System.out.println("并集:"+result);
    27. }
    28. }
    复制代码

    输出:

    1. 交集:[1, 3]
    2. 差集:[5]
    3. 并集:[1, 2, 3, 5]
    复制代码

    参考文档

    hashSet中的equals方法和hashCode方法

    《http://bbs.it-home.org/thread-53825-1-1.html?ref=myread》

  • 相关阅读:
    Restful api 返回值重复的问题
    fastDFS遇到的并发问题recv cmd: 0 is not correct, expect cmd: 100
    忽略警告@SuppressWarnings的用法
    Mysql 一些命令记录
    python 操作excel实现替换特定内容
    python 提取目录中特定类型的文件
    python使用tkinter无法获取输入框的值
    python使用tkinter无法给顶层窗体的输入框设定默认值
    游戏基础知识第一弹
    python 3.7.4 安装 opencv
  • 原文地址:https://www.cnblogs.com/xiadongqing/p/5251528.html
Copyright © 2011-2022 走看看