Сортировка огромного файла в Java - PullRequest
5 голосов
/ 05 марта 2010

У меня есть файл, который состоит из одной строки:

 1 , 1 2 , 1 3 6 , 4 ,...

В этом представлении пробелы разделяют целые числа и запятые.Эта строка настолько огромна, что я не могу прочитать ее с помощью RandomAccessFile.readLine () (требуется почти 4 ГБ).Так что я создал буфер, который может содержать 10 целых чисел.Моя задача - отсортировать все целые числа в строке.

Не могли бы вы, пожалуйста, помочь?

РЕДАКТИРОВАТЬ

@ Оскар Рейес

Мне нужно записать несколько последовательностей целых чисел в файл, а затем прочитать из него.На самом деле я не знаю, как это сделать.Я новичок.Поэтому я решил использовать символы для записи целых чисел, разделителями между целыми числами являются ",", а разделителями между последовательностями являются "\ n \ r", что.Итак, я создал монстра, который читает его:

public BinaryRow getFilledBuffer(String filePath, long offset) throws IOException{
    mainFile = new RandomAccessFile(filePath, "r");

    if (mainFile.length() == 0){
        return new BinaryRow();
    }

    StringBuilder str = new StringBuilder();

    mainFile.seek(mainFile.length()-4); //that is "\n" symbol
    char chN = mainFile.readChar();

    mainFile.seek(offset);
    int i = 0;
    char nextChar = mainFile.readChar();
    while (i < 11 && nextChar != chN){
        str.append(nextChar);
        if (nextChar == ','){
            i++;
            if (i == 10){
                break;
            }
        }
        nextChar = mainFile.readChar();
    }

    if (nextChar == chN){
        position = -1;
    }else{
        position = mainFile.getFilePointer();
    }

    BinaryRow br = new BinaryRow();

    StringBuilder temp = new StringBuilder();

    for (int j = 0; j < str.length(); j++){
        if ((str.charAt(j) != ',')){
            temp.append(str.charAt(j));
            if (j == str.length() - 1){
                br.add(Integer.parseInt(temp.toString()));
            }   
        }else{
            br.add(Integer.parseInt(temp.toString()));
            temp.delete(0, temp.length());
        }
    }


    mainFile.close();
    return br;

}

Если вы могли бы посоветовать, как это сделать, пожалуйста, сделайте это =)

Ответы [ 2 ]

14 голосов
/ 05 марта 2010

Это как раз и есть источник QuickSort , тогда в памяти не хватало оперативной памяти для сортировки в памяти, поэтому они должны хранить частичные результаты на диске.

Итак, что вы можете сделать:

  1. Выберите опору.
  2. Последовательно считывайте ваш файл и сохраняйте данные ниже чем pivot в temp_file_1, а данные больше или равны pivot в temp_file_2
  3. Повторите процедуру в temp_file_1 и добавьте результат к result_file
  4. Повторите процедуру для файла temp_file_2 и добавьте результат к файлу result_file

Когда детали достаточно малы ( как 2, просто поменяйте их местами Достаточно, чтобы отсортировать их в памяти)

Таким образом, вы сможете сортировать по частям и сохранять частичные результаты во временных файлах, и вы получите окончательный файл с отсортированным результатом.

РЕДАКТИРОВАТЬ Я говорил вам, что возможна быстрая сортировка.

Кажется, вам все-таки понадобится дополнительное место для временных файлов.

Вот что я сделал.

Я создаю 40-мегабайтный файл с номерами, разделенными запятыми.

Я называю это input:

вход http://img200.imageshack.us/img200/5129/capturadepantalla201003t.png

Ввод 40mb

Во время сортировки создаются файлы tmp с сегментами «больше чем», «меньше чем», а после завершения сортировки значения отправляются в файл с именем (угадайте, что) output

обработка http://img200.imageshack.us/img200/1672/capturadepantalla201003y.png

Временные файлы создаются с частичными результатами

