计算机基础知识——java02

一、string是否可以用来做SWITCH的参数?

jdk1.7之前,只有byte、short 、char 、int可以做switch的参数,1.7之后,枚举类型、布尔、字符串都可以,string类型是调用了 string.hashCode,将string转换为int从而进行判断

 

二、为什么重写equal()之后还要重写hashcoude()

规范是如果两个对象的equals值相等,那么调用这两个对象中任一个对象的hashCode方法必须产生同样的整数结果。

以java.lang.Object来理解,JVM每new一个Object,它都会将这个Object丢到一个Hash哈希表中去,这样的话,下次做Object的比较或者取这个对象的时候,它会根据对象的hashcode再从Hash表中取这个对象。这样做的目的是提高取对象的效率。

1.new Object(),JVM根据这个对象的Hashcode值,放入到对应的Hash表对应的Key上,如果不同的对象确产生了相同的hash值,也就是发生了Hash key相同导致冲突的情况,那么就在这个Hash key的地方产生一个链表,将所有产生相同hashcode的对象放到这个单链表上去,串在一起。
2.比较两个对象的时候,首先根据他们的hashcode去hash表中找他的对象,当两个对象的hashcode相同,那么就是说他们这两个对象放在Hash表中的同一个key上,那么他们一定在这个key上的链表上。那么此时就只能根据Object的equal方法来比较这个对象是否equal。当两个对象的hashcode不同的话,肯定他们不能equal.

下面是hashcode约定的内容,来自java.lang.Object的规范:

1. 在一个应用程序执行期间,如果一个对象的equals方法比较所用到的信息没有被修改的话,那么,对该对象调用hashCode方法多次,它必须始终如一地返回一个整数。在同一个应用程序的多次执行过程中,这个整数可以不同,即这个应用程序这次执行返回的整数与下一次执行返回的整数可以不一致。

2. 如果两个对象根据equals(Object)方法是相等的,那么调用这两个对象中任意一个对象的hashCode方法必须产生同样的整数结果。

3. 如果两个对象根据equals(Object)方法是不相等的,那么调用这两个对象中任意一个对象的hashCode方法,不要求必须产生不同的整数结果。然而,程序员应该意识到这样的事实,对于不相等的对象产生截然不同的整数结果,有可能提高散列表的性能。

 三、注意hashmap、hashtable、concurrenthashmap区别

 

hash map数组+链表实现(java8 加入红黑树来解决效率问题),null键null值,线程不安全,初始容量是16,Map中元素个数超过Entry 数组的的75%,就会触发扩容(插入元素后触发扩容,触发扩容后如果不插入数据,就会产生无效扩容),扩容为2的n次幂;每次扩容后,原来数组中的元素会重新计算存放位置,并重新插入(HashMap会进行resize操作);

hash table 数组+链表实现,不允许null键null值,线程安全,实现线程安全的方式是修改数据的似乎锁住整个Hashtable,效率低下 ;
初始容量为n==11,扩容为 2n+1;

ConcurrentHashMapHashMap不支持并发操作,没有同步方法,ConcurrentHashMap支持并发操作,通过继承 ReentrantLock(JDK1.7重入锁)/CAS和synchronized(JDK1.8内置锁)来进行加锁(分段锁),每次需要加锁的操作锁住的是一个 segment,这样只要保证每个 Segment 是线程安全的,也就实现了全局的线程安全。

 


JDK1.8之前HashMap的结构为数组+链表,JDK1.8之后HashMap的结构为数组+链表+红黑树;使得在桶里面查找数据的复杂度从O(n)降到O(logn),当然还有一些其他的优化,比如resize的优化等。

JDK1.8之前ConcurrentHashMap的结构为segment数组+数组+链表,JDK1.8之后ConcurrentHashMap的结构为数组+链表+红黑树。

3.2关于hashmap

https://www.cnblogs.com/chengxiao/p/6059914.html#t4

HashMap不保证遍历的顺序和插入的顺序是一致的

LinkedHashMap属于HashMap的子类,与HashMap的区别在于LinkedHashMap保存了记录插入的顺序。

