In J2ME, How to re-index records in recordstore after deleting any record

850 Views Asked by At

I am developing a Location-based J2ME app & in that I'm using RMS to store data.

In RecordStore when I delete any record, the underlying records doesn't get re-indexed. For example, if I have 5 records & I delete record no.2 then record ids will be {1, 3, 4, 5}. But I want record ids after deletion to be {1, 2, 3, 4}. How should I do this??? Because recordId is playing an important role in my app to retrieve & update the record.

2

There are 2 best solutions below

3
On

So, since you've said that your record store is basically small (not that much data), I would recommend simply adding your own custom id field to each record. As Meier said, the RMS record id is not really meant to be recalculated, and changed, once a record has been created. So, I would use your own.

If each of your records contain:

boolean isMale
int age
String firstName

then, I would simply add another field at the start of each record:

int id

It makes your records a little bigger, but not much (4 bytes/record). If you'll have less than 64k records, then you could also use a short for the id, and save a couple bytes.

Here's an example (adapted from this IBM tutorial), of reading, writing, and deleting with this kind of record:

   private RecordStore _rs;

   // these next two methods are just small optimizations, to allow reading and 
   //  updating the ID field in a record without the overhead of creating a new
   //  stream to call readInt() on.  this assumes the id is a 4 byte int, written
   //  as the first field in each record.

   /** Update one record with a new id field */
   private static final void putIdIntoRecord(int id, byte[] record) {      
      // we assume the first 4 bytes are the id (int)
      record[0] = (byte)(id >> 24);
      record[1] = (byte)(id >> 16);
      record[2] = (byte)(id >> 8);
      record[3] = (byte)id;
   }

   /** Get the id field from one record */
   private static final int getIdFromRecord(byte[] record) {
      // we assume the first 4 bytes are the id (int)
      return ((0xFF & record[0]) << 24) | 
            ((0xFF & record[1]) << 16) | 
            ((0xFF & record[2]) << 8) | 
            (0xFF & record[3]);
   }

   /** delete a record with the given (custom) id, re-indexing records afterwards */
   private void delete(int idToDelete) {     
      try {
         RecordEnumeration enumerator = _rs.enumerateRecords(new IdEqualToFilter(idToDelete), 
               null, false);
         _rs.deleteRecord(enumerator.nextRecordId());

         // now, re-index records after 'idToDelete'
         enumerator = _rs.enumerateRecords(new IdGreaterThanFilter(idToDelete), null, true);
         while (enumerator.hasNextElement()) {
            int recordIdToUpdate = enumerator.nextRecordId();
            byte[] record = _rs.getRecord(recordIdToUpdate);
            // decrement the id by 1
            int newId = getIdFromRecord(record) - 1;
            // copy the new id back into the record
            putIdIntoRecord(newId, record);
            // update the record, which now has a lower id, in the store
            _rs.setRecord(recordIdToUpdate, record, 0, record.length);
         }
      } catch (RecordStoreNotOpenException e) {
         e.printStackTrace();
      } catch (InvalidRecordIDException e) {
         e.printStackTrace();
      } catch (RecordStoreException e) {
         e.printStackTrace();
      }               
   }

   /** generate some record store data ... example of writing to store */
   public void writeTestData()
   {
      // just put 20 random records into the record store
      boolean[] booleans = new boolean[20];
      int[] integers = new int[20];
      String[] strings = new String[20];
      for (int i = 0; i < 20; i++) {
         booleans[i] = (i % 2 == 1);
         integers[i] = i * 2;
         strings[i] = "string-" + i;
      }

      writeRecords(booleans, integers, strings);
   }

   /** take the supplied arrays of data, and save a record for each array index */
   public void writeRecords(boolean[] bData, int[] iData, String[] sData)
   {
      try
      {
         // Write data into an internal byte array
         ByteArrayOutputStream strmBytes = new ByteArrayOutputStream();

         // Write Java data types into the above byte array
         DataOutputStream strmDataType = new DataOutputStream(strmBytes);

         byte[] record;

         for (int i = 0; i < sData.length; i++)
         {
            // Write Java data types
            strmDataType.writeInt(i);               // this will be the ID field!
            strmDataType.writeBoolean(bData[i]);
            strmDataType.writeInt(iData[i]);
            strmDataType.writeUTF(sData[i]);

            // Clear any buffered data
            strmDataType.flush();

            // Get stream data into byte array and write record
            record = strmBytes.toByteArray();            
            _rs.addRecord(record, 0, record.length);           

            // Toss any data in the internal array so writes
            // starts at beginning (of the internal array)
            strmBytes.reset();
         }

         strmBytes.close();
         strmDataType.close();
      }
      catch (Exception e)
      {
         e.printStackTrace();
      }
   }

   /** read in all the records, and print them out */
   public void readRecords()
   {
      try
      {
         RecordEnumeration re = _rs.enumerateRecords(null, null, false);         
         while (re.hasNextElement())
         {    
            // Get next record
            byte[] recData = re.nextRecord(); 

            // Read from the specified byte array
            ByteArrayInputStream strmBytes = new ByteArrayInputStream(recData);

            // Read Java data types from the above byte array
            DataInputStream strmDataType = new DataInputStream(strmBytes);

            // Read back the data types
            System.out.println("Record ID=" + strmDataType.readInt());
            System.out.println("Boolean: " + strmDataType.readBoolean());
            System.out.println("Integer: " + strmDataType.readInt());
            System.out.println("String: " + strmDataType.readUTF());
            System.out.println("--------------------");

            strmBytes.close();
            strmDataType.close();        
         }
      }
      catch (Exception e)
      {
         e.printStackTrace();
      }
   }

Here, I make use of a couple small RecordFilter classes, to use when searching the record store:

   /** helps filter out records greater than a certain id */
   private class IdGreaterThanFilter implements RecordFilter {
      private int _minimumId; 
      public IdGreaterThanFilter(int value) {
         _minimumId = value;
      }
      public boolean matches(byte[] candidate) {
         // return true if candidate record's id is greater than minimum value
         return (getIdFromRecord(candidate) > _minimumId);         
      }      
   }

   /** helps filter out records by id field (not "recordId"!) */
   private class IdEqualToFilter implements RecordFilter {
      private int _id; 
      public IdEqualToFilter(int value) {
         _id = value;
      }
      public boolean matches(byte[] candidate) {
         // return true if candidate record's id matches
         return (getIdFromRecord(candidate) == _id);         
      }      
   }
3
On

You need to change your application logic. ID is just for identification, and not for sorting. Because it is for identification, it must remains the same.

Very often the easiest thing to do is to read and write the whole recordstore at once.