java集合框架HashMap与Hashtable的使用

首先先介绍一下集合框架

Java集合框架主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值对映射。Collection接口又有3种子类型,List、Set和Queue再下面是一些抽象类,最后是具体实现类,常用的有ArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap等等。

 

    collection框架有自己的接口和实现,主要分为Set接口,List接口和Queue接口。它们有各自的特点,Set的集合里不允许对象有重复的值,无序的,List允许有重复,有顺序的,它对集合中的对象进行索引,Queue的工作原理是FCFS算法(First Come, First Serve)。

Collection接口中的全部方法。

有几个比较常用的方法,比如方法add()添加一个元素到集合中,addAll()将指定集合中的所有元素添加到集合中,contains()方法检测集合中是否包含指定的元素,toArray()方法返回一个表示集合的数组。

// 将指定的元素插入此列表中的指定位置。
		arrayList.add(1,"小张三");
		pringArrayList(arrayList);
		// 用指定的元素替代此列表中指定位置上的元素。
		arrayList.set(2, "小李四");
		pringArrayList(arrayList);
		// 移除此列表中指定位置上的元素。
		arrayList.remove(0);
	printLinkedList(linkedList);
		// 返回此列表中首次出现的指定元素的索引,如果此列表中不包含该元素,则返回 -1。
		System.out.println(linkedList.indexOf("李四"));
		printLinkedList(linkedList);
		// 获取但不移除此列表的第一个元素;如果此列表为空,则返回 null。
		System.out.println(linkedList.peekFirst());
		printLinkedList(linkedList);
		// 获取但不移除此列表的最后一个元素;如果此列表为空,则返回 null。
		System.out.println(linkedList.peekLast());
		printLinkedList(linkedList);
		// 获取并移除此列表的第一个元素;如果此列表为空,则返回 null。
		System.out.println(linkedList.pollFirst());
		printLinkedList(linkedList);
		// 获取并移除此列表的最后一个元素;如果此列表为空,则返回 null。
		System.out.println(linkedList.pollLast());
		printLinkedList(linkedList);

ArrayList

ArrayList是用数组存储元素的,这个数组可以动态创建,如果元素个数超过了数组的容量,那么就创建一个更大的新数组,并将当前数组中的所有元素都复制到新数组中

LinkedList是在一个链表中存储元素。

链表和数组的最大区别在于它们对元素的存储方式的不同导致它们在对数据进行不同操作时的效率不同,同样,ArrayList与LinkedList也是如此,实际使用中我们需要根据特定的需求选用合适的类,如果除了在末尾外不能在其他位置插入或者删除元素,那么ArrayList效率更高,如果需要经常插入或者删除元素,就选择LinkedList。

Queue

队列是一种先进先出的数据结构,元素在队列末尾添加,在队列头部删除。Queue接口扩展自Collection,并提供插入、提取、检验等操作。

方法offer表示向队列添加一个元素,poll()与remove()方法都是移除队列头部的元素,两者的区别在于如果队列为空,那么poll()返回的是null,而remove()会抛出一个异常。方法element()与peek()主要是获取头部元素,不删除。

HashMap与Hashtable的比较

1、继承的父类不同 
HashMap和Hashtable不仅作者不同,而且连父类也是不一样的。HashMap是继承自AbstractMap类,而HashTable是继承自Dictionary类。不过它们都实现了同时实现了map、Cloneable(可复制)、Serializable(可序列化)这三个接口

HashMap与实体的使用

	@RequestMapping("/getHisByUserId/{userId}")
	public AssembleJSON getChromaHis(@PathVariable Integer userId){
		return AssembleJSON.SUCCESS(service.getDataHisByUserId(userId));
	}
	@Override
	public Map<String, Object> getDataHisByUserId(Integer userId){
		Map<String, Object> map = new HashMap<>();

		List<Date> xAxis = new ArrayList<>();
		List<RtuAttribute> rtuAttributes = rtuAttributeMapper.getDataHisByUserId(userId);
		List<Map<String, Object>> seriesDatas = new ArrayList<>();
		if (rtuAttributes != null && rtuAttributes.size() > 0) {
			for (RtuAttribute rtuAttribute : rtuAttributes) {
				//根据tagCode获取所有的数据
				List<DataHis> dataHisByTagCode = mapper.getDataHisByTagCode(rtuAttribute.getTagCode());
				Map<String, Object> seriesData = new HashMap<>();
				seriesData.put("name", rtuAttribute.getAttributeName());
				seriesData.put("type", "line");
				List<Double> chromaHisData = new ArrayList<>();
				if (dataHisByTagCode != null && dataHisByTagCode.size() > 0) {
					for (DataHis dataHis : dataHisByTagCode) {
						chromaHisData.add(dataHis.getTagValue());
						xAxis.add(dataHis.getTagTime());
					}
				}
				seriesData.put("data", chromaHisData);
				seriesDatas.add(seriesData);
			}
		}

		map.put("xAxisData", xAxis);
		map.put("seriesDatas", seriesDatas);

		return map;
	}
    var chromaHis = echarts.init(document.getElementById('chromaHis'));
    $.get("alter/datahis/getHisByUserId/"+userId,function (data) {
        var dataHis = data.data;
        optionHis = {
            title: {
                text: '曲线'
            },
            tooltip: {
                trigger: 'axis'
            },
            grid: {
                left: '3%',
                right: '4%',
                bottom: '3%',
                containLabel: true
            },
            xAxis: {
                type: 'category',
                boundaryGap: false,
                data:dataHis.xAxisData
            },
            yAxis: {
                type: 'value'
            },
            series: dataHis.seriesDatas
        };


        chromaHis.setOption(optionHis, true);
    });