TreeMap实现了SortedMap接口,TreeMap有能力对插入的记录根据key排序,默认按照升序排序,也可以自定义比较强,在使用TreeMap的时候,key应当实现Comparable。

为什么hashmap线程不安全

1、put的时候导致的多线程数据不一致。
这个问题比较好想象,比如有两个线程A和B,首先A希望插入一个key-value对到HashMap中,首先计算记录所要落到的桶的索引坐标,然后获取到该桶里面的链表头结点,此时线程A的时间片用完了,而此时线程B被调度得以执行,和线程A一样执行,只不过线程B成功将记录插到了桶里面,假设线程A插入的记录计算出来的桶索引和线程B要插入的记录计算出来的桶索引是一样的,那么当线程B成功插入之后,线程A再次被调度运行时,它依然持有过期的链表头但是它对此一无所知,以至于它认为它应该这样做,如此一来就覆盖了线程B插入的记录,这样线程B插入的记录就凭空消失了,造成了数据不一致的行为。

2、另外一个比较明显的线程不安全的问题是HashMap的get操作可能因为resize而引起死循环(cpu100%),具体分析如下:

下面的代码是resize的核心内容:

void transfer(Entry[] newTable, boolean rehash) {  
        int newCapacity = newTable.length;  
        for (Entry<K,V> e : table) {  



       <span class="hljs-keyword">while</span>(<span class="hljs-keyword">null</span> != e) {  
            Entry&lt;K,V&gt; next = e.next;           
            <span class="hljs-keyword">if</span> (rehash) {  
                e.hash = <span class="hljs-keyword">null</span> == e.key ? <span class="hljs-number">0</span> : hash(e.key);  
            }  
            <span class="hljs-keyword">int</span> i = indexFor(e.hash, newCapacity);   
            e.next = newTable[i];  
            newTable[i] = e;  
            e = next;  
        } 
    }  
} 

这个方法的功能是将原来的记录重新计算在新桶的位置,然后迁移过去。

多线程HashMap的resize

我们假设有两个线程同时需要执行resize操作,我们原来的桶数量为2,记录数为3,需要resize桶到4,原来的记录分别为:[3,A],[7,B],[5,C],在原来的map里面,我们发现这三个entry都落到了第二个桶里面。
假设线程thread1执行到了transfer方法的Entry next = e.next这一句,然后时间片用完了,此时的e = [3,A], next = [7,B]。线程thread2被调度执行并且顺利完成了resize操作,需要注意的是,此时的[7,B]的next为[3,A]。此时线程thread1重新被调度运行,此时的thread1持有的引用是已经被thread2 resize之后的结果。线程thread1首先将[3,A]迁移到新的数组上,然后再处理[7,B],而[7,B]被链接到了[3,A]的后面,处理完[7,B]之后,就需要处理[7,B]的next了啊,而通过thread2的resize之后,[7,B]的next变为了[3,A],此时,[3,A]和[7,B]形成了环形链表,在get的时候,如果get的key的桶索引和[3,A]和[7,B]一样,那么就会陷入死循环。

如果在取链表的时候从头开始取(现在是从尾部开始取)的话,则可以保证节点之间的顺序,那样就不存在这样的问题了。

综合上面两点,可以说明HashMap是线程不安全的。

四、ArrayList、LinkedList、Vector的区别

1. 实现:

ArrayList,Vector 是基于数组的实现。

LinkedList 是基于链表的实现。

2.安全
​ArrayList,LinkedList 不是线程安全的。

Vector 是线程安全的,实现方式是在方法中加 synchronized 进行限定。

3.性能

ArrayList和Vector由于是基于数组实现,随机访问的速度是O(1),在指定位置插入和删除时间复杂度为O(n),还可能出现扩容问题,这比较消耗性能。

LinkedList不会出现扩容问题,适合增删操作;查找元素需要遍历链表,时间复杂度为O(n)。

4.实际使用

快速插入、删除元素,使用LinkedList

快速随机访问元素,使用ArrayList

单线程,使用List,比如ArrayList

多线程,使用Vector

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值