Наконец, все файлы tmp удаляются, и результат сохраняется в файле «output» с правильной отсортированной последовательностью чисел:

вывод http://img203.imageshack.us/img203/5950/capturadepantalla201003w.png

Наконец, файл "output" создан, обратите внимание, что он тоже составляет 40 МБ

Вот полная программа.

import java.io.*;
import java.util.*;

public class FileQuickSort {

    static final int MAX_SIZE = 1024*1024*16; // 16 megabytes in this sample, the more memory your program has, less disk writing will be used. 
    public static void main( String [] args ) throws IOException {
        fileQuickSort( new File("input"), new File("output"));
        System.out.println();
    }

    //
    static void fileQuickSort( File inputFile, File outputFile ) throws IOException {
        Scanner scanner = new Scanner( new BufferedInputStream( new FileInputStream( inputFile ), MAX_SIZE));
        scanner.useDelimiter(",");

        if( inputFile.length() > MAX_SIZE && scanner.hasNextInt()) {
            System.out.print("-");

            // put them in two buckets... 
            File lowerFile = File.createTempFile("quicksort-","-lower.tmp",new File("."));
            File greaterFile = File.createTempFile("quicksort-","-greater.tmp", new File("."));
            PrintStream  lower   = createPrintStream(lowerFile);
            PrintStream greater  = createPrintStream(greaterFile);
            PrintStream target = null;
            int pivot = scanner.nextInt();

            // Read the file and put the values greater than in a file 
            // and the values lower than in other 
            while( scanner.hasNextInt() ){
                int current = scanner.nextInt();

                if( current < pivot ){
                    target = lower;
                } else {
                    target = greater;
                }
                target.printf("%d,",current);
            }
            // avoid dropping the pivot
            greater.printf("%d,",pivot);
            // close the stream before reading them again
            scanner.close();
            lower.close();
            greater.close();
            // sort each part
            fileQuickSort( lowerFile , outputFile );
            lowerFile.delete();
            fileQuickSort( greaterFile   , outputFile);
            greaterFile.delete();

            // And you're done.
        } else {

            // Else , if you have enough RAM to process it
            // 
            System.out.print(".");
            List<Integer> smallFileIntegers = new ArrayList<Integer>();
            // Read it
            while( scanner.hasNextInt() ){
                smallFileIntegers.add( scanner.nextInt() );
            }
            scanner.close();

            // Sort them in memory 
            Collections.sort( smallFileIntegers );

            PrintStream out = createPrintStream( outputFile);
            for( int i : smallFileIntegers ) {
                out.printf("%d,",i);
            }
            out.close();
            // And your're done
        }
    }
    private static PrintStream createPrintStream( File file ) throws IOException {
        boolean append = true;
        return new PrintStream(  new BufferedOutputStream( new FileOutputStream( file, append )));
    }
}

Формат файлов number,number,number,number

Ваш текущий формат: n u m b e r , n u m b , b e r

Чтобы исправить это, вам просто нужно прочитать все и пропустить пропуски.

Добавьте еще один вопрос для этого.

1 голос
/ 05 марта 2010

Чтение в память по частям (по 100 МБ каждый?), По одному, за раз, сортировка и сохранение на диск.

Затем откройте все упорядоченные чанки, прочитайте первый элемент каждого и добавьте самый нижний к выводу. Затем прочитайте следующий элемент фрагмента, с которого вы только что прочитали, и повторите.

При слиянии вы можете сохранять массив последних прочитанных int из каждого чанка и просто перебирать его, чтобы получить самый низкий уровень. Затем вы заменяете только что использованное значение следующим элементом в чанке, из которого оно было взято.

example with chunks [1, 5, 16] [2, 9, 14] [3, 8, 10]
array [(1), 2, 3], lowest 1 --> to output
      [5, (2), 3], lowest 2 --> to output
      [5, 9, (3)], lowest 3 -->
      [(5), 9, 8],        5
      [16, 9, (8)],       8
      [16, (9), 10],      9 
...
...