【Java集合】Java集合类中 Map集合

Map

首先观察Map接口的定义

public interface Map<K,V>

在Map接口中有如下常用方法:

Map本事是一个接口,要使用Map需要通过子类进行对象实例化。

Map接口的常用子类有如下四个:HashMap、HashTable、TreeMap、ConcurrentHashMap。

 

HashTable子类

JDK1.0提供有三大主要类:VectorEnumerationHashtableHashtable是最早实现这种二元偶对象数据结构,后期 的设计也让其与Vector一样多实现了Map接口而已。

观察HashTable

public class Test {
    public static void main(String[] args) {
        Map<Integer,String> map = new Hashtable<>();
        map.put(1,"Hello");
        //key重复
        map.put(1,"Hello");
        map.put(3,"秋招");
        map.put(2,"Future");
        System.out.println(map);
    }
}


{3=秋招, 2=Future, 1=Hello}

Process finished with exit code 0

Hashtable的原理和HashMap基本一致。

HashMap和Hashtable的区别

一、Hashtable是线程安全的,方法时synchronized,适合在多线程环境中使用,效率较低
       HashMap是线程不安全的,方法不是synchronized,适合在单线程环境中使用,效率较高
      所以如果要在多线程下使用的话,需要手动同步HashMap,Collections.synchronizedMap()

      PS:Hashtable的效率较低的原因?
      在线程竞争激烈的情况下Hashtable的效率非常低下。因为当一个线程访问HashTable的同步方法时,访问其它同步方法的线程就可能        会进入阻塞状态或者轮训状态。如果线程1使用put方法进行添加元素,线程2不但不能使用put方法添加元素,并且也不能使用get方法        来获取元素,所以竞争越激烈效率越低。

二、HashMap的Key和Value都可以为null值,而HashTable的Key和Value都不允许有Null值。

三、HashMap中数组的默认大小是16,而且一定是2的倍数,扩容后的数组长度是之前数组长度的2倍。HashTable中数组默认大小是11,         扩容后的数组是之前数组长度的2倍+1。

四、哈希值的使用不同。

//HashMap重新计算hash值,而且用&代替求模:
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
static int hash(Object x){
    int h = x.hashCode();
    h += ~(h << 9);
    h ^= (h >>> 14);
    h += (h << 4);
    h ^= (h >>> 10);
    return h;
}

static int indexFor(int h, int length){
    return h & (length - 1);//HashMap的表长永远是2 ^n
}



//HashTable直接使用对象的HashCode值
int hahs = key.hashCode();//注意区分二者的hash值
int index = (hash & 0x7FFFFFFF) % tab.length;

五、判断是否还有某个键

       在HashMap中,null可以作为键,这样的键就只有一个;可以有一个或者多个键所对应的值为null。当get()方法返回null值时,既可以表示HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能用get()方法来判断HashMap中是否存在某个键,而应该用containsKey()方法来判断Hashtable的键值都不能为null,所以可以用get()方法来判断是否含有某个键。

 

个人建议:多考虑使用HashMap,Hashtable不常用。

 

Map集合使用Iterator输出

Map接口与Collection接口不同,Collection接口有iterator()方法可以很方便的取得Iterator对象来输出,而Map接口本 身并没有此方法。下面我们首先来观察Collection接口与Map接口数据保存的区别:

在Map接口里面有一个重要的方法,将Map集合转为Set集合:

public Set<Map.Entry<K, V>> entrySet();

Map要想调用Iterator接口输出,走的是一个间接使用的模式,如下图:

通过Iterator输出Map集合

public class Test {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "Hello");
        map.put(2, "Future");
        map.put(3, "秋招,I am coming!");
        //1.将Map集合转为Set集合
        Set<Map.Entry<Integer,String>> set = map.entrySet();
        //2.获取Iterator对象
        Iterator<Map.Entry<Integer,String>> iterator = set.iterator();
        //3.输出
        while (iterator.hasNext()){
            Map.Entry<Integer,String> entry = iterator.next();
            System.out.println(entry.getKey()+" = " + entry.getValue());
        }
    }
}


1 = Hello
2 = Future
3 = 秋招,I am coming!

Process finished with exit code 0

 

关于Map中Key的说明

在之前使用Map集合的时候使用的都是系统类作为key(Integer,String)。实际上也可采用自定义类作为key。这 个时候一定要记得覆写Object类的hashCode()equals()方法。

 

TreeMap子类

TreeMap是一个可以排序的Map子类,它是按照Key的内容排序的。

观察TreeMap的使用

public class Test {
    public static void main(String[] args) {
        Map<Integer,String> map = new TreeMap<>();
        map.put(2,"C");
        map.put(1,"A");
        map.put(3,"B");
        System.out.println(map);
    }
}


{1=A, 2=C, 3=B}

Process finished with exit code 0

这个时候的排序处理依然按照的是Comparable接口完成的。

结论:有Comparable出现的地方,判断数据就依靠compareTo()方法完成,不再需要equals()与hashCode()

 

HashMap和TreeMap的区别

  1. 实现方式的区别:HashMap基于哈希表实现。TreeMap基于红黑树实现。
  2. TreeMap能够把它保存的记录根据键排序。
  3. HashMap:适用于在Map中插入、删除和查找元素。
  4. TreeMap:适用于按自然顺序或自定义顺序遍历键(Key)

HashMap通常比TreeMap快一点。

 

Map集合小结:

  1. Collection保存数据的目的一般用于输出(Iterator)Map保存数据的目的是为了根据key查找,找不到返回 null。

  2. Map使用Iterator输出(Map.Entry的作用)

  3. HashMap数据结构一定要理解(链表与红黑树)HashMapHashtable区别

 

讲一下集合中的Fail-Fast机制

举个栗子:

       假设存在两个线程(线程1、线程2),线程1通过Iterator在遍历集合A中的元素,在某个时候,线程2修改了集合A中的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就会抛出一个ConcurrentModificationException异常,从而产生Fail-Fast机制。

产生的原因:

       当调用容器的iterator()方法返回Iterator对象时,把容器中包含对象的个数赋值给了一个变量expectedModCount,在调用next()方法时,会比较expectedModCount与容器中实际对象的个数是否相等,若二者不相等,则会抛出ConcurrentModification异常。

      如果在遍历集合的同时,需要删除元素的话,可以用iterator里面的remove()方法删除元素。

 

 

HashMap与ConcurrentHashMap单独说明

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值