17

I'm implementing a custom ArrayAdapter<T>, and I want to set the hasStableIds to true. But the ids of my T items are Strings and the getItemId method returns longs.

So, what I am currently doing is:

@Override
public boolean hasStableIds() {
    return true;
}

@Override
public long getItemId(int position) {
    return this.getItem(position).getId().hashCode();
}

Where getId() returns a String.

Is this the correct solution for using String ids?
In particular, for this case, the String Ids are GUIDs, is there a better option?

pomber
  • 23,132
  • 10
  • 81
  • 94

4 Answers4

6

You don't want to use the hashCode() because this way, two different GUIDS might result in the same ID.

Just code a mapping. Something like this...

private static class GuidToIdMap {
    private final Map<String, Long> guidToIdMapping = new HashMap<String, Long>();
    private final Map<Long, String> idToGuidMapping = new HashMap<Long, String>();

    private long idGenerator = 0;

    public long getIdByGuid(final String guid) {
        if ( ! guidToIdMapping.containsKey(guid)) {
            final long id = idGenerator++;
            guidToIdMapping.put(guid, id);
            idToGuidMapping.put(id,   guid);
        }
        return guidToIdMapping.get(guid);
    }

    public String getGuidById(final long id) {
        if (idToGuidMapping.containsKey(id)) {
            return idToGuidMapping.get(id);
        }
        return null;
    }
}


private final GuidToIdMap guidToIdMap = new GuidToIdMap();

@Override
public boolean hasStableIds() {
    return true;
}

@Override
public long getItemId(int position) {
    final String guid = this.getItem(position).getId();
    return guidToIdMap.getIdByGuid(guid);
}

// Use guidToIdMap.getGuidById(id) where the Android Framework passes you the generated id

Performance impact will be negligible....

Graham Borland
  • 60,055
  • 21
  • 138
  • 179
Niels
  • 725
  • 5
  • 14
  • Use final long id = new Random().nextLong(); instead of idGenerator variable. It's possible to get duplicate ids with idGenerator. – rajeswari ratala Feb 21 '19 at 06:57
  • @rajeswariratala please explain how it would be possible to get duplicates for 'idGenerator'?! – Niels Feb 25 '19 at 14:51
  • Since it is not synchronized, I was getting duplicates. you can either use an atomiclong and getAndIncrement() or random long. – rajeswari ratala Feb 26 '19 at 05:46
  • Odd. Most likely you did not post a GUI task on the [Android UI thread](https://developer.android.com/reference/android/app/Activity.html#runOnUiThread(java.lang.Runnable)). Consider [AsyncTasks](https://developer.android.com/guide/components/processes-and-threads#AsyncTask) for doing background work for which the result needs to be processed by the GUI. – Niels Mar 04 '19 at 09:40
2

It is ok for as long as it is true that the returned id keeps being the same for same object after changes and it's unique as described in javadoc for hasStableIds

eduyayo
  • 2,020
  • 2
  • 15
  • 35
  • @Niels sure! But my phrase is right anyway. Same string is same hash and that is inherent to java even when it is not the same string but accidentally has the same hash. – eduyayo Oct 04 '16 at 08:41
1

You can do this: return UUID.getLeastSignificantBits()

P/s: You probably should not. A long's size is 8 bytes whereas an UUID's size 16 bytes. Hence an UUID can not be stored in a long. From Wikipedia:

The identifier size and generation process need to be selected so as to make this sufficiently improbable in practice. Therefore, two different UUIDs can have the same leastSignificantBits.

Tin Tran
  • 2,265
  • 1
  • 14
  • 17
-1

No you are incorrect getItemId has tow method signatures and one of them returns long per docs: https://developer.android.com/reference/android/widget/Adapter.html#getItemId(int)

Fred Grott
  • 3,505
  • 1
  • 23
  • 18
  • Actually, there is only one getItemId method and it returns long, so please check your information before posting. – kyay10 Feb 15 '19 at 02:24