Большая сумма констант в Java

Я должен включать приблизительно 1 мегабайт данных в JAVA-приложении для очень быстрого доступа и легкого доступа в остальной части исходного кода. Мое основное образование не является Java, таким образом, моя начальная идея состояла в том, чтобы преобразовать данные непосредственно в исходный код Java, определив 1 мегабайт постоянных массивов, классы (вместо структуры C++) и т.д., что-то вроде этого:

public final/immutable/const MyClass MyList[] = { 
  { 23012, 22, "Hamburger"} , 
  { 28375, 123, "Kieler"}
};

Однако кажется, что Java не поддерживает такие конструкции. Это корректно? Если да, что лучшее решение к этой проблеме?

Примечание: Данные состоят из 2 таблиц с каждым приблизительно 50 000 записей данных, которые должны искаться различными способами. Это может потребовать некоторых индексов позже со значительным, больше записей, возможно, 1 миллион записей, сохранило этот путь. Я ожидаю, что приложение запустит очень быстро, не выполняя итерации через эти записи.

15
задан Lars D 9 May 2010 в 15:24
поделиться

10 ответов

Я лично не поместил бы это в исходную форму.

Вместо этого включите данные в соответствующем необработанном формате в ваш jar-файл (я предполагаю, что вы будете упаковывать приложение или библиотеку) и используйте Class.getResourceAsStream или ClassLoader. getResourceAsStream , чтобы загрузить его.

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

22
ответ дан 1 December 2019 в 01:10
поделиться

Из-за ограничений файлов байт-кода Java файлы классов не могут быть больше 64k iirc. (Они просто не предназначены для этого типа данных.)

Я бы загрузил данные при запуске программы, используя что-то вроде следующих строк кода:

import java.io.*;
import java.util.*;

public class Test {
    public static void main(String... args) throws IOException {
        List<DataRecord> records = new ArrayList<DataRecord>();
        BufferedReader br = new BufferedReader(new FileReader("data.txt"));
        String s;
        while ((s = br.readLine()) != null) {
            String[] arr = s.split(" ");
            int i = Integer.parseInt(arr[0]);
            int j = Integer.parseInt(arr[1]);
            records.add(new DataRecord(i, j, arr[0]));
        }
    }
}


class DataRecord {
    public final int i, j;
    public final String s;
    public DataRecord(int i, int j, String s) {
        this.i = i;
        this.j = j;
        this.s = s;
    }
}

( NB: Сканер - это довольно медленный, поэтому не поддавайтесь соблазну использовать его только потому, что у него простой интерфейс. Придерживайтесь некоторой формы BufferedReader и split или StringTokenizer.)

Конечно, эффективность можно повысить, если преобразовать данные в двоичный формат. В этом случае вы можете использовать DataInputStream (но не забудьте пройти через BufferedInputStream или BufferedReader )

В зависимости от того, как вы желаете получить доступ к данным, вам может быть лучше хранить записи в хэш-карте ( HashMap ) (имея i или j как ключ).

Если вы хотите загрузить данные одновременно с загрузкой JVM самого файла класса (примерно!), Вы можете выполнить чтение / инициализацию не в рамках метода, а в капсуле static {... } .


Для подхода с отображением памяти посмотрите пакет java.nio.channels в java. В частности, метод

общедоступная абстрактная карта MappedByteBuffer (режим FileChannel.MapMode, длинная позиция, длинный размер) выдает исключение IOException

. Полные примеры кода можно найти здесь .


Дэн Борнштейн (ведущий разработчик DalvikVM) объясняет решение вашей проблемы в этом выступлении (посмотрите около 0:30:00).Однако я сомневаюсь, что решение применимо к такому же объему данных, как мегабайт.

7
ответ дан 1 December 2019 в 01:10
поделиться

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

public enum Stuff {

 HAMBURGER (23012, 22),
 KIELER    (28375, 123);

 private int a;
 private int b;

 //private instantiation, does not need to be called explicitly.
 private Stuff(int a, int b) {
    this.a = a;
    this.b = b;
  }

 public int getAvalue() {
   return this.a;
 }

 public int getBvalue() {
   return this.b;
 }

}

Затем к ним можно получить доступ следующим образом:

Stuff someThing = Stuff.HAMBURGER;
int hamburgerA = Stuff.HAMBURGER.getA() // = 23012

Другая идея заключается в использовании статического инициализатора для установки частных полей класса.

3
ответ дан 1 December 2019 в 01:10
поделиться

конвертировать данные непосредственно в исходный код Java, определяя 1 МБ константных массивов, классов

