Самый быстрый способ загрузки длинных данных [дубликат]

Если вам просто нужно проверить, есть ли какие-либо элементы в массиве

if (empty($playerlist)) {
     // list is empty.
}

Если вам нужно очистить пустые значения перед проверкой (обычно делается для предотвращения explode странных строк):

foreach ($playerlist as $key => $value) {
    if (empty($value)) {
       unset($playerlist[$key]);
    }
}
if (empty($playerlist)) {
   //empty array
}
16
задан Crozin 22 April 2010 в 20:02
поделиться

7 ответов

Спасибо за каждый ответ, но я уже нашел метод, соответствующий моим критериям:

BufferedInputStream bis = new BufferedInputStream(new FileInputStream("./path"));
int n = readInt(bis);
int t = readInt(bis);
int array[] = new int[n];
for (int i = 0; i < n; i++) {
    array[i] = readInt(bis);
}

private static int readInt(InputStream in) throws IOException {
    int ret = 0;
    boolean dig = false;

    for (int c = 0; (c = in.read()) != -1; ) {
        if (c >= '0' && c <= '9') {
            dig = true;
            ret = ret * 10 + c - '0';
        } else if (dig) break;
    }

    return ret;
}

Для чтения 1 миллиона целых чисел требуется всего около 300 мс!

12
ответ дан Crozin 21 August 2018 в 03:51
поделиться
  • 1
    что делает ваша переменная int t? – Adam Johns 27 June 2014 в 16:48
  • 2
    @AdamJohns Абсолютно ничего, это всего лишь второе число из файла (см. Формат файла из вопроса). Переменная array также ничего не делает. ;) – Crozin 28 June 2014 в 07:36
  • 3
    Отлично. Это было ок. В 2 раза быстрее, чем использование StringTokenizer в моей проблеме (чтение 1 миллион целых чисел до 1 миллиона каждый). – jbarrameda 28 February 2017 в 23:26

Можно переформатировать вход так, чтобы каждое целое было на отдельной строке (вместо одной длинной строки с миллионом целых чисел), вы должны видеть значительно улучшенную производительность, используя Integer.parseInt(BufferedReader.readLine()) из-за более умного буферизации по строке, а не чтобы разделить длинную строку на отдельный массив строк.

Edit: я протестировал это и смог прочитать результат, полученный seq 1 1000000, в массив из int в течение полусекунды, но конечно, это зависит от машины.

1
ответ дан Arkku 21 August 2018 в 03:51
поделиться
  • 1
    К сожалению, я не могу изменить формат файла. Он должен быть двумя целыми числами, разделенными одним пространством в первой строке и 1 миллионом целых чисел во второй строке (также разделенным одним пробелом). – Crozin 22 April 2010 в 20:04

StreamTokenizer может быть быстрее, как предлагается здесь .

2
ответ дан Community 21 August 2018 в 03:51
поделиться
  • 1
    Фактически StreamTokenizer, кажется, является самым быстрым решением до сих пор (пожалуйста, проверьте мое обновление вопроса). Но для чтения необходимых данных все еще требуется около 1400 мс. – Crozin 22 April 2010 в 19:59
  • 2
    спасибо TG, StreamTokenizer очень приятный. – KevinDTimm 22 April 2010 в 20:07
  • 3
    Отлично. См. Также информативный ответ @Kevin Brock: stackoverflow.com/questions/2693223/… – trashgod 23 April 2010 в 04:00

Вы можете уменьшить время для результата StreamTokenizer, используя BufferedReader:

Reader r = null;
try {
    r = new BufferedReader(new FileReader(file));
    final StreamTokenizer st = new StreamTokenizer(r);
    ...
} finally {
    if (r != null)
        r.close();
}

Кроме того, не забудьте закрыть ваши файлы, как я показал здесь.

Вы также можете сэкономить время, используя пользовательский токенизатор для ваших целей:

public class CustomTokenizer {

    private final Reader r;

    public CustomTokenizer(final Reader r) {
        this.r = r;
    }

    public int nextInt() throws IOException {
        int i = r.read();
        if (i == -1)
            throw new EOFException();

        char c = (char) i;

        // Skip any whitespace
        while (c == ' ' || c == '\n' || c == '\r') {
            i = r.read();
            if (i == -1)
                throw new EOFException();
            c = (char) i;
        }

        int result = (c - '0');
        while ((i = r.read()) >= 0) {
            c = (char) i;
            if (c == ' ' || c == '\n' || c == '\r')
                break;
            result = result * 10 + (c - '0');
        }

        return result;
    }

}

Не забудьте использовать для этого BufferedReader. Этот пользовательский токенизатор предполагает, что входные данные всегда полностью действительны и содержат только пробелы, новые строки и цифры.

Если вы много читаете эти результаты, и эти результаты мало меняются, вы, вероятно, должны сохранить массив и следить за последним измененным временем файла. Затем, если файл не изменился, просто используйте кешированную копию массива, и это значительно ускорит результаты. Например:

public class ArrayRetriever {