Hashtable使用

	@Override
	public Hashtable<String, List<DicItem>> getDicByCodes(String dictCodes,Integer userId, String orgManageDataCode,String orgDataCode, Integer orgLevel){
		Hashtable<String,List<DicItem>> hashtable = new Hashtable<>();
		String[] codes = dictCodes.split(",");
		List<DicItem> dicItems = new ArrayList<>();
		for(String code : codes){
			dicItems = getDicByCode(code,userId,orgManageDataCode,orgDataCode,orgLevel);
			if(dicItems != null){
				hashtable.put(code, dicItems);
			}
		}
		return hashtable;
	}

2、对外提供的接口不同 
Hashtable比HashMap多提供了elments() 和contains() 两个方法。

elments() 方法继承自Hashtable的父类Dictionnary。elements() 方法用于返回此Hashtable中的value的枚举。

contains()方法判断该Hashtable是否包含传入的value。它的作用与containsValue()一致。事实上,contansValue() 就只是调用了一下contains() 方法。
3、对Null key 和Null value的支持不同 
Hashtable既不支持Null key也不支持Null value。Hashtable的put()方法的注释中有说明。 

当key为Null时,调用put() 方法,运行到下面这一步就会抛出空指针异常。因为拿一个Null值去调用方法了。 

è¿éåå¾çæè¿°

当value为null值时,Hashtable对其做了限制,运行到下面这步也会抛出空指针异常。 

è¿éåå¾çæè¿°

HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,可能是 HashMap中没有该键,也可能使该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。
4、线程安全性不同 
Hashtable是线程安全的,它的每个方法中都加入了Synchronize方法。在多线程并发的环境下,可以直接使用Hashtable,不需要自己为它的方法实现同步

HashMap不是线程安全的,在多线程并发的环境下,可能会产生死锁等问题。具体的原因在下一篇文章中会详细进行分析。使用HashMap时就必须要自己增加同步处理,

虽然HashMap不是线程安全的,但是它的效率会比Hashtable要好很多。这样设计是合理的。在我们的日常使用当中,大部分时间是单线程操作的。HashMap把这部分操作解放出来了。当需要多线程操作的时候可以使用线程安全的ConcurrentHashMap。ConcurrentHashMap虽然也是线程安全的,但是它的效率比Hashtable要高好多倍。因为ConcurrentHashMap使用了分段锁,并不对整个数据进行锁定。
5、初始容量大小和每次扩充容量大小的不同 
Hashtable默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。

创建时,如果给定了容量初始值,那么Hashtable会直接使用你给定的大小,而HashMap会将其扩充为2的幂次方大小。也就是说Hashtable会尽量使用素数、奇数。而HashMap则总是使用2的幂作为哈希表的大小。

之所以会有这样的不同,是因为Hashtable和HashMap设计时的侧重点不同。Hashtable的侧重点是哈希的结果更加均匀,使得哈希冲突减少。当哈希表的大小为素数时,简单的取模哈希的结果会更加均匀。而HashMap则更加关注hash的计算效率问题。在取模计算时,如果模数是2的幂,那么我们可以直接使用位运算来得到结果,效率要大大高于做除法。HashMap为了加快hash的速度,将哈希表的大小固定为了2的幂。当然这引入了哈希分布不均匀的问题,所以HashMap为解决这问题,又对hash算法做了一些改动。这从而导致了Hashtable和HashMap的计算hash值的方法不同 

6、计算hash值的方法不同 
为了得到元素的位置,首先需要根据元素的 KEY计算出一个hash值,然后再用这个hash值来计算得到最终的位置。

Hashtable直接使用对象的hashCode。hashCode是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值。然后再使用除留余数发来获得最终的位置。 

è¿éåå¾çæè¿°

Hashtable在计算元素的位置时需要进行一次除法运算,而除法运算是比较耗时的。 
HashMap为了提高计算效率,将哈希表的大小固定为了2的幂,这样在取模预算时,不需要做除法,只需要做位运算。位运算比除法的效率要高很多。

HashMap的效率虽然提高了,但是hash冲突却也增加了。因为它得出的hash值的低位相同的概率比较高,而计算位运算

为了解决这个问题,HashMap重新根据hashcode计算hash值后,又对hash值做了一些运算来打散数据。使得取得的位置更加分散,从而减少了hash冲突。当然了,为了高效,HashMap只做了一些简单的位处理。从而不至于把使用2 的幂次方带来的效率提升给抵消掉。
è¿éåå¾çæè¿°

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wespten

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值