HashMap与HashTable~

这一块主要整理一下HashMap与HashTable之间个别重要的点和细节以及这两者之间的异同点!

首先简单介绍一下何谓线程安全?

如果我们的代码所在的进程中有多个线程在同时运行,而且这些线程可能同时运行这段代码。如果每次的运行结果和单线程的运行结果是一样的,而且其他的变量的值也和预期的是一样的,那就是线程安全的。

一、HashMap

1、HashMap线程不安全体现在哪里?

HashMap的默认容量是16,当在进行put的时候,插入的元素超过了容量(由加载因子决定)的范围就会触发扩容操作,也就是rehash过程,这个方法会重新将原数组的内容重新hash到新的扩容数组当中,在多线程的环境下,存在同时其他的元素也在进行put操作,如果hash值相同,可能出现在同一数组下用链表表示,造成闭环,导致在获取数据也就是进行get操作的时候,产生死循环,所以HashMap是线程不安全的。(本来应该画图解释的,但是限于自身对这个多线程环境操作不太熟悉,也就直接给出文字总结版)。

2、HashMap的适用场景?

因为HashMap是非线程安全的,故其适用于单线程环境下。

底下的代码演示是上课没写出来,课下自己又练了练,贴在这里。

//10万个数据统计数据重复出现的次数并打印
        //首先随机产生10万个原始数据
        LinkedList list=new LinkedList();
        Random random=new Random();
        for(int i=0;i<100000;i++){
            list.add(random.nextInt(100));
        }
        //统计出现的次数
        HashMap<Integer,Integer> map=new HashMap<>();
        Iterator<Integer> iterator=list.iterator();
        while(iterator.hasNext()){
            Integer value=iterator.next();
            Integer num=map.get(value);
            if(num==null){
                map.put(value,1);
            }else{
                map.put(value,num+1);
            }
        }
        //打印数据
        System.out.println("统计10万个数据重复出现的次数:");
        Iterator<Map.Entry<Integer,Integer>> iterator1=map.entrySet().iterator();
        while(iterator1.hasNext()){
           Map.Entry<Integer,Integer> entry=iterator1.next();
           Integer value1=entry.getKey();
           Integer number=entry.getValue();
           System.out.print(number+"  "+value1 +"      ");
        }
       System.out.println();
   }
//统计10万个数据,找出第一个重复的数据并进行打印
        //方法一、
        //首先随机产生10万个原始数据
        LinkedList list=new LinkedList();
        Random random=new Random();
        for(int i=0;i<100000;i++){
            list.add(random.nextInt(100));
        }
        //统计第一个重复出现的数据
        System.out.println("第一个重复的数据为:");
        HashMap<Integer,Integer> map=new HashMap<>();
        Iterator<Integer> iterator=list.iterator();
        while(iterator.hasNext()){
            Integer value=iterator.next();//数据本身,也就是键值key的位置
            if(!map.containsKey(value)){//HashMap中如果不包含此键值,说明是第一次重复的数据
                System.out.println(value+"   ");
                return ;
            }

        }

        //方法二、
        LinkedList list=new LinkedList();
        Random random=new Random();
        for(int i=0;i<100000;i++){
            list.add(random.nextInt(100));
        }
        System.out.println("第一次重复出现的数据为:");
        HashMap<Integer,Integer> map=new HashMap<>();
        Iterator<Integer> iterator=list.iterator();
        while(iterator.hasNext()){
            Integer value=iterator.next();
            Integer num=map.get(value);
            if(num==null){
                map.put(value,1);
            }else{
                System.out.print(value+"   ");
                return;
            }
        }
//统计10万个数据,找出出现次数最多的数据并进行打印
        LinkedList list=new LinkedList();
        Random random=new Random();
        for(int i=0;i<100000;i++){
            list.add(random.nextInt(100));
        }
        Iterator<Integer> iterator=list.iterator();
        HashMap<Integer,Integer> map=new HashMap<>();
        Integer max=0;
        Integer data=0;
        while(iterator.hasNext()){
            Integer value=iterator.next();
            Integer num=map.get(value);
            if(num==null){
                map.put(value,1);
            }else{
                map.put(value,num+1);
            }
        }
        System.out.println("统计10万个数据中重复出现次数最多的数据为:");
        Iterator<Map.Entry<Integer,Integer>> iterator1=map.entrySet().iterator();
        while(iterator1.hasNext()){
            Map.Entry<Integer,Integer> entry=iterator1.next();
            Integer Data=entry.getKey();
            Integer vvalue=entry.getValue();
            if(vvalue>max){
                max=vvalue;
                data=Data;
            }
        }
        System.out.println(data+"==>"+max);

