Проблемы с кучей памяти на Java - PullRequest
1 голос
/ 04 октября 2019

Я пытаюсь запустить программу ниже, но я получаю ошибку OutOfMemory во время структуры цикла добавления StringBuilder.

  1. Я пытаюсь сделать что-нибудь, чтобы уменьшить использование памяти настолько, чтобы иметь возможностьпрочитать файл CSV (более 200 000 строк, но только 3 столбца: элемент, рейтинг, пользователь).
  2. Затем я создал бы двумерный массив int, в котором уникальные элементы представляют строки, уникальные пользователи представляют столбцы, а пересечение - это рейтинг.
  3. Наконец, я бы использовал StringBuilder, чтобы помочь создатьвыходной файл CSV

Спасибо за помощь и время.

      List<String> userList = new ArrayList<String>();
      List<String> itemList = new ArrayList<String>();


      FileInputStream stream = null;
      Scanner scanner = null;
      int[][] layout = new int[10672][24303];

      int indexItemList = 0;
      double temp = 0;

      try{
         stream = new FileInputStream(fileName);
         scanner = new Scanner(stream, "UTF-8");
         while (scanner.hasNextLine()){
             String line = scanner.nextLine();
             if (!line.equals("")){
                String[] elems = line.split(",");
                if (indexItemList == 0) {
                    temp = Double.valueOf(elems[1]);
                  layout[0][0] = (int)temp;
                    itemList.add(elems[0]);
                    userList.add(elems[2]);
                    indexItemList++;
                }
                else {
                    boolean itemFound = itemList.contains(elems[0]);
                    boolean userFound = userList.contains(elems[2]);

                    int indexItem = 1;
                    int indexUser = 1;
                    if ((itemFound) && (userFound)) {
                        indexItem = itemList.indexOf(elems[0]);
                        indexUser = userList.indexOf(elems[2]);
                     temp = Double.valueOf(elems[1]);
                        layout[indexItem][indexUser] = (int)temp;
                    }                    
                    else if ((itemFound) && (!userFound)) {
                        userList.add(elems[2]);
                        indexItem = itemList.indexOf(elems[0]);
                        indexUser = userList.indexOf(elems[2]);
                     temp = Double.valueOf(elems[1]);
                        layout[indexItem][indexUser] = (int)temp;
                    }
                    else if ((!itemFound) && (userFound)){
                        itemList.clear();
                        itemList.add(elems[0]);
                        indexUser = userList.indexOf(elems[2]);
                     temp = Double.valueOf(elems[1]);
                        layout[indexItemList][indexUser] = (int)temp;
                        indexItemList++;
                    }
                    else if (!((itemFound) && (userFound))) {
                        itemList.clear();
                        itemList.add(elems[0]);
                        userList.add(elems[2]);
                        indexUser = userList.indexOf(elems[2]);
                     temp = Double.valueOf(elems[1]);
                        layout[indexItem][indexUser] = (int)temp;
                        indexItemList++;
                    }   
                }
             }
         } 
         if (scanner.ioException() != null){
            throw scanner.ioException();
         }
      }
      catch (IOException e){
         System.out.println(e);
      }
      finally{
         try{
            if (stream != null){
               stream.close();
            }
         }
         catch (IOException e){
            System.out.println(e);
         }
         if (scanner != null){
            scanner.close();
         }
      }

      StringBuilder sb = new StringBuilder();

      for (int i = 0; i < layout.length; i++){
          for (int j = 0; j < layout[i].length; j++){
             sb.append(layout[i][j] + "");
             layout[i][j] = 0;
             if (j < layout[i].length - 1){
                sb.append(",");
             }
          }
          sb.append("\n");
       }

Ответы [ 2 ]

2 голосов
/ 04 октября 2019

Ваш файл содержит 200'000 строк, но ваш 2D-массив имеет 259'361'616 ячеек, и размер StringBuilder будет пропорционален этому числу. Вам не нужно хранить все это: это очень пустая матрица.

Вот что я бы сделал: читая входной файл, я построил бы два набора строк: элементы и пользователи, и картуприсвоение рейтинга каждой паре (элемент, пользователь):

    Set<String> items = new TreeSet<>();
    Set<String> users = new TreeSet<>();
    Map<String,Double> ratings = new HashMap<>();
    try (InputStream stream = new FileInputStream(fileName);
            Scanner scanner = new Scanner(stream, "UTF-8")) {
        while (scanner.hasNextLine()) {
            String line = scanner.nextLine();
            if (!line.equals("")) {
                String[] elems = line.split(",");
                String item = elems[0];
                String user = elems[2];
                double rating = Double.parseDouble(elems[1]);
                items.add(item);
                users.add(user);
                ratings.put(item+','+user, rating);
            }
        }
    } catch (IOException e) {
        System.out.println(e);
    }

Обратите внимание, что я использовал TreeSet s, чтобы убедиться, что элементы отсортированы, но если вас это не волнует, вы можетевместо этого используйте HashSet s. Чтобы сохранить элементы в порядке их появления, как вы делаете в своем коде, вы можете использовать LinkedHashSet s.

Затем вы можете записать в выходной файл, например:

    try (OutputStream stream = new FileOutputStream(outputName);
            Writer writer = new OutputStreamWriter(stream, "UTF-8");
            PrintWriter out = new PrintWriter(writer)) {
        for (String item: items) {
            int j = 0;
            for (String user: users) {
                Double rating = ratings.get(item+','+user);
                double r = rating == null ? 0 : rating;
                out.print(r);
                ++j;
                if (j < users.size()) {
                    out.print(',');
                }
            }
            out.println();
        }
    } catch (IOException e) {
        System.out.println(e);
    }

ОБНОВЛЕНИЕ:

В случае, когда у вас есть более одного рейтинга для одной и той же пары (предмет, пользователь), вы сохраняете только последний. Вы можете рассчитать среднее значение, используя Accumulator с вместо Double с на вашей карте:

public class Accumulator {
    private int count;
    private double sum;

    public void add(double value) {
        sum += value;
        ++count;
    }

    public double getAverage() {
        return count == 0 ? 0 : sum/count;
    }
}

ОБНОВЛЕНИЕ 2: Ошибки

Размер StringBuilder не пропорционаленк размеру матрицы, но к количеству элементов, умноженному на количество пользователей.

0 голосов
/ 08 октября 2019

Структура матрицы, которую я пытаюсь создать

Исходный файл CSV, который я анализирую, содержит более 200 000 строк с ровно 3 столбцами (пользователь, рейтинг, элемент). Я надеялся создать матрицу, напоминающую прикрепленную фотографию. Причина в том, что я планировал использовать эту матрицу позже для моего метода вычисления косинусного сходства между двумя строками матрицы (что будет входом метода). По сути, я бы сравнил все оценки первого ряда (userID # 1) с оценками второго ряда (userID # 2).

...