Гость
Map
Форумы / Java [игнор отключен] [закрыт для гостей] / Генератор адресов ячеек Excel, улучшайзинг :) / 14 сообщений из 14, страница 1 из 1
25.05.2020, 09:59
    #39961550
Nixic
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генератор адресов ячеек Excel, улучшайзинг :)
Всем привет! Давненько я не просил посмотреть мой диковатый код и вот :)
Задачка прилетела, в её рамках понял, что нужен некий генератор.
По факту надо не больше 20-30 адресов ячеек сгенерить, можно было тупо список захардкодить, но это ж не наш путь.
Сейчас есть косяк, что больше примерно 580 адресов генерируются долго, больше 12 секунд, а уже при 600 примерно 24 секунды, 670 уже 152 секунды.
Еще есть ограничение: адреса могут генерироваться только 2-х буквенные, то есть больше чем ZA-Z(фактически ZY это последний возможный адрес) уже не получить.
Больше чем 26 * 26 + 26 - 1 не сгенерить, т.е. 701 ячейку еще можно, при 702 уже получим ошибку(java.lang.IndexOutOfBoundsException: Index: 26, Size: 26).
А ведь у экселя xlsx 16384 столбцов судя по данным гугла.
Как они это делают :)
Задача состоит в том, что надо получить список A1, B1, C1, ... AA1, AB1, AC1, ... AAA1, AAB1, AAC1 и т.д. в общем открываем любой эксель и крутим вправо, вот весь нужный список и видно в адресах столбиков. Плюс номер строки добавить, но это не сильно важно.

На входе имеем количество необходимых адресов, на выходе лист с этими адресами, ну или массив, не суть.

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


CharUtils2
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class CharUtils2 {

    static List<String> alphabetList = IntStream.rangeClosed('A', 'Z').mapToObj(c -> "" + (char) c).collect(Collectors.toList());

    private static List<String> totalAddressList = new ArrayList<>();
    private static int alphabetSize = alphabetList.size();

    public static List<String> generateColumnAddressesWithRowNum(Integer columnCount, Integer rowNum) {
        List<String> prefixAddressList = genPrefixAddress(columnCount);
        return generateColumnAddresses(prefixAddressList, null, columnCount, rowNum);
    }

    /**
     * Генерирует префиксы для адресов ячеек которые используют 2 буквы, например AA1, AB1, BA1 и и т.д.
     * Делит количество колонок(например 57) на длину англ.алфавита(26), получает 2 полных вхождения кол-ва букв алфавита в 57 и перебором по индексу складывает префиксы в список.
     * В примере получится, что в ответе будет список A и B.
     *
     * @param columnCount количество колонок
     * @return список префиксов, A, B, C и т.д.
     */
    private static List<String> genPrefixAddress(Integer columnCount) {
        List<String> prefixAddress = new ArrayList<>();
        int alphabetSize = alphabetList.size();
        if (columnCount > alphabetSize) {
            int aCount = columnCount / alphabetSize;
            for (int i = 0; i < aCount; i++) {
                prefixAddress.add(alphabetList.get(i));
            }
        }
        return prefixAddress;
    }

    /**
     * Генерирует массив адресов ячеек в одной строке.
     *
     * @param prefixAddressList список префиксов для адресов ячеек если кол-во ячеек больше чем длина англ.алфавита(26)
     * @param prefixAddress     используется в рекурсии, для генерации адреса ячейки с префиксом, например чтобы получить AA1, AB1, AC1 и т.д.
     * @param columnCount       кол-во колонок для которого нужно сгенерировать адреса ячеек
     * @param rowNum            номер строки который будет добавлен в адреса ячеек, в конец
     * @return список сгенерированных адресов.
     */
    private static List<String> generateColumnAddresses(List<String> prefixAddressList, String prefixAddress, Integer columnCount, Integer rowNum) {
        List<String> addressList = new ArrayList<>();
        for (int i = 0; i < columnCount; i++) {
            if (alphabetSize > i) {
                String address = "";
                if (prefixAddress != null) {
                    address = prefixAddress + alphabetList.get(i);
                } else {
                    address = alphabetList.get(i);
                }
                addressList.add(address + rowNum);
            }
        }
        for (String s : addressList) {
            if (!totalAddressList.contains(s))
                totalAddressList.add(s);
        }
        if (columnCount > 0) {
            for (String prefAddr : prefixAddressList) {
                columnCount = columnCount - alphabetSize;
                generateColumnAddresses(prefixAddressList, prefAddr, columnCount, rowNum);
            }
        }
        return totalAddressList;
    }

    public static void main(String[] args) {
        long m = System.currentTimeMillis();
        List<String> strings = CharUtils2.generateColumnAddressesWithRowNum(60, 1);
        System.out.println((double) (System.currentTimeMillis() - m));
//        for (int i = 0; i < strings.size(); i++) {
//            String address = strings.get(i);
//            System.out.println(address + " [" + i + "]");
//        }
    }

}