二、HashTable

HashTable在JDK1.1.的时候就已经有了。

1、HashTable的线程安全体现在哪里?

HashTable的put()、get()、remove()方法是关键字synchronized来修饰的,其保证了方法的同步性进而保证了其线程安全性。

synchronized关键字:

加上synchronized的关键字的方法,如果两个线程同时操作该方法,表示这两个过程是并发的。

不加synchronized关键字的方法,如果两个线程同时操作该方法,表示这两个过程是顺次执行的,也就是一个过程执行完再执行另一个过程。

在多线程环境下,synchronized关键字非常常见,当需要进行“同步”操作的时候,需要该关键字对代码块或者方法进行锁定,被synchronized锁定的代码块,只能同时有一条线程访问该代码块。那么,synchronized关键字是锁对象还是锁代码块?答案是锁对象!!!(在这里,直接给出结论,后续可能会使用代码解释该关键字修饰方法的详细解析)

当synchronized修饰静态方法时,表示锁定的是class对象;修饰动态方法时,表示锁定的是当前对象(this)。

当synchronized修饰代码块时,表示锁住的是某个对象,被该关键字修饰的其他线程的方法如果要执行,也必须得到该锁,即synchronized代码块执行完。

2、HashTable的适用场景?

HashTable是早期Java类库提供的一个哈希表表现,本身是同步的,不支持null值和空键,它的方法是同步方法,所带来的问题的无论在什么时刻,只能有一个线程来操作HashTable,效率比较低下,导致的性能开销,现在已经很少使用了。

3、HashTable源码中使用的Entry与HashMap源码中的Entry一样吗?

答案是一样的,源码分析如下:

//HashMap内部定义的静态内部类Entry,实现Map.Entry<K,V>接口
static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;
        int hash;
根据上述的成员变量可以看出,该Entry是单向链表,只有一个next域指向下一个节点!!!
       
        
//HashTable定义的静态内部类Entry,实现Map.Entry<K,V>接口
private static class Entry<K,V> implements Map.Entry<K,V> {
        int hash;
        final K key;
        V value;
        Entry<K,V> next;
根据定义的成员变量可以看出,是单向链表,只含有一个指向下一个节点的next域
        

4、HashTable使用的迭代器与之前集合实现类所使用的迭代器有何区别?

 (Enumeration枚举遍历与Iterator遍历有何区别???)

HashTable使用的迭代器是Enumeration枚举遍历元素的。

Enumeration枚举仅适用于传统,例如HashTable、Vector,枚举是初始Java发行版JDK1.0的一部分,现在已经基本被Iteration迭代器所取代了。区别主要在于:


                               Enumeration                                                                                                      Iteration


只有两个函数接口,通过Enumeration,只能读取                           有三个函数接口,除了能读取集合中的数据之外,也可以对集合

集合中的数据,但是不能对数据进行修改。                                       中的数据进行修改。


不支持fail-fast快速失败机制                                                                                               支持fail-fast快速失败机制


三、HashMap与HashTable之间的异同点?

1、相同点

HashMap与HashTable都实现了Map接口,底层数据结构都是数组加链表。

2、不同点:主要从继承关系、初始默认容量的大小、线程安全性、扩容方式、null值的处理、hash函数、方法这八个方面来说明。


                                                            HashMap                                                                                   HashTable


继承关系:                             extends AbstractMap                                                                   extends Dictionary


初始默认容量的大小:                     16                                                                                                      11


线程安全性:                               非线程安全                                                                                        线程安全的


扩容方式:                  resize(2 * table.length);(2的指数) 2倍扩容                      newCapacity = oldCapacity * 2 + 1         2倍+1扩容


null值的处理:            最多只允许一个key为null,value可多个为null                                              key和value都不允许为null


hash函数:                       int h = hashSeed;                                                                                                  hashSeed ^ k.hashCode();

                             if (0 != h && k instanceof String) {  首先判断哈希种子是否为0以及传进的k是否为string
                            return sun.misc.Hashing.stringHash32((String) k);     如果满足,调用特殊哈希函数处理!
                              }

                           h ^= k.hashCode();


方法:                               只含有ContainsKey(Object key)方法                                                           含有ContainsKey(Object key)、

                                                                                                                                                                              Contains(Object value)、                                                                                                                                                                                      ContainsValue(Object value)


猜你喜欢

转载自blog.csdn.net/qq_40303781/article/details/84308560