The autosizeColumn()
function very slow and unneficient. Even authors of apache POI mentioned in docs, that:
This process can be relatively slow on large sheets, ...
Calculating and setting the cell's width manually is way faster - in my case I reduced the time from ~25,000ms to ~1-5ms.
This is how to achieve it (I was basing on Vladimir Shcherbukhin's answer:
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet();
final int[] maxNumCharactersInColumns = new int[headers.length]; // maximum number of characters in columns. Necessary to calculate the cell width in most efficient way. sheet.autoSizeColumn(...) is very slow.
Row headersRow = sheet.createRow(0);
CellStyle headerStyle = createHeadersStyle(workbook); // createHeadersStyle() is my own function. Create headers style if you want
for (int i = 0; i < headers.length; i++) { // create headers
Cell headerCell = headersRow.createCell(i, CELL_TYPE_STRING);
headerCell.setCellValue(headers[i]);
headerCell.setCellStyle(headerStyle);
int length = headers[i].length();
if (maxNumCharactersInColumns[i] < length) { // adjust the columns width
maxNumCharactersInColumns[i] = length + 2; // you can add +2 if you have filtering enabled on your headers
}
}
int rowIndex = 1;
for (List<Object> rowValues : rows) {
Row row = sheet.createRow(rowIndex);
int columnIndex = 0;
for (Object value : rowValues) {
Cell cell = createRowCell(row, value, columnIndex); // createRowCell() is my own function.
int length;
if (cell.getCellType() == Cell.CELL_TYPE_STRING) {
String cellValue = cell.getStringCellValue();
// this is quite important part. In some excel spreadsheet you can have a values with line-breaks. It'll be cool to handle that scenario :)
String[] arr = cellValue.split("\n"); // if cell contains complex value with line breaks, calculate only the longest line
length = Arrays.stream(arr).map(String::length).max(Integer::compareTo).get();
} else {
length = value != null ? value.toString().length() : 0;
}
if (maxNumCharactersInColumns[columnIndex] < length) { // if the current cell value is the longest one, save it to an array
maxNumCharactersInColumns[columnIndex] = length;
}
columnIndex++;
}
rowIndex++;
}
for (int i = 0; i < headers.length; i++) {
int width = (int) (maxNumCharactersInColumns[i] * 1.45f) * 256; // 1.45f <- you can change this value
sheet.setColumnWidth(i, Math.min(width, MAX_CELL_WIDTH)); // <- set calculated cell width
}
sheet.setAutoFilter(new CellRangeAddress(0, 0, 0, headers.length - 1));
ByteArrayOutputStream output = new ByteArrayOutputStream();
workbook.write(output);
workbook.close();