在页面中怎么用struts2来获取HashMap第0值

在页面中怎么用struts2来获取HashMap第0值,第1张

#在struts2里不是你这样用的,当你用request,session,application保存值后,在<s:property>里可以用才能用#跟request(看你用什么保存的)跟 保存的名字才能取到值,比如我下面的这个就可以取到值,不懂可以继续问~~ qq 290350144Map<Integer,String> map=new HashMap<Integer,String>();get set省略在strtuts2的execute()的方法里写 mapput(0,"aaa");

sessionput("map",map); 前台页面用<s:property value="#sessionmap[0]" />这样就能取到

HashMap map = new HashMap<String,ArrayLisy<T>>();

T就是该链表的数据类型

增加一条记录

ArrayLisy<T> list = new ArrayLisy<T>();

mapput("item1",list);

把map存在request中,然后用EL表达式,迭代map:

<c:forEach var="item" items="${map2}">

${itemkey} > ${itemvalue} <br>

</c:forEach>

上面的itemvalue应该就是Previous对象咯,你尝试一下itemvaluexxx看看能不能正常取值?

HashMap在实际开发中用到的频率非常高,面试中也是热点。所以决定写一篇文章进行分析,希望对想看源码的人起到一些帮助,看之前需要对链表比较熟悉。

以下都是我自己的理解,欢迎讨论,写的不好轻喷。

HashMap中的数据结构为散列表,又名哈希表。在这里我会对散列表进行一个简单的介绍,在此之前我们需要先回顾一下 数组 链表 的优缺点。

数组和链表的优缺点取决于他们各自在内存中存储的模式,也就是直接使用 顺序存储 链式存储 导致的。无论是数组还是链表,都有明显的缺点。而在实际业务中,我们想要的往往是寻址、删除、插入性能都很好的数据结构,散列表就是这样一种结构,它巧妙的结合了数组与链表的优点,并将其缺点弱化(并不是完全消除)

散列表的做法是将key映射到数组的某个下标,存取的时候通过key获取到下标(index)然后通过下标直接存取。速度极快,而将key映射到下标需要使用 散列函数 ,又名 哈希函数 。说到哈希函数可能有人已经想到了,如何将key映射到数组的下标。

图中计算下标使用到了以下两个函数:

值得注意的是,下标并不是通过hash函数直接得到的,计算下标还要对hash值做index()处理。

Ps:在散列表中,数组的格子叫做 ,下标叫做 桶号 ,桶可以包含一个key-value对,为了方便理解,后文不会使用这两个名词。

以下是哈希碰撞相关的说明:

以下是下标冲突相关的说明:

很多人认为哈希值的碰撞和下标冲突是同一个东西,其实不是的,它们的正确关系是这样的, hashCode发生碰撞,则下标一定冲突;而下标冲突,hashCode并不一定碰撞

上文提到,在jdk18以前HashMap的实现是 散列表 = 数组 + 链表 ,但是到目前为止我们还没有看到链表起到的作用。事实上,HashMap引入链表的用意就是解决下标冲突。

下图是引入链表后的散列表:

如上图所示,左边的竖条,是一个大小为16的数组,其中存储的是链表的头结点,我们知道,拥有链表的头结点即可访问整个链表,所以认为这个数组中的每个下标都存储着一个链表。其具体做法是,如果发现下标冲突,则 后插入的节点以链表的形式追加到前一个节点的后面

这种使用链表解决冲突的方法叫做: 拉链法 (又叫链地址法)。HashMap使用的就是拉链法,拉链法是冲突发生以后的解决方案。

Q:有了拉链法,就不用担心发生冲突吗?

A:并不是!由于冲突的节点会不停的在链表上追加,大量的冲突会导致单个链表过长,使查询性能降低。所以一个好的散列表的实现应该从源头上减少冲突发生的可能性,冲突发生的概率和哈希函数返回值的均匀程度有直接关系,得到的哈希值越均匀,冲突发生的可能性越小。为了使哈希值更均匀,HashMap内部单独实现了hash()方法。

以上是散列表的存储结构,但是在被运用到HashMap中时还有其他需要注意的地方,这里会详细说明。

现在我们清楚了散列表的存储结构,细心的人应该已经发现了一个问题:Java中数组的长度是固定的, 无论哈希函数是否均匀,随着插入到散列表中数据的增多,在数组长度不变的情况下,链表的长度会不断增加 。这会导致链表查询性能不佳的缺点出现在散列表上,从而使散列表失去原本的意义。为了解决这个问题,HashMap引入了扩容与负载因子。

以下是和扩容相关的一些概念和解释:

Ps: 扩容要重新计算下标 扩容要重新计算下标 扩容要重新计算下标 ,因为下标的计算和数组长度有关,长度改变,下标也应当重新计算。

在18及其以上的jdk版本中,HashMap又引入了红黑树。

红黑树的引入被用于替换链表,上文说到,如果冲突过多,会导致链表过长,降低查询性能,均匀的hash函数能有效的缓解冲突过多,但是并不能完全避免。所以HashMap加入了另一种解决方案,在往链表后追加节点时,如果发现链表长度达到8,就会将链表转为红黑树,以此提升查询的性能。

java hashmap 得到指定key的value的方法:

private static ArrayList valueGetKey(Map map,String value) 

