统计数据之HashMap

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/diaomeng11/article/details/52335050

因为工作需求,需要将数据库内大量数据取出来统计之后存入,因为数据了太大,我一开始就有些顾虑,毕竟新手,知道自己还有所欠缺。

果不其然,第一次写出来的是一个很蠢的统计过程,大致过程是取出数据,然后放入map,存入过程中,遍历map,查看是否存在数据,这关键的,最烂的一步,以至于我测试的时候,使用10W条测试就已经跑死了。

大致代码贴出来以示警戒:

	private void infosOne(List<String> list){
		long startTime=System.currentTimeMillis();
		boolean isT = false;
		Map<String,Integer> jokeInfos1 = new HashMap<String, Integer>();
		for(String d : list){			
			String[] jokeIds = d.split(",");
			for(String jid: jokeIds){
				for (Map.Entry<String,Integer> entry : jokeInfos1.entrySet()) {
					if(entry.getKey().equals(jid)){
						isT = true;
						entry.setValue(entry.getValue()+1);
//						System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
						break;
					}
				}
				if(!isT){
					jokeInfos1.put(jid, 1);
				}
				isT = false;
			}
		}
		long endTime=System.currentTimeMillis();
		System.out.println("infosOne 遍历map 用时="+(endTime-startTime)+"ms 共"+jokeInfos1.size()+"条记录");
//		for (Map.Entry<String,Integer> entry : jokeInfos1.entrySet()) {
//			System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());	
//			
//		}
	}	
后来和同学聊起,受他们指点,决定使用get(key)、containsKey来代替遍历,效果果然好很多

第一次测试使用的是60W条,大量重复数据

		List<String> sList = new ArrayList<String>();
		for(int i = 0;i<100000; i++){
			sList.add("123,333,421,3,12,44,51,242,554,999,");
			sList.add("28,888,221,8,12,22,51,222,552,777,");
			sList.add("14,444,451,4,15,44,51,545,554,666,");
			sList.add("17,777,711,7,11,77,11,171,117,222,");
			sList.add("1123,71,1,7,12,5,");
			sList.add("88,");
			sList.add("628,");
			sList.add("8532,");
			sList.add("13,177,711,7,11,77,11,171,117,222,");
		}

这个时候差距就已经出来了,测试结果如下

infosContainsKey 用时=2235ms 共41条记录
infosGetKey 用时=1843ms 共41条记录
infosOne 遍历map 用时=9886ms 共41条记录

 然后我在循环中加了一条数据,很冲动 
 

			sList.add(i+",177,711,7,11,77,11,171,117,"+(i+4)+",");
当时跑出来的结果是这样的

infosContainsKey 用时=2980ms 共100004条记录
infosGetKey 直接get 用时=2381ms 共100004条记录

为啥没有遍历的结果?因为我没等出来,虽然最终会出来,但是完全不是我想要的结果,这种过程也没必要测出来,警讯大家不用就是了。

然后我把10W循环缩小成了1W,查看结果:

		for(int i = 0;i<10000; i++){

infosContainsKey 用时=347ms 共10004条记录
infosGetKey 直接get 用时=264ms 共10004条记录
infosOne 遍历map 用时=66471ms 共10004条记录


跑了几次,结果相差不大,总之这时候差距就体现出来了,这方法真的是。。。太烂了

然后观察几次结果,发现使用get(key)每次都会比containsKey快一些

get(key)是这样的

public V get(Object key) {
        if (key == null)
            return getForNullKey();
        Entry<K,V> entry = getEntry(key);

        return null == entry ? null : entry.getValue();
    }

    /**
     * Offloaded version of get() to look up null keys.  Null keys map
     * to index 0.  This null case is split out into separate methods
     * for the sake of performance in the two most commonly used
     * operations (get and put), but incorporated with conditionals in
     * others.
     */
    private V getForNullKey() {
        if (size == 0) {
            return null;
        }
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
            if (e.key == null)
                return e.value;
        }
        return null;
    }

final Entry<K,V> getEntry(Object key) {
        if (size == 0) {
            return null;
        }

        int hash = (key == null) ? 0 : hash(key);
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }


containsKey是这样的

  public boolean containsKey(Object key) {
        return getEntry(key) != null;
    }

    /**
     * Returns the entry associated with the specified key in the
     * HashMap.  Returns null if the HashMap contains no mapping
     * for the key.
     */
    final Entry<K,V> getEntry(Object key) {
        if (size == 0) {
            return null;
        }

        int hash = (key == null) ? 0 : hash(key);
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }

细看之下发现,这俩方法几乎差不多呀,归根到底,都是getEntry(key),那么时间差在什么地方呢?我先把我写的两个方法贴出来:

	private void infosContainsKey(List<String> list){
		long startTime=System.currentTimeMillis();
		Map<String,Integer> jokeInfos2 = new HashMap<String, Integer>();
		for(String d : list){			
			String[] jokeIds = d.split(",");
			for(String jid: jokeIds){
				if(jokeInfos2.containsKey(jid)){
					jokeInfos2.put(jid, jokeInfos2.get(jid)+1);
				}else{
					jokeInfos2.put(jid, 1);
				}
			}
		}
		long endTime=System.currentTimeMillis();
		System.out.println("infosContainsKey 用时="+(endTime-startTime)+"ms 共"+jokeInfos2.size()+"条记录");
	}

	private void infosGetKey(List<String> list){
		long startTime=System.currentTimeMillis();
		Integer temp;
		Map<String,Integer> jokeInfos3 = new HashMap<String, Integer>();
		for(String d : list){			
			String[] jokeIds = d.split(",");
			for(String jid: jokeIds){
				temp=jokeInfos3.get(jid);
				if(temp!=null){
					jokeInfos3.put(jid, temp+1);
				}else{
					jokeInfos3.put(jid, 1);
				}
			}
		}
		long endTime=System.currentTimeMillis();
		System.out.println("infosGetKey 用时="+(endTime-startTime)+"ms 共"+jokeInfos3.size()+"条记录");
	}

发现,可能差距是因为,infosGetKey只用了一次get,而 infosContainsKey 先containsKey 又get了一次,所以花的时间多了一些,

于是我为了验证一下,把一次get改成了两次,然后用20W循环测试

结果如下:

infosContainsKey 用时=5557ms 共200004条记录
infosGetKey 用时=5584ms 共200004条记录

再改回来测试:

infosContainsKey 用时=5602ms 共200004条记录
infosGetKey 用时=4917ms 共200004条记录
总之呢,暂时先用get的方法来做,虽然问题可能还不一定解决,不过目前这个是最好用的,后面可能会使用数据库方面的内容来优化这个统计任务。




猜你喜欢

转载自blog.csdn.net/diaomeng11/article/details/52335050