Имейте в виду, что существуют строгие ограничения на размер классов и их структуры [ref JVM Spec .

1
ответ дан 1 December 2019 в 01:10
поделиться

Помещение данных в источник на самом деле не могло бы быть самым быстрым решением, в любом случае. Загрузка Java-класса довольно сложная и медленная (по крайней мере, на платформе, которая выполняет проверку байт-кода, не уверена в Android).

Самый быстрый способ сделать это - определить собственный формат двоичного индекса. Затем вы можете прочитать это как байт [] (возможно, используя отображение памяти) или даже RandomAccessFile , не интерпретируя его каким-либо образом, пока вы не начнете к нему обращаться. Ценой этого будет сложность кода, который обращается к нему. С записями фиксированного размера отсортированный список записей, доступ к которым осуществляется через двоичный поиск, по-прежнему будет довольно простым, но все остальное будет некрасивым.

Но прежде чем это сделать, вы уверены, что это не преждевременная оптимизация? Самым простым (и, вероятно, все еще довольно быстрым) решением было бы просто сериализовать карту, список или массив - вы пробовали это и определили, что это на самом деле слишком медленно?

3
ответ дан 1 December 2019 в 01:10
поделиться

Вот как вы определяете это в Java, если я понял, что вам нужно:

public final Object[][] myList = { 
          { 23012, 22, "Hamburger"} , 
          { 28375, 123, "Kieler"}
        };
1
ответ дан 1 December 2019 в 01:10
поделиться

Сериализация Java звучит как нечто, что нужно разбирать... не очень хорошо. Разве нет какого-то стандартного формата для хранения данных в потоке, который можно прочитать/просмотреть с помощью стандартного API, не разбирая его?

Если бы вы создавали данные в коде, то все они загружались бы при первом использовании. Это вряд ли будет намного эффективнее, чем загрузка из отдельного файла - помимо разбора данных в файле класса, JVM придется проверять и компилировать байткоды для создания каждого объекта миллион раз, а не только один раз, если вы загрузите его из цикла.

Если вам нужен произвольный доступ и вы не можете использовать файл с отображением в памяти, то существует RandomAccessFile, который может сработать. Вам нужно либо загружать индекс при запуске, либо сделать записи фиксированной длины.

Возможно, вы захотите проверить, работают ли библиотеки HDF5 на вашей платформе; хотя для такого простого и маленького набора данных это может быть излишеством.

0
ответ дан 1 December 2019 в 01:10
поделиться

Похоже, вы планируете написать собственную облегченную базу данных.
Если вы можете ограничить длину строки до реалистичного максимального размера, следующее может сработать:

  • записать каждую запись в двоичный файл, записи будут иметь одинаковый размер, поэтому вы потратите немного байтов с каждой записью (int a, int b, int stringsize, string, padding)
  • Чтобы прочитать запись, откройте файл как файл с произвольным доступом, умножьте индекс на длину записи, чтобы получить смещение и найти позицию .
  • Поместите байты в байтовый буфер и прочтите значения, String необходимо преобразовать с помощью ctor String (byte [], int start, int length, Charset).

Если вы не можете ограничить длину блока, выгрузите строки в дополнительный файл и сохраните только смещения в вашей таблице. Это требует дополнительного доступа к файлу и затрудняет изменение данных.
Некоторую информацию о произвольном доступе к файлам в java можно найти здесь http://java.sun.com/docs/books/tutorial/essential/io/rafs.html .

Для более быстрого доступа вы можете кэшировать некоторые из ваших прочитанных записей в Hashmap и всегда удалять самые старые с карты при чтении новой.
Псевдокод (не компилируется):

class MyDataStore
{
   FileChannel fc = null;
   Map<Integer,Entry> mychace = new HashMap<Integer, Entry>();
   int chaceSize = 50000;
   ArrayList<Integer> queue = new ArrayList();
   static final int entryLength = 100;//byte
   void open(File f)throws Exception{fc = f.newByteChannel()}
   void close()throws Exception{fc.close();fc = null;}
   Entry getEntryAt(int index)
   {
       if(mychace.contains(index))return mychace.get(index);

       long pos = index * entryLength; fc.seek(pos);ByteBuffer 
       b = new ByteBuffer(100);
       fc.read(b);
       Entry a = new Entry(b);
       queue.add(index);
       mychace.put(index,a);
       if(queue.size()>chacesize)mychace.remove(queue.remove(0));
       return a;
   }

}
class Entry{
   int a; int b; String s;
   public Entry(Bytebuffer bb)
   {
     a = bb.getInt(); 
     b = bb.getInt(); 
     int size = bb.getInt();
     byte[] bin = new byte[size];
     bb.get(bin);
     s = new String(bin);
   }
}

Отсутствует в псевдокоде:

  • запись, так как он нужен для постоянных данных
  • общее количество записей / размер файла, требуется только дополнительный целое число в начале файла и дополнительное смещение 4 байта для каждой операции доступа.
1
ответ дан 1 December 2019 в 01:10
поделиться

Можно также объявить статический класс (или набор статических классов), раскрывающий рассматриваемые значения как методы. В конце концов, вы хотите, чтобы ваш код мог найти значение для заданного имени, и не хотите, чтобы это значение менялось.

Итак: location=MyLibOfConstants.returnHamburgerLocation().zipcode

И вы можете хранить эти данные в хэш-таблице с ленивой инициализацией, если вы считаете, что вычисление их на лету будет пустой тратой времени.

0
ответ дан 1 December 2019 в 01:10
поделиться

Разве вам не нужен кэш? Поскольку классы загружаются в память, не ограничиваясь определенным размером, они должны быть такими же быстрыми, как при использовании констант ... На самом деле он может даже искать данные с помощью каких-то индексов (например, с хэш-кодом объекта ...) Вы можете, например, создать все ваши массивы данных (например, {23012, 22, "Гамбургер"}), а затем создать 3 хэш-карты: map1.put (23012 , hamburgerItem); map2.put (22, hamburgerItem); map3.put ("Гамбургер", hamburgerItem); Таким образом вы можете очень быстро искать в одном из карта в соответствии с имеющимся у вас параметром ... (но это работает только в том случае, если ваши ключи уникальны на карте ... это всего лишь пример, который может вас вдохновить)

На работе у нас есть очень большое веб-приложение (80 экземпляров веб-логики), и это почти то, что мы делаем: кеширование повсюду. Из списка стран в базе данных создайте кеш ...

Есть много разных кешей, вы должны проверить ссылку и выбрать то, что вам нужно ... http: // ru. wikipedia.org/wiki/Cache_algorithms

0
ответ дан 1 December 2019 в 01:10
поделиться
Другие вопросы по тегам:

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