现在有一个100万行的信息的文件,要进行抽奖。随机取出一定量的信息。问下各位怎样做最快最优呢?先说我的做法:
1.通过bufferreader按行读取
2.存入一个stringbuffer中,结束后,再split(",")存入一个string数组a中。
3.通过a.length得出数据量总数(比如num=100万)。
4.进行ran.nextInt(num)取得中奖信息的下标,存入list中。
5.a[list.get(i)],得到抽奖结果。这有个弊端,如果多人同时操作的话,服务器照样卡死。
这是我所能做的最大的优化了,不知各位还能否有更好的方法。

解决方案 »

  1.   

    用RandomAccessFile,里面有seek方法,是偏移量,再读取的话,是从偏移位置开始读取。
    如果每行数据长度一样的话,就好办了,直接偏移随机数*长度,然后读取一行数据。
    如果每行数据长度不一样,就需要你程序控制了。
    流的话,也有skip方法。
      

  2.   

    我觉得lz卡死主要出在StringBuffer上面,1000000行*每行50字符好了,放到StringBuffer里面,能把heap给爆了
    lz不想大改的话,这样改
    取行数  while((line = reader.readLine())!=null){ count++; }
    取随机数ran
    跳过ran行,取一行 for(int i=0;i<ran;i++){  reader.readLine(); } line = reader.readLine();至少内存不会爆
      

  3.   

    没说清楚,是 StringBuffer.toString().split(",")的时候heap用得超多,会爆的
      

  4.   

    跳过ran行?跳过ran行可以直接读了
      

  5.   

    不能么?
    ran.nextInt(num)  存入list中。
    a[list.get(i)]  得到结果
    不就是找第ran行么反正先把空间爆了的问题解决了,时间的问题再说,这个优化有必要好好写的,改进的空间很大丫
      

  6.   

    所谓的一行就是以'\r' '\n'(或者只是'\n')结尾的字节序,如果字符串类 不好处理,试试字节吧。
      

  7.   

    做个缓存把数据都缓存到一个map里面,所有用户都访问一个map比较好点,
    如果每个用户访问都读文件,并发量大也吃不消
      

  8.   

    忘了说一点,抽奖的结果会有15万以上的数据,funfenffun和ghsau的方法不管哪种,都会不停的去遍历文件,耗时过长。两种方法我已经试过了,均超过了10分钟,weblogic超时停止了。而且,10分钟也太长了点,最好能在2分钟内搞定。
    RandomAccessFile的问题:seek每次定位其实都是遍历一次文件,不过效率要比funfenffun的那种快一些而已。
      

  9.   

    不知道你是怎么做, 这是我做的测试,随机读15万条数据,耗时:5219毫秒.
    public class BigFileRead {
    public static void main(String[] args){
    t2();
    // t1();
    }

    //随机读取
    static void t2(){
    File fd = new File("e:\\test\\fd.dt");
    File fi = new File("e:\\test\\fi.dt");
    RandomAccessFile fdIn = null;
    RandomAccessFile fiIn = null;
    byte[] buffer = new byte[512];
    long post = 0;
    int ri = 0;

    int dataLen = 0;
    long dataPost = 0; 
    int readLen = 0;
    long a = 0;

    try{
    fdIn = new RandomAccessFile(fd,"r"); 
    fiIn = new RandomAccessFile(fi,"r");

    a = System.currentTimeMillis();

    for(int i=0;i<15*100*100;i++){
    for(;;){
    ri = (int) (Math.random()*100*100*100);

    if(ri<0 || ri>100*100*100){
    continue;
    }else{
    break;
    }
    }

    post = ri*12;

    fiIn.seek(post);
    fiIn.read(buffer,0,12);
    dataPost = Utility.byte2Long(buffer, 0);
    dataLen = Utility.byte2Int(buffer,8);

    fdIn.seek(dataPost);
    readLen = fdIn.read(buffer, 0, dataLen);
    if(0 < readLen){
    System.out.println(i+":"+( new String(buffer, 0, readLen)));
    }
    }

    System.out.println("耗时:" + (System.currentTimeMillis()-a));
    }catch(Exception ex){
    ex.printStackTrace();
    }finally{
    try{
    if(null != fdIn) fdIn.close();
    }catch(Exception ex){
    }

    try{
    if(null != fiIn) fiIn.close();
    }catch(Exception ex){
    }
    }
    }

    //创建文件
    static void t1(){
    File fd = new File("e:\\test\\fd.dt");//数据文件
    File fi = new File("e:\\test\\fi.dt");//索引
    FileOutputStream fdOut = null;
    FileOutputStream fiOut = null;
    byte[] buffer = null;
    long offset=0;
    int len = 0;

    try{
    fdOut = new FileOutputStream(fd);
    fiOut = new FileOutputStream(fi);

    for(int i=0,end=100*100*100;i<end;i++){
    buffer = ("<<_"+i+"_>>").getBytes();
    fdOut.write(buffer);

    len = buffer.length;
    offset += len;
    fiOut.write(Utility.long2Byte(offset));
    fiOut.write(Utility.int2Byte(len));

    }
    }catch(Exception ex){
    ex.printStackTrace();
    }finally{
    try{
    if(null != fdOut) fdOut.close();
    }catch(Exception ex){
    }

    try{
    if(null != fiOut) fiOut.close();
    }catch(Exception ex){
    }
    }
    }
    }
      

  10.   

    额楼上,Utility.byte2Int这个方法我本地没有包,没法测试。我的意思其实是说,seek方法其实也是从文件头定位到文件尾的,换句话说,也就是每次定位都会遍历一次这个100W行的数据(遍历到需要定位的位置为止),所以即使是不完全遍历,15W次的效率也会很低。这两天我又研究了下,找到个简单的方法。
    首先生成100w以内的15w个随机数字,然后将其排序(存入a数组中),接着开始遍历这个文件,读一行,i++,然后当i=a[j]的时候,读取这行信息进行下面的操作,接着j++,当j>15W的时候,退出遍历。这样只需要一次遍历就取出了所有随机的数据。
      

  11.   

    如果你仔细看t1()这个方法的话,你会发现我建了个索引文件,你读一个100w行的文件,一行一行的跳肯定很慢,有索引的好处就是直接跳到指定位置。另外,Utility.byte2Int在我的博客里有。