    private File inputFile;
    private long lastModified;
    private int[] lastResult;

    public ArrayRetriever(File file) {
        this.inputFile = file;
    }

    public int[] getResult() {
        if (lastResult != null && inputFile.lastModified() == lastModified)
            return lastResult;

        lastModified = inputFile.lastModified();

        // do logic to actually read the file here

        lastResult = array; // the array variable from your examples
        return lastResult;
    }

}
2
ответ дан Kevin Brock 21 August 2018 в 03:51
поделиться
  • 1
    Спасибо за ответ - я проверю это завтра - надеюсь, что это то, что я ищу. – Crozin 22 April 2010 в 22:32
  • 2
    +1 Возможно, стоит указать размер буфера при конструировании BufferedReader. – trashgod 23 April 2010 в 03:57

Сколько памяти у вас на компьютере? Вы можете столкнуться с проблемами GC.

Самое лучшее, что нужно сделать, это обработать данные по одной строке за раз, если это возможно. Не загружайте его в массив. Загрузите то, что вам нужно, обработайте, запишите и продолжите.

Это уменьшит объем памяти и по-прежнему будет использовать то же количество файлов IO

1
ответ дан Pyrolistical 21 August 2018 в 03:51
поделиться
  • 1
    Похоже, что его вторая строка - одна линия, содержащая миллион чисел. – NG. 22 April 2010 в 19:39
  • 2
    Если мои расчеты верны, то 1 млн. int обойдется мне в 7 МБ памяти - это не так много. Мне просто нужно загрузить эти данные из файла в память - мне понадобится это для некоторых вычислений, требующих загрузки целой информации. – Crozin 22 April 2010 в 19:45

Я бы расширил FilterReader и проанализировал строку, поскольку она читается в методе read (). Попросите метод getNextNumber вернуть числа. Код оставлен как упражнение для читателя.

0
ответ дан Skip Head 21 August 2018 в 03:51
поделиться

Использование StreamTokenizer на BufferedReader даст вам неплохую производительность. Вам не нужно писать собственную функцию readInt ().

Вот код, который я использовал для локального тестирования производительности:

/**
 * Created by zhenhua.xu on 11/27/16.
 */
public class MyReader {

private static final String FILE_NAME = "./1m_numbers.txt";
private static final int n = 1000000;

public static void main(String[] args) {
    try {
        readByScanner();
        readByStreamTokenizer();
        readByStreamTokenizerOnBufferedReader();
        readByBufferedInputStream();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public static void readByScanner() throws Exception {
    long startTime = System.currentTimeMillis();

    Scanner stdin = new Scanner(new File(FILE_NAME));
    int array[] = new int[n];
    for (int i = 0; i < n; i++) {
        array[i] = stdin.nextInt();
    }

    long endTime = System.currentTimeMillis();
    System.out.println(String.format("Total time by Scanner: %d ms", endTime - startTime));
}

public static void readByStreamTokenizer() throws Exception {
    long startTime = System.currentTimeMillis();

    StreamTokenizer st = new StreamTokenizer(new FileReader(FILE_NAME));
    int array[] = new int[n];

    for (int i = 0; st.nextToken() != StreamTokenizer.TT_EOF; i++) {
        array[i] = (int) st.nval;
    }

    long endTime = System.currentTimeMillis();
    System.out.println(String.format("Total time by StreamTokenizer: %d ms", endTime - startTime));
}

public static void readByStreamTokenizerOnBufferedReader() throws Exception {
    long startTime = System.currentTimeMillis();

    StreamTokenizer st = new StreamTokenizer(new BufferedReader(new FileReader(FILE_NAME)));
    int array[] = new int[n];

    for (int i = 0; st.nextToken() != StreamTokenizer.TT_EOF; i++) {
        array[i] = (int) st.nval;
    }

    long endTime = System.currentTimeMillis();
    System.out.println(String.format("Total time by StreamTokenizer with BufferedReader: %d ms", endTime - startTime));
}

public static void readByBufferedInputStream() throws Exception {
    long startTime = System.currentTimeMillis();

    BufferedInputStream bis = new BufferedInputStream(new FileInputStream(FILE_NAME));
    int array[] = new int[n];
    for (int i = 0; i < n; i++) {
        array[i] = readInt(bis);
    }

    long endTime = System.currentTimeMillis();
    System.out.println(String.format("Total time with BufferedInputStream: %d ms", endTime - startTime));
}

private static int readInt(InputStream in) throws IOException {
    int ret = 0;
    boolean dig = false;

    for (int c = 0; (c = in.read()) != -1; ) {
        if (c >= '0' && c <= '9') {
            dig = true;
            ret = ret * 10 + c - '0';
        } else if (dig) break;
    }

    return ret;
}

Результаты, которые я получил:

  • Общее время от сканера: 789 мс
  • Общее время по StreamTokenizer: 226 мс
  • Общее время с помощью StreamTokenizer с BufferedReader: 80 мс
  • Общее время по BufferedInputStream: 95 мс
0
ответ дан Zhenhua Xu 21 August 2018 в 03:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: