本周在做一个数据分析的任务,大概有一百万的数据,然后里面需要用到就是在这一百万查出的数据,,然而我用了速度最慢的一个写法,下面模拟计算过程:
package com.test.set;
import java.util.*;
/**
* Created by 林晓升 on 2016/9/4.
*/
public class Test1 {
public static void main(String args[]){
long startTime=System.currentTimeMillis();
List<Integer> list=new ArrayList<>();
for(int i=0;i<1000000;i++){
list.add(new Random().nextInt(1000000));
}
System.out.println("list size:"+list.size());
Set<Integer> set=new HashSet<>();
int j=0;
for(Integer i:list){
for(Integer i2:list){
if(i==i2){
set.add(i);
}
}
j++;
System.out.println(j);
}
System.out.println("set size"+set.size());
for(int i:set){
System.out.println(i);
}
System.out.println("计算花费时间:"+(System.currentTimeMillis()-startTime)+"ms");
}
}
这里我用的是两层循环比较,可想而知这样的时间复杂度就是T(n)=O(n^2)了,那这个计算的速度可能是非常非常的慢,反正我等了一个晚上都没算出最后结果,这是可悲。后来在同事的帮助下,几句话的时间优化了下,速度竟发生天大改变
package com.test.set;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
/**
* Created by 林晓升 on 2016/9/4.
*/
public class Test2 {
public static void main(String args[]){
long startTime=System.currentTimeMillis();
Set<Integer> set1=new HashSet<>();//重复结果
Set<Integer> set2=new HashSet<>();//临时结果
for(int i=0;i<1000000;i++) {
System.out.println(i);
int num = new Random().nextInt(1000000);
if (set1.contains(num)) {
continue;
}
if (set2.contains(num)) {
set1.add(num);
continue;
}
set2.add(num);
}
System.out.println("set1 size"+set1.size());
for(int i:set1){
System.out.println(i);
}
System.out.println("计算花费时间:"+(System.currentTimeMillis()-startTime)+"ms");
}
}
代码通过先放到一个set1里面,如果set1有的话那么直接跳过,否则去判断set2,set2如果也有的话,那证明之前已添加过了相同的,则把这个值添加进set1,逻辑很简单,可正是因为这里用到了Set使用了哈希算法,查询的速度就明显大大改变了。
哈希算法之所以存取如此之快,是因为通过关键字key得到要存储记录内存的存储位置,可以想下我的第一种计算方式,相当于一个人拿这个号牌,在一百万人面前挨个挨个去问我是否和你相同,如果运气好的话可能很快就找到了,运气差的话可能要问上一百万次。而哈希算法是根据这个关键字key去存放,比如学号2016100-2016199是一班的,学号2016200-2016299是二班的,以此类推,那么当一个人的学号是2016801那么这个人会被放到8班的位置上,如果有人要找他的时候,就会去问 我现在要找学号是2016801的这个人,那么学生处的人就会根据学号的写法知道你要的这个人在8班的一个座位上,这样就是通过某种约定俗成的规则找到要找的那个人。
也就是说,我们只需要通过某个函数f,使得存储位置=f (关键字)那样我们可以通过查找关键字不需要比较就可获得需要的记录的存储位置。这就是一种新的存储技术一一散列技术(哈希算法)。散列技术是在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使得每个关键字key 对应一个存储位置f (key)。查找时,根据这个确定的对应关系找到给定值key 的映射f (key) ,若查找集合中存在这个记录,则必定在f (key) 的位置上。这里我们把这种对应关系f 称为散列函数, 又称为哈希(Hash) 函数。按这个思想,采用散列技术将记录存储在一块连续的存储空间中,这块连续存储空间称为散列表或哈希表(Hash table)。 那么关键字对应的记录存储位置我们称为散列地址。
整个散列过程其实就是两步。
(1) 在存储时,通过散列函数计算记录的散列地址,并按此散列地址存储该记录。
(2) 当查找记录时,我们通过同样的散列函数计算记录的散列地址,按此散列地址访问该记录。由于存取用的是同一个散列函数, 因此结果当然也是相同的。所以说,散列技术既是一种存储方法,也是一种查找方法。然而它与线性表、树、图等结构不同的是,前面几种结构,数据元素之间都存在某种逻辑关系,可以用连线图示表示出来,而散列技术的记录之间不存在什么逻辑关系,它只与关键字有关联。因此,散列主要是面向查找的存储结构。
我们时常会碰到两个关键字key1 != key2,但是却有f(key1) = f(key2),这种现象我们称为哈希冲突,如果没有哈希冲突,散列表是一种非常高效的查找数据结构,其时间复杂度为O(1);