I'm using the following code to fill up a ContentValues variable.
public ContentValues getContentValues() {
ContentValues initialValues = new ContentValues();
initialValues.put("blt_name", m_name);
initialValues.put("blt_pictureURI", m_pictureURI);
initialValues.put("blt_description", m_description);
initialValues.put("blt_UUID", getUUID().toString());
return initialValues;
}
My problem is that put() is putting the UUID and the name in the same hash location! I have no idea why. When creating the initialValues variable it creates an internal hashmap having 7 slots. When putting the values, key is added in slot 0, name is ALSO added in slot 0 (overwriting uuid), pic is added in slot 3 and desc is added in slot 7.
All four keys are, of course, different values, declared as final Strings.
I tried new ContentValues(4) in order to force them into the right spot, that was worse. 2 values were overwritten.
[Edit] I just tried changing the order of the puts. By moving the UUID so that it is put() last, it still overwrites slot 0 in hashmap. (I know what you are thinking, and YES the keys are unique.)
[EDIT] I tried it with the following code, and it works perfectly. I'm at a lost. I also edited the original question because I tried it with hard coded strings, and that didn't work either.
initialValues.put("a", m_name);
initialValues.put("b", m_pictureURI);
initialValues.put("c", m_description);
initialValues.put("d", getUUID().toString());
Any help would be appreciated,
-I_Artist
Are you sure that this is a problem? ContentValues is essentially a hash table, not an array. It's almost inevitable that there will be collisions between different keys. But collisions do not mean that you've lost your data. The only real way to be sure that your data is (or is not) stored appropriately is to try to get the data from the ContentValues object:
String newName = initialValues.get("blt_name");
String newPicture = initialValues.get("blt_pictureURI");
String newDesc = initialValues.get("blt_description");
String newUUID = initialValues.get("blt_UUID");
// now do something with these values to check if they're right...
I bet you'll find that the data have their correct values. If not, there's more going on than the code that you've posted can show us.
What happens if you hard code your keys as "Key1", "Key2", "Key3", "Key4"? I know you said that you are sure your keys are unique, however I am still curious if there could be something we are all not seeing.. Maybe you could show us an example of the values being set for the keys and their values?
(Ha! First time using stackoverflow, obviously this is not an answer...)
(So this might actually be your answer)
The HashMap computes an index into that array using the hashCode() of the key. It doesn't just use the hashCode() modulo the array size, but rather uses a more complex function of the hashCode().
It may be possible that the keys: "blt_UUID" AND "blt_name" are being hashed to the same value. This being the case, the two keys are being given the same index and a "collision" is occurring and the value is being overwritten. Try changing the key to something else, maybe using all capitalization, and try it again.
Best of luck.
Related
I am a bit new to Firebase and so have been playing around with to help myself get more acquainted with it. So while I was playing around with realtime databases, I was trying to append data to the JSON tree. The code is as below
mSaudi.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
count++;
mHistory = mChildRef.child(Integer.toString(count));
current = riyadh;
mChildRef.setValue(riyadh);
mHistory.push().setValue("riyadh");
}
});
The tree which I require is something like this:
value:
1: some text
2: some other text
But what's a actually happening is this:
value:
1: some text
and on updation
value:
2:some text
the previous entry gets erased
I have tried changing the references in various ways but to no avail. Any help in this regard would be appreciated.
If you would like to save both values, you have to save them using a variable such as a Hashmap. If you save a string and then try save another one under the same branch, it will delete everything previously saved. So try the following
HashMap<String, String> map = new HashMap<>();
map.put("1","String");
map.put("2","String");
mHistory.push().setValue(map);
This will save both the strings without deleting one.
If you would only like to add one String
mHistory.push().child("1").setValue("Your first String");
The biggest problem with this though is that everytime you use push() you generate a random key, so you would have to save the key as a string and use it as a reference in your child.
When you set a value on Firebase, it is going to replace everything in, and under the reference.
Let's say that you have a house value, with 2 childs: Color and Size.
If you want to edit only the color value, before the setValue(), you will have to change the reference you are pushing to.
If your reference was getReference().child("houses") and you push something there, it's going to replace everything there and below it. The way to do it is create a new reference (or update the previews one) like this: getReference().child("houses").child(houseKey).child("color") and push your String there.
In your example, you will need to add the field you want to change as a child before the push() method.
The other way was already told by #Janwilx72 and is getting the whole object, updating the value locally and pushing the entire object again.
You can try this
mChildRef.child("2").setValue("some text");
It should be appending new item instead of overwriting them
Right now, I have my code looking like this:
SqlTables.java:
// Separate file
public enum SqlTables {
TABLE_1("table_1"),
.
.
.
TABLE_N("table_n");
final String name;
private SqlTables(String name) {
this.name = name;
}
}
Table1Columns.java:
// Separate file
public enum Table1Columns {
...
}
SqlDatabaseInterface.java:
Cursor c = db.query(SqlTables.TABLE_1.toString(), null...);
final int indexId = c.getColumnIndexOrThrow(
Table1Columns.ID.toString());
final int indexName = c.getColumnIndexOrThrow(
Table1Columns.NAME.toString());
while (c.moveToNext()) {
final String id = c.getString(indexId);
final String name = c.getString(indexName);
}
I did not find any documentation regarding how Android mapped columns to indices, so while I've confirmed that it is based on the order in which the columns were stated in the query, I do not have a guarantee that this will work 100% of the time, or that future updates will keep this behavior. The benefits of using getColumnIndexOrThrow(...) is the guarantee that my indices will always be correct, so long as the columns they reference are included in the query. The downside is that, besides the tedium, it makes my code much harder to read and greatly increases the length of my methods. On the other hand, I could just specify the indices without calling getColumnsIndexOrThrow, and that'd shorten my code, but the lack of documentation makes it feel wrong.
No, you definitely should not hard code the column index values into your code. If you do, your database will be nearly impossible to manage (since making changes to the table could cause the index values to change).
From what I can tell, your code is difficult to read because:
You are literring your code with unnecessary calls to toString(). Just get rid of them... they are completely redundant.
You are using the final keyword way too much and its cluttering your code. This should be used as a precaution in situations where you might accidentally change the value of a variable. This is not one of those situations...
You are spreading simple statements across multiple lines. Just use one line and it'll make it a lot easier to read.
That said, there is technically nothing wrong with any of the above coding practices. I'm just saying, if you really care about making your code easy to read then you might want to consider them as suggestions.
To store an integer in an Android preference, I would intuitively go for EditTextPreference and do the usual String-int-String conversions.
But then I came across a piece of code that stores an integer in a <ListPreference> instead:
<ListPreference
android:key="#string/total_score"
android:defaultValue="0" />
and retrieves it using preferences.getInt(getString(R.string.total_score), 0);
Does this really work? If so, how?
Is it considered acceptable practice?
UPDATE: Thanks to the answers below, I have been able to find the implementation source code for getInt(). I am posting it here for easy reference:
jint android::content::SharedPreferences::getInt(local_ref< java::lang::String > const &a0, jint a1)
{
return call_method<
android::content::SharedPreferences::J2CPP_CLASS_NAME,
android::content::SharedPreferences::J2CPP_METHOD_NAME(2),
android::content::SharedPreferences::J2CPP_METHOD_SIGNATURE(2),
jint
>(get_jobject(), a0, a1);
}
In theory, yes you can store an integer with a ListPreference. After all it's a UI-preference that maps a user displayed label/key (android:entries) to an internal value (android:entryValues) and displays all those mapping options in a listview. That internal value might as well be an integer. You could use a <integer-array>-resource for the entryValues.
In practice, I've never seen that work - it's bugged.
Of course, you can set a int value to the preference key of the ListPreference in your code, since it's a normal preference internally. But that would defeat the whole purpose of predefined resource arrays and ability to select from a list. As a workaround, if a int-array would be handy, I recommend using a <string-array> for the values and convert them an integer in code, as you would with your EditTextPreference
Check this question for a non-working example. ;)
To answer your title question which one to choose for an integer: Depends.
You can use either one with the workarounds.
If the user should be able to enter any value (or just a lot of values), the EditTextPreference is the way to go. A ListPreference would just be too long.
If you have a small set of predefined ints, use a ListPreference. Thats way more comfortable to use and might be displayed with useful labels. Example: If the user is supposed to select a timing interval, you could map the seconds in the value and display a different label, e.g. an hour [value 3600; label "Hour"].
Edit: Also got an idea where your code snippet may be related to. Since this ListPreference does neither specify android:entries nor android:entryValues, it might just be part of a default preference file. You can use PreferenceManager.setDefaultValues() with an XML file to reset/initalize all your preference keys. In this case it's completely random which preference you choose, because all fields that count are android:key and android:defaultValue. You might use any other type in this case, does not matter.
I think what's going on is that the ListPreference is storing the entry values as an array of chars, or in other words an array of bytes (I'm inferring this from the setEntryValues(CharSequence[] entryValues) method). This could be why storing a number works, because the value is stored into the byte array, so using the preferences.getInt(...) method will still work. This could be dangerous, however, as discrepancies with signing could occur, so I wouldn't recommend it, and would parse the int from a string preference instead.
hey,
i have a dictionnary like
"key2","value2"
"key3","value3"
"key1","value1"
is it possible to sort it on value ?
If not, any other object can do same thing? (sort value on key/value pair)
Well, clearly you're talking about a Map.
It really depends on what Map you're using, You don't need to sort a TreeMap, coz it is already sorted.
For any other, You can get the keys using map.keySet() and use Collections.sort() to sort them. (Note, the map will remain the same, you'll get a Set containing all the key values)
Hope it helped.
I have a number (123456) converted to a hash key and stored in SharedPrefs using:
String correctMd5 = passwdfile.getString(PhoneFinder.PASSWORD_PREF_KEY, null);
I then retreive the number from a string:
String[] tokens = msg.getMessageBody().split(":");
String md5hash = PhoneFinder.getMd5Hash(tokens[1]);
and compare the two:
if (correctMd5 == md5hash) {
Toast.makeText(context, "Hash OK: " + md5hash, Toast.LENGTH_SHORT).show();
}
However, this check does not complete succesfully.
If I convert to strings and display them, the hashes are the same, however if I convert to bytes the 4 right most bytes are different. I assume some special character is hidden in there somewhere, how do I check and kill it?
You should probably use correctMd5.equals(md5hash) instead of the correctMd5 == md5hash.
Is it solving the problem ?
The problem appears to be:
correctMd5 == md5hash
Because a String is an Object in Java (Android) this will compare the Object, not its String value. For instance, if you have two different variables they might be in 2 different memory locations, or they might be references to the same memory location.
On the other hand, if you want to find out if the VALUES stored by the memory location are equal, you should use
correctMd5.equals(md5hash)
In your conditional, you could have two strings "1000" and "1000" but stored at different memory locations. In my conditional, it will still be true regardless of memory location, if the String values are equal.
If == is true, .equals() should be true (in most cases, if not all). But if .equals() is true, there is no guarantee that == is true.