⑴ java中,HashMap的创建流程是什么
创建流程,指的是:
Map<String, String> mp = new HashMap<String, String>();
吗?
就是创建一个HashMap对象,其中键的类型为String,值的类型也为String;
当然,键和值的类型也可以是其他的。例如,创建了一个类叫做Student,我们现在还有每一个学生的学号。那么就可以建立一个Map<String, Student> 这样的HashMap。通过这样的方式,我们就可以利用学号来找到具体对应的学生。
⑵ Java中HashMap初始容量问题
这个问题可以跟踪一下HashMap的源码就知道了,根据输入的初始化容量(门槛?)的值(先了解HashMap中容量和负载因子的概念,其实这个和HashMap确定存储地址的算法有关),先判断是否大于最大容量,最大容量2的30次方,1<<30 =(1073741824),如果大于此数,初始化容量赋值为1<<30,如果小于此数,调用tableSizeFor方法 使用位运算将初始化容量修改为2的次方数,都是向大的方向运算,比如输入13,小于2的4次方,那面计算出来桶的初始容量就是16.
publicHashMap(intinitialCapacity){
this(initialCapacity,DEFAULT_LOAD_FACTOR);
}
/**
*Constructsanempty<tt>HashMap</tt>withthespecifiedinitial
*capacityandloadfactor.
*
*@
*@paramloadFactortheloadfactor
*@
*ortheloadfactorisnonpositive
*/
publicHashMap(intinitialCapacity,floatloadFactor){
if(initialCapacity<0)
("Illegalinitialcapacity:"+
initialCapacity);
if(initialCapacity>MAXIMUM_CAPACITY)
initialCapacity=MAXIMUM_CAPACITY;
if(loadFactor<=0||Float.isNaN(loadFactor))
("Illegalloadfactor:"+
loadFactor);
this.loadFactor=loadFactor;
this.threshold=tableSizeFor(initialCapacity);
}
/**
* Returns a power of two size for the given target capacity.
*/
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
⑶ java中main函数中初始化一个hashmap对象
你这题不给20分以上 估计没人给你写 飘过先~
⑷ Java中HashMap和TreeMap的区别深入理解
HashMap底层是用数组做的,TreeMap是基于树做的
这么做的结果就是HashMap的数据在不停的添加的时候效率会比较低,而对于查找的效率是比较快的,TreeMap对于添加的效率是比较高的但是对于查找的效率要相对比较低一些
这里简单从底层说一下,我就不从具体的实现上说,只从数据结构和大致的原理上来补充一下我的答案。
HashMap这个类在存储的时候,首先根据key来计算机将要存储的key-value映射对存储在数组的什么位置上,当计算出位置后就把这个映射对存储到这个位置上。当读取的时候,首先根据key来计算出一个位置来,到数组的相应的位置上去读取数据,如果没有数据,则表示此Map中不存在该映射对,若存在则直接返回。说到这里就可以解释一下为什么对于不停地存储的效率相对比较低了,首先在初始化的时候对于数组的长度给了一个初始的长度,当往这里面添加数据达到一定的程度的时候就没法继续添加数据了,继续添加数据的冲突就会增大,或者没法添加数据(这里有一个衡量的量就是装填因子,是指这个数组中的添加的数据的个数和数组长度的比值,在java中比较合理的装填因子数是0.75),当数据添加到这个程度的时候不能不让用户继续添加数据吧,总得解决继续添加数据的问题啊,于是提出了解决方案,即开一个更大的数组,把当前上的每一个数据重新在更大的数组上计算位置,并把数据复制过去,这样完成的数组的扩大,看完这个我们知道,这个过程是很耗时的,所以说对于不停的存储数据时效率是比较低的。这里有没有一个稍微好一点的解决方案或者说不需要进行数组的扩大吗,我们能不能一开始在初始化的时候就把数组的空间开辟的足够大,这样就不用在存储的过程进行复制了,可以吗,答案是肯定的,java给我们提供了这个在初始化的时候的方式。但是,也是有问题的那就是我们的数据不够多,就会造成空间的浪费。有没有一个速度即快又不浪费空间的方式呢,解决方案也有一个,那就是在开始的时候我们就能很好的预测数据的规模,这样我们在开始的时候按照相应的规模进行初始化,这样就很好了,实际中我们是不能很好的预测这个规模的。于是对于这种情况提出了下一个解决方案TreeMap。
TreeMap是一个基于树的存储结构,学过数据结构的应该知道,树的实现方式是基于指针实现的,在Java中是用引用模拟实现的,这里大家都知道,其实树的读取效率并不低,这里是相对于数组的顺序查找来说的,但是与HashMap的查找方式相比就有了差距,HashMap是上来先问你在哪,直接就去取数据了,TreeMap需要遍历,也就是需要挨个询问你是我要的东西吗,对比一下,是就返回,不是就继续查找,于是查找的效率就低了,但是它解决了HashMap数组扩大的时候的效率问题,就是新添加的数据可以往里面添加,不会出现复制的情况,这里就是由于模拟的指针的缘故实现的。
其实从总体来说这两个各有利弊,我们在使用的时候需要根据实际的需要来选择相应的类。
⑸ JAVA hashmap的用法
已经给楼主写了个例子..
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
public class HashMapTest {
public static void main(String[] args){
HashMap<String,Object> hm=new HashMap<String,Object>();
People p1=new People();
People p2=new People();
People p3=new People();
People p4=new People();
hm.put("People3", p1);
hm.put("People1", p2);
hm.put("People4", p3);
hm.put("People2", p4);
Iterator<String> it=hm.keySet().iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
class People {
private String name;
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
运行了没问题..
祝楼主早日成功!
⑹ HashMap底层实现原理剖析
Hashmap是一种非常常用的、应用广泛的数据类型,最近研究到相关的内容,就正好复习一下。网上关于hashmap的文章很多,但到底是自己学习的总结,就发出来跟大家一起分享,一起讨论。
1.HashMap的数据结构:在java 中 数据结构,最基本 也就两种 一种数组 一种模拟指针。所有的数据结构都可以用这两个基本结构来构造的,hashmap也不例外。Hashmap实际上是一个数组和链表的结合体。数组的默认长度为16,
2.hashMap源码解析
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 初始化容量大小
static final int MAXIMUM_CAPACITY = 1 << 30; ///容器最大值
static final float DEFAULT_LOAD_FACTOR = 0.75f; //加载影子
static final Entry[] EMPTY_TABLE = {}; //null 的hashMap
transient Entry[] table = (Entry[]) EMPTY_TABLE;///动态扩大容器时使用的一个hashMap
transient int size;//当前数据量
int threshold;//扩大容器时的大小 为 capacity * load factor
final float loadFactor;//使用率阀值,默认为:DEFAULT_LOAD_FACTOR
存取元素 :调用put方法
public V put(K key, V value) {
//判断当前table 为Null 第一次Put
if (table == EMPTY_TABLE) {
inflateTable(threshold); //初始化容器的大小
}
if (key == null)
return putForNullKey(value); //判断当前key 为null 将Null key添加到数组的第一个位置
int hash = hash(key); //将当前key进行hash 详情见下方
int i = indexFor(hash, table.length); //调用完hash算法后,详情见下方
for (Entry e = table[i]; e != null; e = e.next) { //循环判断当前数组下标为Entry的实体 将当前key相同的替换为最新的值
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i); //如果key都不同 则添加Entry.详情见下方
return null;
}
hashMap的hash算法剖析
final int hash(Object k) {
int h = hashSeed;
if (0 != h && k instanceof String) { //判断当前k是否为string 和
return sun.misc.Hashing.stringHash32((String) k); //使用stringHash32算法得出key 的hash值
}
h ^= k.hashCode(); //调用key的hashCode 得出值 后使用"或"运算符
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
前面说过HashMap的数据结构是数组和链表的结合,所以我们当然希望这个HashMap里面的 元素位置尽量的分布均匀些,尽量使得每个位置上的元素数量只有一个,那么当我们用hash算法求得这个位置的时候,马上就可以知道对应位置的元素就是我们要的,而不用再去遍历链表,这样就大大优化了查询的效率。
一个十进制数32768(二进制1000 0000 0000 0000),经过上述公式运算之后的结果是35080(二进制1000 1001 0000 1000)。看出来了吗?或许这样还看不出什么,再举个数字61440(二进制1111 0000 0000 0000),运算结果是65263(二进制1111 1110 1110 1111),现在应该很明显了,它的目的是让“1”变的均匀一点,散列的本意就是要尽量均匀分布。使用上述算法后 "1"就变得很均匀了。
我们用table[index]表示已经找到的元素需要存储的位置。先判断该位置上有没有元素(这个元素是HashMap内部定义的一个类Entity, 基本结构它包含三个类,key,value和指向下一个Entity的next),没有的话就创建一个Entity对象,在 table[index]位置上插入,这样插入结束;如果有的话,通过链表的遍历方式去逐个遍历,看看有没有已经存在的key,有的话用新的value替 换老的value;如果没有,则在table[index]插入该Entity,把原来在table[index]位置上的Entity赋值给新的 Entity的next,这样插入结束
}
indexFor 返回当前数组下标 ,
static int indexFor(int h, int length) {
return h & (length-1);
}
那么得到key 之后的hash如何得到数组下标呢 ?把h与HashMap的承载量(HashMap的默认承载量length是16,可以自动变长。在构造HashMap的时候也可以指定一个长 度。这个承载量就是上图所描述的数组的长度。)进行逻辑与运算,即 h & (length-1),这样得到的结果就是一个比length小的正数,我们把这个值叫做index。其实这个index就是索引将要插入的值在数组中的 位置。第2步那个算法的意义就是希望能够得出均匀的index,这是HashTable的改进,HashTable中的算法只是把key的 hashcode与length相除取余,即hash % length,这样有可能会造成index分布不均匀。
首先来解释一下为什么数组大小为2的幂时hashmap访问的性能最高?
看下图,左边两组是数组长度为16(2的4次方),右边两组是数组长度为15。两组的hashcode均为8和9,但是很明显,当它们和1110“与”的时候,产生了相同的结果,也就是说它们会定位到数组中的同一个位置上去,这就产生了碰撞,8和9会被放到同一个链表上,那么查询的时候就需要遍历这个链表,得到8或者9,这样就降低了查询的效率。同时,我们也可以发现,当数组长度为15的时候,hashcode的值会与14(1110)进行“与”,那么最后一位永远是0,而0001,0011,0101,1001,1011,0111,1101这几个位置永远都不能存放元素了,空间浪费相当大,更糟的是这种情况中,数组可以使用的位置比数组长度小了很多,这意味着进一步增加了碰撞的几率,减慢了查询的效率!
void addEntry(int hash, K key, V value, int bucketIndex) {
//// 若HashMap的实际大小 不小于 “阈值”,则调整HashMap的大小
if ((size >= threshold) && (null != table[bucketIndex])) {
resize(2 * table.length);
hash = (null != key) ? hash(key) : 0;
//// 设置“bucketIndex”位置的元素为“新Entry”,// 设置“e”为“新Entry的下一个节点”
bucketIndex = indexFor(hash, table.length);
}
createEntry(hash, key, value, bucketIndex);
}
//将当前key 和value添加到Entry[]中
void createEntry(int hash, K key, V value, int bucketIndex) {
Entry e = table[bucketIndex]; //将第一个就得table 复制个新的entry
table[bucketIndex] = new Entry<>(hash, key, value, e); //将当前新的Entry 复制个table[bucketIndex] 旧的table[bucketIndex] 和新的table[buckIndex]之间用next关联。第一个键值对A进来,通过计算其key的hash得到的index=0,记做:table[0] = A。一会后又进来一个键值对B,通过计算其index也等于0,现在怎么办?HashMap会这样做: B.next = A ,table[0] = B,如果又进来C,index也等于0,那么 C.next = B ,table[0] = C;这样我们发现index=0的地方其实存取了A,B,C三个键值对,他们通过next这个属性链接在一起
size++; //容量加1
}
以上就是HashMap添加元素时的过程解析
那么如何get元素呢?
public V get(Object key) {
if (key == null) return getForNullKey(); //当前key是否为null 如果为null值返回table[0]这个value
Entry entry = getEntry(key);
return null == entry ? null : entry.getValue();
}
final EntrygetEntry(Object key) {
if (size == 0) { return null; } //判断容量是否大于0
int hash = (key == null) ? 0 : hash(key); //对当前key 进行hash hash后得到一个值
for (Entry e = table[indexFor(hash, table.length)]; //获取当前Entry 循环遍历
e != null;
e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}
扩展问题:
1.当前我们的hashMap中越来越大的之后,"碰撞"就越来越明显,那么如何解决碰撞呢?扩容!
当hashmap中的元素个数超过数组大小capti*loadFactor时,就会进行数组扩容,loadFactor的默认值为0.75,也就是说,默认情况下,数组大小为16,那么当hashmap中元素个数超过16*0.75=12的时候,就把数组的大小扩展为2*16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知hashmap中元素的个数,那么预设元素的个数能够有效的提高hashmap的性能。比如说,我们有1000个元素new HashMap(1000), 但是理论上来讲new HashMap(1024)更合适,不过上面annegu已经说过,即使是1000,hashmap也自动会将其设置为1024。 但是new HashMap(1024)还不是更合适的,因为0.75*1000 < 1000, 也就是说为了让0.75 * size > 1000, 我们必须这样new HashMap(2048)才最合适,既考虑了&的问题,也避免了resize的问题
HashMap的两种遍历方式
第一种
Map map = newHashMap();
Iterator iter = map.entrySet().iterator();
while(iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
Object key = entry.getKey();
Object val = entry.getValue();
}
效率高,以后一定要使用此种方式!
第二种
Map map = newHashMap();
Iterator iter = map.keySet().iterator();
while(iter.hasNext()) {
Object key = iter.next();
Object val = map.get(key);
}
效率低,以后尽量少使用!
归纳
简单地说,HashMap 在底层将 key-value 当成一个整体进行处理,这个整体就是一个 Entry 对象。HashMap 底层采用一个 Entry[] 数组来保存所有的 key-value 对,当需要存储一个 Entry 对象时,会根据hash算法来决定其在数组中的存储位置,在根据equals方法决定其在该数组位置上的链表中的存储位置;当需要取出一个Entry时,
也会根据hash算法找到其在数组中的存储位置,再根据equals方法从该位置上的链表中取出该Entry。
⑺ java的HashMap能不能在new的同时赋值,就像new String[]的时候赋初值一样
好像是不行的吧。。
你举例的是,String[](字符串数组),数组是可以在new的时候直接赋值。。但是Hashmap是集合类,集合好像没见过这么用的,因为在集合中究竟存放什么类型都尚未定义
⑻ 关于JAVA MAP怎么初始化生成
首先你要理解 Map的基本结构,key-value
这里最外层的Map,key是String类型,value是ArrayList。ArrayList里面放得又是Map,这个Map的key是String,value也是String。
importjava.util.ArrayList;
importjava.util.HashMap;
importjava.util.Map;
publicclassTest{
publicstaticvoidmain(String[]args){
Map<String,ArrayList<Map<String,String>>>topMap=newHashMap<>();
Stringkey1="map_key1";//topMap的第一个key
ArrayList<Map<String,String>>value1=newArrayList<>();//topMap的第一个value
//start
Map<String,String>strMap=newHashMap<>();
strMap.put("hello","你好");
strMap.put("thanks","谢谢");
value1.add(strMap);
value1.add(newHashMap<>());//添加一个空的Map
//end
topMap.put(key1,value1);//放入topMap
//以上start到end段的代码是在往value1的ArrayList中填充数据
//不填充也可以放入到topMap中,就像下面这样
topMap.put("emptyList",newArrayList<Map<String,String>>());
topMap.put("emptyList2",newArrayList<>());//jdk1.7及之后推荐这样写,省掉泛型描述。前面的newHashMap<>()、newArrayList<>()也是省掉了
}
}
⑼ java中hashmap的应用,本人小白,这题要咋搞
importjava.util.HashMap;
importjava.util.Map;
publicclasswork{
publicstaticvoidmain(String[]args){
classStudent{//定义内部类Student
privateStringnum;//学号
privateStringname;//名称
privateintage;//年龄
Student(){
}//无参构造方法
Student(Stringnum,Stringname,intage){//有参构造方法
this.num=num;
this.name=name;
this.age=age;
}
publicStringgetNum(){
returnnum;
}
publicvoidsetNum(Stringnum){
this.num=num;
}
publicStringgetName(){
returnname;
}
publicvoidsetName(Stringname){
this.name=name;
}
publicintgetAge(){
returnage;
}
publicvoidsetAge(intage){
this.age=age;
}
@Override
publicStringtoString(){//复写toString方法
return"学号:"+this.num+",姓名:"+this.name+",年龄:"+this.age;
}
}
Map<String,Student>staff=newHashMap<String,Student>();
Studentstudent1=newStudent("15H001","唐僧",40);//用有参构造方法创建一个学生
Studentstudent2=newStudent();//用无参构造方法创建一个学生
student2.setNum("15H002");
student2.setName("孙悟空");
student2.setAge(2000);
Studentstudent3=newStudent("15H003","猪八戒",1000);//用有参构造方法创建一个学生
staff.put(student1.getNum(),student1);//1号学生放入hashMap
staff.put(student2.getNum(),student2);//2号学生放入hashMap
staff.put(student3.getNum(),student3);//3号学生放入hashMap
System.out.println("3.根据key(学号)查找学号为15H003和15H004的学生,如果存在则输出其学号、姓名、年龄信息,否则输出相应的提示信息");
String[]nums={"15H003","15H004"};
for(inti=0;i<nums.length;i++){
System.out.println("查找学号:"+nums[i]);
Studentstudent=staff.get(nums[i]);
if(student!=null){//如果找到了
System.out.println("学号:"+student.getNum()+",姓名:"+student.getName()+",年龄:"+student.getAge());
}else{//如果没有找到
System.out.println("学号:"+nums[i]+"的学生没有擦寻到相关信息");
}
}
System.out.println("4.输出所有学生的信息");
for(Map.Entry<String,Student>entry:staff.entrySet()){
Stringkey=entry.getKey();
Studentvalue=entry.getValue();
System.out.println("学号:"+key+",姓名:"+value.getName()+",年龄:"+value.getAge());
}
System.out.println("5.移除唐僧");
staff.remove("15H001");//根据唐僧的学号移除唐僧
System.out.println("6.把HashMap变成数组,并输出每个数组元素的信息(学号、姓名、年龄)");
Object[]values=staff.values().toArray();
for(inti=0;i<values.length;i++){
Studentstudent=(Student)values[i];
System.out.println(student.toString());
}
}
}
⑽ java hashmap怎么初始化
HashMap 是一种常用的数据结构,一般用来做数据字典或者 Hash 查找的容器。普通青年一般会这么初始化:
HashMap<String, String> map = new HashMap<String, String>();
map.put("name", "test");
map.put("age", "20");
看完这段代码,很多人都会觉得这么写太啰嗦了,文艺青年一般这么来了:
HashMap<String, String> map = new HashMap<String, String>() {
{
map.put("name", "test");
map.put("age", "20");
}
};