44. Отфильтровать большую последовательность чисел
Условие задачи:
Необходимо обработать последовательность чисел размером до 1 Гб и получить только значения больше 5.
Доступно 1.1 Гб памяти, поэтому нельзя загружать всю последовательность и результат целиком в память.
Спойлеры к решению
Подсказки
💡 Обрабатывай данные последовательно, по одному элементу.
💡 Не создавай промежуточный список с результатами.
💡 Результат можно сразу записывать в выходной поток.
💡 Для файла удобно использовать
💡 Потребление памяти должно оставаться постоянным и не зависеть от размера входных данных.
💡 Не создавай промежуточный список с результатами.
💡 Результат можно сразу записывать в выходной поток.
💡 Для файла удобно использовать
BufferedReader и BufferedWriter.💡 Потребление памяти должно оставаться постоянным и не зависеть от размера входных данных.
Решение
Если числа записаны построчно, можно читать и фильтровать их на лету:
public void filter(
InputStream input,
OutputStream output
) throws IOException {
BufferedReader reader = new BufferedReader(
new InputStreamReader(input)
);
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(output)
);
String line;
while ((line = reader.readLine()) != null) {
int value = Integer.parseInt(line.trim());
if (value > 5) {
writer.write(Integer.toString(value));
writer.newLine();
}
}
writer.flush();
}
Метод не хранит исходную последовательность и результат в коллекциях. В памяти одновременно находится только текущая строка и буферы чтения и записи.
Если источник представлен как Iterable<Integer>, можно вернуть ленивый результат:
public Iterable<Integer> filter(Iterable<Integer> source) {
if (source == null) {
throw new IllegalArgumentException(
"Source must not be null"
);
}
return () -> new Iterator<Integer>() {
private final Iterator<Integer> iterator = source.iterator();
private Integer nextValue;
private boolean nextReady;
@Override
public boolean hasNext() {
if (nextReady) {
return true;
}
while (iterator.hasNext()) {
Integer value = iterator.next();
if (value != null && value > 5) {
nextValue = value;
nextReady = true;
return true;
}
}
return false;
}
@Override
public Integer next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
Integer result = nextValue;
nextValue = null;
nextReady = false;
return result;
}
};
}
Такой результат вычисляется только во время обхода:
for (Integer value : filter(source)) {
System.out.println(value);
}
Временная сложность — O(n), дополнительная память — O(1), без учёта внутренних буферов ввода и вывода.