Пока писал пост, запустил генерацию 701-го адреса, время выполнения 333 сек, последние адреса ячеек:
ZW1 [698]
ZX1 [699]
ZY1 [700]
По памяти жрет 385мб по данным VisualVM
...
Рейтинг: 0 / 0
25.05.2020, 10:45
    #39961576
mayton
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генератор адресов ячеек Excel, улучшайзинг :)
Мне кажется можно переписать без использования коллекций типа List<String> strings
...
Рейтинг: 0 / 0
25.05.2020, 10:48
    #39961579
artas
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генератор адресов ячеек Excel, улучшайзинг :)
Nixic,

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
 public static void main(String []args){
        for(char i = 1;i<701;i++){
            System.out.println(getNameFromNumber(i));
        }
     }
     
    public static String getNameFromNumber(int num) {
        int numeric = (num - 1) % 26;
        char letter = (char)(65 + numeric);
        int num2 = new Integer((num - 1) / 26);
        if (num2 > 0) {
            return getNameFromNumber(num2).toString() + "" + letter;
        } else {
            return letter + "";
        }
    }


алгориитм спер у великого и могучего
...
Рейтинг: 0 / 0
25.05.2020, 10:54
    #39961582
artas
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генератор адресов ячеек Excel, улучшайзинг :)
Nixic,

проверил ща в экселе, все правильно
701 - ZY
702 - ZZ (если считать что первая кол-ка = 1)
...
Рейтинг: 0 / 0
25.05.2020, 10:57
    #39961585
mayton
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генератор адресов ячеек Excel, улучшайзинг :)
Если заинлайнить getNameFromNumber то можно избавиться от деления и от вычисления остатка.
Они - линейно зависимы от парамтера цикла.
...
Рейтинг: 0 / 0
25.05.2020, 11:01
    #39961589
artas
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генератор адресов ячеек Excel, улучшайзинг :)
mayton,

я ж честно признался, мопед не мой, я с пыха только перевел. В любом случае, я думаю вопрос скорости решен
...
Рейтинг: 0 / 0
25.05.2020, 11:17
    #39961604
mayton
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генератор адресов ячеек Excel, улучшайзинг :)
Ага. Особенно эта строка радует.

Код: java
1.
int num2 = new Integer((num - 1) / 26);
...
Рейтинг: 0 / 0
25.05.2020, 11:55
    #39961628
Nixic
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генератор адресов ячеек Excel, улучшайзинг :)
artas
mayton,
я ж честно признался, мопед не мой, я с пыха только перевел. В любом случае, я думаю вопрос скорости решен

Реально круто! Я когда гуглил что-то подобное, я искал на java, ничего толком не нашёл и написал свою дичь!))
Спасибо!
...
Рейтинг: 0 / 0
25.05.2020, 12:05
    #39961632
