阿里面经之解答 by cmershen(2)——static/final,HashMap/Hashtable/ConcurrentHashMap
来源:互联网 发布:一彩软件 编辑:程序博客网 时间:2024/05/02 00:12
7. static和final的用法
static可以修饰变量,方法,代码块。
static修饰的变量在内存中只有一份,在类加载的时候被完成初始化,且被该类的所有实例共享。
static修饰的方法必须实现,不能用abstract修饰。
static修饰的代码块在类加载完成后就会执行代码块的内容。
执行顺序:(重要,好多面试题都考)
父类静态代码块-子类静态代码块-父类非静态代码块-父类构造方法-子类非静态代码块-子类构造方法
final修饰的变量,引用不可变,但引用的内容是可变的。
final修饰的方法,不能被继承,不能被子类修改。
final修饰的类不能被继承。
final修饰的形参不可变。
8.HashMap和Hashtable的区别
Hashtable是线程同步的,而HashMap是未经同步的。(就像ArrayList和Vector,Vector是同步的,而ArrayList不同步)
Hashtable的值不可以是null(key和value都不可以),而HashMap的key和value都可以是null
Hashtable直接使用对象的hashCode做key,而HashMap则不是。
Hashtable.java部分代码如下:
public synchronized V put(K key, V 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();//可见这里直接用了key的hashCode int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entry<K,V> entry = (Entry<K,V>)tab[index]; for(; entry != null ; entry = entry.next) { if ((entry.hash == hash) && entry.key.equals(key)) { V old = entry.value; entry.value = value; return old; } } addEntry(hash, key, value, index); return null; }
而HashMap.java是这样做的:
static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }
HashMap的key不可以是可变对象,否则当对象的hashCode值发生变化时,会出现一些诡异的情况。
例如用HashMap
public class Student { private int id; @Override public int hashCode() { // TODO Auto-generated method stub return id; } public int getId() { return id; } public void setId(int id) { this.id = id; } public Student(int id) { this.id = id; }}
那么如果学生的学号改变了,就像这样:
public class Main { public static void main(String[] args) { HashMap<Student, Integer> map = new HashMap<>(); Student s1=new Student(1); Student s2=new Student(2); map.put(s1, 100); map.put(s2, 98); s1.setId(2); System.out.println(map); }}
就会出现{Student@2=100, Student@2=98}
的输出结果,一个学生对应了两个学号,且学生1的成绩就会找不到。
不过有一个诡异的问题:
如果Main函数这样写:
public static void main(String[] args) { HashMap<Student, Integer> map = new HashMap<>(); Student s1=new Student(1); Student s2=new Student(2); map.put(s1, 100); map.put(s2, 98); s1.setId(3); System.out.println(map); System.out.println(map.get(s1)); }
会看到这样的输出:
{Student@3=100, Student@2=98}null
奇怪了,明明s1的学号改成3,且map里面也记录了学号为3的成绩是100分,怎么变成null了?
读HashMap的源码发现,在执行 map.put(s1,100)
时,系统为hashCode=1的key找到一个地址,存储对象s1和value值100,map.put(s2, 98);
这一行也同理。但当s1的hashCode值变为3时,系统是到HashCode值为3对应的地址去找,显然这一地址对应的Entry为null。
那么改为s1.setID(2);
呢?
结论也是输出null。因为HashMap.java中有如下片段:
do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null);
只有当HashMap中对应地址中存储的key和输入的key是同一个对象时,才会返回这个e。
而hashcode=2的地址中存储的对象是s2,和s1不是同一个类,故返回null。
而若重写equals方法使得s1和s2被判为相等,则返回98。
所以,如果必须要用可变对象做key,务必保证这个对象的属性改变时,不得改变它的HashCode。
9.HashMap和ConcurrentHashMap
ConcurrentHashMap是线程安全的,它的底层实现是将大的数组分成几段小的segment,每个小的segment上都有锁,在插入元素时,先找到要插入到哪个segment里面,再获取这个segment上的锁。
在高并发时,Hashtable的效率很低下,因为所有线程都竞争同一个锁,而ConcurrentHashMap则不是,当一个线程占用了一个segment的锁,其他段的数据也能被其他线程访问。
//原文中说get方法在读到的值是空的时候要加锁重读,我看了好几遍源码也没看出锁在哪,求高人指点。public V get(Object key) { Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek; int h = spread(key.hashCode()); if ((tab = table) != null && (n = tab.length) > 0 && (e = tabAt(tab, (n - 1) & h)) != null) { if ((eh = e.hash) == h) { if ((ek = e.key) == key || (ek != null && key.equals(ek))) return e.val; } else if (eh < 0) return (p = e.find(h, key)) != null ? p.val : null; while ((e = e.next) != null) { if (e.hash == h && ((ek = e.key) == key || (ek != null && key.equals(ek)))) return e.val; } } return null; }
10.如果故意构造相同hash的字符串进行攻击,怎么处理?那jdk7呢?
还是前面的代码,稍加改动一下:
public static void main(String[] args) { HashMap<Student, Integer> map = new HashMap<>(); Student s1=new Student(1); Student s2=new Student(2); map.put(s1, 100); map.put(s2, 98); Student s3 = new Student(2); System.out.println(map.get(s3)); }
如果知道了Student的实现,想得到学生2的成绩,就可以新建一个对象s3把它的id设置为2,然后map.get(s3)得到的就变成了s2的成绩。因此程序输出s2的成绩98。
因此,HashMap要求hashcode相等且对象的equals返回true 才认为是同一个key,输出value。
- 阿里面经之解答 by cmershen(2)——static/final,HashMap/Hashtable/ConcurrentHashMap
- 阿里面经之解答 by cmershen(4)——线程池
- 阿里面经之解答 by cmershen(5)——内存泄露,java.util.concurrent包
- 阿里面经之解答by cmershen(1)——Java的基本特性,面向对象的六大特征等
- 阿里面经之解答 by cmershen(3)——String/StringBuffer/StringBuilder,Java序列化,线程安全,线程同步,ThreadLocal
- static/final,HashMap/Hashtable/ConcurrentHashMap
- 面试题:HashMap HashTable ConcurrentHashMap区别
- 面试题:HashMap HashTable ConcurrentHashMap区别
- 多线程之Map:Hashtable HashMap 以及ConcurrentHashMap
- 聊聊并发——HashMap、HashTable及ConcurrentHashMap
- Java集合——HashMap、HashTable以及ConCurrentHashMap异同比较
- Java集合——HashMap、HashTable以及ConCurrentHashMap异同比较
- Java集合——HashMap、HashTable以及ConCurrentHashMap异同比较
- HashMap,HashTable,ConcurrentHashMap,ConcurrentSkipListMap
- HashMap、ConcurrentHashMap、HashTable、HashSet
- ConcurrentHashMap、HashMap、HashTable区别
- HashMap、HashTable、ConcurrentHashMap、Queue
- HashMap HashTable ConcurrentHashmap
- MyBatis Generator 配置
- Nginx之页面缓存
- NSURLSession的GET和POST请求的封装
- 糗事百科交互式爬虫
- iBatis简单入门教程
- 阿里面经之解答 by cmershen(2)——static/final,HashMap/Hashtable/ConcurrentHashMap
- Android中对于TabPageIndicator开源框架的使用
- Hbase API 操作
- 经验分享:CSS浮动(float,clear)通俗讲解
- s3c6410 uboot 修改和过程分析
- 第一篇博文
- Stata基本功能及其函数实现
- iOS 自己封装的网络请求,json解析的类
- MapReduce基础开发之三字段处理并输出Hive表