{

    Set set = mapentrySet();//新建一个不可重复的集合

    

    ArrayList arr = new ArrayList<>();//新建一个集合

    

    Iterator it = setiterator();//遍历的类

    while(ithasNext()) 

    {

      MapEntry entry = (MapEntry)itnext();//找到所有key-value对集合

      

      if(entrygetValue()equals(value)) //通过判断是否有该value值

      {

        

        int s = (int)entrygetKey();//取得key值

        

        arradd(s);

        

      }

    }

    return arr;

import javautilHashMap;

import javautilIterator;

import javautilSet;

public class TestHm {

public static void main(String atgs[]){

HashMap hm=new HashMap();

//添加内容

hmput("Object key1", "Object value");

hmput("Object key2", "Object value");

hmput("Object key3", "Object value");

hmput("Object key4", "Object value");

//删除Object key3

hmremove("Object key3");

//创建数组,声明i作为数组下标

String s[]=new String[hmsize()];

int i=0;

//显示内容

Set set=hmkeySet();

Iterator it=setiterator();

while(ithasNext()){

String t=(String)itnext();

Systemoutprintln(t);

s[i++]=t;

}

//测试数组是否添加值

Systemoutprintln("===================");

for(int t=0;t<=2;t++)

Systemoutprintln(s[t]);

}

}

Hashmap是一种非常常用的、应用广泛的数据类型,最近研究到相关的内容,就正好复习一下。网上关于hashmap的文章很多,但到底是自己学习的总结,就发出来跟大家一起分享,一起讨论。

1HashMap的数据结构:在java 中 数据结构,最基本 也就两种 一种数组 一种模拟指针。所有的数据结构都可以用这两个基本结构来构造的,hashmap也不例外。Hashmap实际上是一个数组和链表的结合体。数组的默认长度为16,

2hashMap源码解析

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 初始化容量大小 

static final int MAXIMUM_CAPACITY = 1 << 30; ///容器最大值

static final float DEFAULT_LOAD_FACTOR = 075f; //加载影子

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, tablelength); //调用完hash算法后,详情见下方

 for (Entry e = table[i]; e != null; e = enext) { //循环判断当前数组下标为Entry的实体 将当前key相同的替换为最新的值

            Object k;

            if (ehash == hash && ((k = ekey) == key || keyequals(k))) {

                V oldValue = evalue;

                evalue = value;

                erecordAccess(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 sunmiscHashingstringHash32((String) k); //使用stringHash32算法得出key   的hash值

        }

        h ^= khashCode(); //调用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 tablelength);

            hash = (null != key) hash(key) : 0;

          //// 设置“bucketIndex”位置的元素为“新Entry”,// 设置“e”为“新Entry的下一个节点”

            bucketIndex = indexFor(hash, tablelength);

        }

        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会这样做: Bnext = A ,table[0] = B,如果又进来C,index也等于0,那么 Cnext = 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 : entrygetValue();

    }

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, tablelength)]; //获取当前Entry 循环遍历

            e != null;

            e = enext) {

            Object k;

            if (ehash == hash &&

                ((k = ekey) == key || (key != null && keyequals(k))))

                return e;

        }

        return null;

    }

扩展问题:

1当前我们的hashMap中越来越大的之后,"碰撞"就越来越明显,那么如何解决碰撞呢?扩容!

当hashmap中的元素个数超过数组大小captiloadFactor时,就会进行数组扩容,loadFactor的默认值为075,也就是说,默认情况下,数组大小为16,那么当hashmap中元素个数超过16075=12的时候,就把数组的大小扩展为216=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的 *** 作,所以如果我们已经预知hashmap中元素的个数,那么预设元素的个数能够有效的提高hashmap的性能。比如说,我们有1000个元素new HashMap(1000), 但是理论上来讲new HashMap(1024)更合适,不过上面annegu已经说过,即使是1000,hashmap也自动会将其设置为1024。 但是new HashMap(1024)还不是更合适的,因为0751000 < 1000, 也就是说为了让075 size > 1000, 我们必须这样new HashMap(2048)才最合适,既考虑了&的问题,也避免了resize的问题

HashMap的两种遍历方式

第一种

Map map = newHashMap();

Iterator iter = mapentrySet()iterator();

while(iterhasNext()) {

MapEntry entry = (MapEntry) iternext();

Object key = entrygetKey();

Object val = entrygetValue();

}

效率高,以后一定要使用此种方式!

第二种

Map map = newHashMap();

Iterator iter = mapkeySet()iterator();

while(iterhasNext()) {

Object key = iternext();

Object val = mapget(key);

}

效率低,以后尽量少使用!

归纳

简单地说,HashMap 在底层将 key-value 当成一个整体进行处理,这个整体就是一个 Entry 对象。HashMap 底层采用一个 Entry[] 数组来保存所有的 key-value 对,当需要存储一个 Entry 对象时,会根据hash算法来决定其在数组中的存储位置,在根据equals方法决定其在该数组位置上的链表中的存储位置;当需要取出一个Entry时,

也会根据hash算法找到其在数组中的存储位置,再根据equals方法从该位置上的链表中取出该Entry。

以上就是关于在页面中怎么用struts2来获取HashMap第0值全部的内容,包括:在页面中怎么用struts2来获取HashMap第0值、Java的HashMap存储数据库数据的问题、用sql语句将查询结果存入hashmap中后,怎样写一个servlet取出数据,并在jsp页面显示,最好写出代码,谢谢等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/9390427.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-27
下一篇 2023-04-27

发表评论

登录后才能评论

评论列表(0条)

保存