chpasha
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генератор адресов ячеек Excel, улучшайзинг :)
я уверен что в poi уже есть где-то утилитарный класс, который умеет пересчитывать координаты в excel-нотацию. надо было сразу туда копать. но иногда и самому полезно мозгами пошевелить, не без этого.
...
Рейтинг: 0 / 0
25.05.2020, 12:53
    #39961652
Nixic
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генератор адресов ячеек Excel, улучшайзинг :)
Спасибо всем, особенно artas'у, взял на вооружение (имена переменных еще надо отрефачить будет):

CharUtils2
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
import java.util.ArrayList;
import java.util.List;

public class CharUtils2 {

    /**
     * Генерирует массив адресов ячеек в одной строке.
     *
     * @param columnCount кол-во колонок для которого нужно сгенерировать адреса ячеек
     * @param rowNum      номер строки который будет добавлен в адреса ячеек, в конец
     * @return список сгенерированных адресов.
     */
    public static List<String> generateColumnAddressesWithRowNum(Integer columnCount, Integer rowNum) {
        List<String> addressList = new ArrayList<>();
        for (char i = 1; i <= columnCount; i++) {
            addressList.add(getNameFromNumber(i) + rowNum);
        }
        return addressList;
    }

    private static String getNameFromNumber(int num) {
        int columnIndex = Math.max(num - 1, 0);
        int numeric = columnIndex % 26;
        char letter = (char) (65 + numeric);
        int num2 = columnIndex / 26;
        if (num2 > 0) {
            return getNameFromNumber(num2) + "" + letter;
        } else {
            return letter + "";
        }
    }

    public static void main(String[] args) {
        long m = System.currentTimeMillis();
        int columnCount = 10000;
        int rowNum = 1;
        List<String> strings = generateColumnAddressesWithRowNum(columnCount, rowNum);
        for (String string : strings) {
            System.out.println(string);
        }
        System.out.println("Count addresses:" + strings.size());
        System.out.println("Completed for " + (double) (System.currentTimeMillis() - m) + " ms");
    }

}


Последние строчки в консоли:
NTN1
NTO1
NTP1
Count addresses:10000
Completed for 74.0 ms
...
Рейтинг: 0 / 0
25.05.2020, 12:57
    #39961654
mayton
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генератор адресов ячеек Excel, улучшайзинг :)
Рефачь. Возми курс на экономию ресурсов памяти. Твоей утилите полезно освоить Stream-able подход к генерации информации.
Тем более что современный Java API уже к этому располагает.
...
Рейтинг: 0 / 0
25.05.2020, 13:08
    #39961658
artas
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генератор адресов ячеек Excel, улучшайзинг :)
chpasha,
вы правы
Код: java
1.
String columnLetter = CellReference.convertNumToColString(columnNumber);
...
Рейтинг: 0 / 0
25.05.2020, 13:11
    #39961660
artas
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генератор адресов ячеек Excel, улучшайзинг :)
chpasha,
brw, их реализация

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
public static String convertNumToColString(int col) {
		// Excel counts column A as the 1st column, we
		//  treat it as the 0th one
		int excelColNum = col + 1;

		String colRef = "";
		int colRemain = excelColNum;

		while(colRemain > 0) {
			int thisPart = colRemain % 26;
			if(thisPart == 0) { thisPart = 26; }
			colRemain = (colRemain - thisPart) / 26;

			// The letter A is at 65
			char colChar = (char)(thisPart+64);
			colRef = colChar + colRef;
		}

		return colRef;
	}
...
Рейтинг: 0 / 0
25.05.2020, 13:43
    #39961679
Nixic
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Генератор адресов ячеек Excel, улучшайзинг :)
Затестил код апача, он быстрее процентов на 25 работает.
Ну, буду надеятся, что полученный опыт не пропадёт, запушил вместо "своего" метода апачевский :)
...
Рейтинг: 0 / 0
Форумы / Java [игнор отключен] [закрыт для гостей] / Генератор адресов ячеек Excel, улучшайзинг :) / 14 сообщений из 14, страница 1 из 1
Целевая тема:
Создать новую тему:
Автор:
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]