Hi I am trying to save a object in HashMap if it is not exist than if it is exsit I want to control its value with new data. If data change than I want to do sth. else. But whenever I tried to compare new data and hash value I saw they are same on every time . How can I handle with this issue. There is code:
BluetoothLeDevice deviceLe;
private Map<String, byte[]> mMacMap;
byte [] integer0 =new byte[4];
byte[] tempInteger0=new byte[4];
public void addSensor(String macId, BluetoothLeDevice deviceLe) {
byte [] addSensorrecord=deviceLe.getScanRecord();
int j=0;
for(int i=15;i<19;i++)
{
integer0 [j]=addSensorrecord[i];
j++;
}
if (mMacMap.containsKey(macId)) {
tempInteger0 = mMacMap.get(macId);
if(!integer0 .equals(tempInteger0))
{
mMacMap.remove(macId);
mMacMap.put(macId, integer0 );
new SendBLEData().execute(deviceLe);
}
} else {
final byte [] LocalInteger0=new byte[4];
int t=0;
for(int i=15;i<19;i++)
{
LocalInteger0[t]=addSensorrecord[i];
t++;
}
mMacMap.put(macId, LocalInteger0);
new SendBLEData().execute(deviceLe);
}
}
I am guessing, that your problem is here:
!Integer0.equals(tempInteger0))
I think you want to compare two arrays; and you are surprised to find them to be different ... all the time.
Your problem is: equals() on arrays doesn't do a comparison of the array content. In other words: this call to equals() only gives "true" if the arrays you are comparing ... are one and the same, like in:
int a[] = { 1 };
int b[] = a;
int c[] = { 1 };
Here:
a.equals(b) --> true
but
a.equals(c) --> false
When comparing array content matters, then you should use ArrayList instead. Because two ArrayList objects are equal when they contain exactly the same equal elements.
And you see: you are using that equals on arrays to make a decision in your code. So, you either change to ArrayLists; or use Arrays.equals() as user hamsty suggested.
Just a few additions to the already posted answers.
The remove below is not necessary, a simple put will replace the old value
mMacMap.remove(macId);
mMacMap.put(macId, integer0 );
From the javadoc
If the map previously contained a mapping for the key, the old value
is replaced by the specified value. (A map m is said to contain a
mapping for a key k if and only if m.containsKey(k) would return
true.)
Have you considered making bytes 15-19 into a string and adding them onto the maps key? This would eliminate the array compare and make the lookups much faster.
!Integer0.equals(tempInteger0))
is your problem.
Use this to compare the content of arrays:
Arrays.equals(Integer0, tempInteger0)
The problem is the following sequence of events:
macId not in mMacMap, insert new byte[4]; into the map
macId in mMacMap, the array created in the previous step never matches integer0 due to the Array comparison problem mentioned by the other answer, replace macId in the map with a reference to integer0
macId in mMacMap, since the array is a reference to integer0, it will always compare positively and the contents will no longer be updated.
Repeat 3.
Basically caused by these two issues:
Array#equals does not behave intuitively, use the static method Arrays.equals
Java is heavily reference-based, so if you insert something into a map it will not be copied, but simply a new reference is created; this may bite you if you change a shared object afterwards (like the array).
Related
I'm writing a piece of code which takes a great deal of objects and adds them to another array. The catch is, I don't want any duplicates. Is there a way I could implement a Hashset to solve this problem?
public static Statistic[] combineStatistics(Statistic[] rptData, Statistic[] dbsData) {
HashSet<Statistic> set = new HashSet<Statistic>();
for (int i=0; i<rptData.length; i++) {
set.add(rptData[i]);
}
/*If there's no data in the database, we don't have anything to add to the new array*/
if (dbsData!=null) {
for (int j=0; j<dbsData.length;j++) {
set.add(dbsData[j]);
}
}
Statistic[] total=set.toArray(new Statistic[0]);
for (int workDummy=0; workDummy<total.length; workDummy++) {
System.out.println(total[workDummy].serialName);
}
return total;
}//end combineStatistics()
Properly implement equals(Object obj) and hashCode() on YourObject if you expect value equality instead of reference equality.
Set<YourObject> set = new HashSet<YourObject>(yourCollection);
or
Set<YourObject> set = new HashSet<YourObject>();
set.add(...);
then
YourObject[] array = set.toArray(new YourObject[0])
I think you should pay attention to:
1 - what to do if there is a duplicate in the original Collection? Use the first added to the array? Use the other(s)?
2 - You definitely need to implement equals and hashcode so that you can tell what are duplicate objects
3 - Are you going to create a fixed size array and then won't add anymore objects? Or are you going to keep adding stuff?
You can use any kind of Set actually, but if you use LinkedHashSet, then you will have a defined iteration order (which looks like an array). HashSet wont't garantee any order and TreeSet will try to order data ascending.
Depends on what you are referring to as a duplicate. If you mean an identical object, then you could use a List and simply see if the List contains the object prior to adding it to the list.
Object obj = new Object();
List<Object> list = new ArrayList<Object>();
if (!list.contains(obj)) {
list.add(obj);
}
int sizeOfTheShortestList = webresult.size();
for (int i=0; i<sizeOfTheShortestList; i++) {
if (webresult1.get(i).equals(dbresult[i]) )
{
System.out.println("Equals..: " + webresult1.get(i));
}
}
from above code i find errors please give solution to compare arraylist values and array values
Take arraylist as 'al'.
Take array as 'a'.
now u need to compare 'al' and 'a'.
iterator it = new iterator(al);
int length = a.size();
int count=0;
for(int i=0;i<length; i++)
{
if(it.next()==a[i]);
{
count++;
}
}
if(count == length)
{
System.out.println("Both are equal");
}
else
{
System.out.println("Both are not equal");
}
You could use something like ArrayList#retailAll
Retains only the elements in this list that are contained in the
specified collection. In other words, removes from this list all of
its elements that are not contained in the specified collection.
webresult.retainAll(Arrays.asList(dbresult);
// Now webresult will only contain the values that were in webresult and are in dbresult
Try some thing like this
int sizeOfTheShortestList = Math.min(webresult.size(), webresult1.length);
for (int i=0; i<sizeOfTheShortestList; i++) {
if (webresult.get(i).equals(webresult1[i]) {
System.out.println("Equals..: " + webresult.get(i));
}
}
I hope I understood your question in right way..otherwise please comment
First you need to compare the lengths of both webresult and dbresult, because if they are of different lengths you will get IndexOutOfBounds exception.
Is webresult.get(i) is of the same type as of dbresult[i]?
Implementation of equals, missing from question, should add that too, if not using default.
It depends on what kind of errors you get.
Another solution would be to use the toArray() method on your arraylist and compare 2 arrays. But your code should work if the data structures have the same length, because you are comparing the elements from the array and arraylist, not the collections themselves.
Assuming that your arraylist is shorter than the array (otherwise you'd get an IndexOutOfBounds exception), you should make sure that the types of webresult.get(i) and dbresult[i] are the same. Also, ".equals()" only works for comparing objects (such as Strings), and not for primitives. So if webresult.get(i) and dbresult[i] are ints or chars, ".equals" will not work. You should use "==" instead.
You can use the ArrayList#toArray() to convert your list to an array.
Then you can use Arrays#equals() to compare the two arrays.
The objects that you are comparing will have to override Object#eqauls()
I tried to use HashSet to remove the duplications from an ArrayList<StringBuilder>.
E.g. Here is an ArrayList, each line is a StringBuilder object.
"u12e5 u13a1 u1423"
"u145d"
"u12e5 u13a1 u1423"
"u3ab4 u1489"
I want to get the following:
"u12e5 u13a1 u1423"
"u145d"
"u3ab4 u1489"
My current implementation is:
static void removeDuplication(ArrayList<StringBuilder> directCallList) {
HashSet<StringBuilder> set = new HashSet<StringBuilder>();
for(int i=0; i<directCallList.size()-1; i++) {
if(set.contains(directCallList.get(i)) == false)
set.add(directCallList.get(i));
}
StringBuilder lastString = directCallList.get(directCallList.size()-1);
directCallList.clear();
directCallList.addAll(set);
directCallList.add(lastString);
}
But the performance becomes worse and worse as the ArrayList size grows. Is there any problem with this implementation? Or do you have any better ones in terms of performance?
StringBuilder doesn't implement equals() or hashcode(). Two StringBuilders are only equal if they are the exact same object, so adding them to a HashSet won't exclude two different StringBuilder objects with identical content.
You should convert the StringBuilders to String objects.
Also, you should initialize your HashSet with an "initial capacity" in the constructor. This will help with the speed if you are dealing with large numbers of objects.
Lastly, it's not necessary to call contains() on the hashset before adding an object. Just add your Strings to the set, and the set will reject duplicates (and will return false).
Let's analyze your method to find where we can improve it:
static void removeDuplication(ArrayList<StringBuilder> directCallList) {
HashSet<StringBuilder> set = new HashSet<StringBuilder>();
for(int i=0; i<directCallList.size()-1; i++) {
if(set.contains(directCallList.get(i)) == false)
set.add(directCallList.get(i));
}
This for loop repeats once for each element in the ArrayList. This seems unavoidable for the task at hand. However, since HashSet can only contain one of each item, the if statement is redundant. HashSet.add() does the exact same check again.
StringBuilder lastString = directCallList.get(directCallList.size()-1);
I don't understand the need to get the lastString from your list and then add it. If your loop works correctly, it should have already been added to the HashSet.
directCallList.clear();
Depending on the implementation of the list, this can take up to O(n) time because it might need to visit every element in the list.
directCallList.addAll(set);
Again, this takes O(n) time. If there are no duplicates, set contains the original items.
directCallList.add(lastString);
This line seems to be a logic error. You will add a String which is already in the set and added to directCallList.
}
So overall, this algorithm takes O(n) time, but there is a constant factor of 3. If you can reduce this factor, you can improve the performance. One way to do this is to simply create a new ArrayList, rather than clearing the existing one.
Additionally, this removeDuplication() function can be written in one line if you use the correct constructors and return the ArrayList without duplicates:
static List<StringBuilder> removeDuplication(List<StringBuilder> inList) {
return new ArrayList<StringBuilder>(new HashSet<StringBuilder>(inList));
}
Of course, this still doesn't address the issues with StringBuilder that others have pointed out.
So you had some other options, but I like my solutions short, simple, and to the point. I've changed your method to no longer manipulate the parameter, but rather return a new List. I used a Set<String> to see if the contents of each StringBuilder was already included and returned the unique Strings. I also used a for each loop instead of accessing by index.
static List<StringBuilder> removeDuplication(List<StringBuilder> directCallList) {
HashSet<String> set = new HashSet<String>();
List<StringBuilder> returnList = new ArrayList<StringBuilder>();
for(StringBuilder builder : directCallList) {
if(set.add(builder.toString())
returnList.add(builder);
}
return returnList;
}
As Sam states, StringBuider does not override hashCode and equals and so the Set will not work appropriately.
I think the answer is to wrap the Builder in an object that executes toString only once:
class Wrapper{
final String string;
final StringBuilder builder;
Wrapper(StringBuilder builder){
this.builder = builder;
this.string = builder.toString();
}
public int hashCode(){return string.hashCode();}
public boolean equals(Object o){return string.equals(o);}
}
public Set removeDups(List<StringBuilder> list){
Set<Wrapper> set = ...;
for (StringBuilder builder : list)
set.add(new Wrapper(builder));
return set;
}
The removeDups method could be updated to extract the builders from the set and return a List<StringBuilder>
As explained, StringBuilders don't override Object#equals and aren't Comparable.
Although using StringBuilders to concatenate your Strings is the way to go, I would suggest that once you are done with your concatenation, you should store the underlying strings (stringBuilder.toString()) instead of the StringBuilders in your list.
Removing duplicates then becomes a one line:
Set<String> set = new HashSet<String>(list);
Or even better, store the strings in the set directly if you don't need to know that there are duplicates.
I have a hashmap initialized as follows:
Hashmap<String[][], Boolean> tests = new Hashmap<String [][], Boolean>();
I would like to insert into tests without having to initialize the key:
tests.put({{"a"}, {"a"}}, true);
However, Java doesn't seem to let me do this. It works if I do it like this:
String[][] hi = {{"a"}, {"a"}};
tests.put(hi, true);
Is there any way to avoid the latter and get the former working?
Can someone also explain the reasoning behind this error?
Thanks
Yes, you can write like this:
tests.put(new String[][] {{"a"}, {"a"}}, true);
This is often referred to as an anonymous array or a just-in-time array.
In your case you would have to use
tests.put(new String[][]{{"a"}, {"a"}}, true);
because as you noticed {{"a"}, {"a"}}
String[][] hi = {{"a"}, {"a"}};
can be used only while creating reference to array.
You can use
tests.put(new String[][]{{"hello", "goodbye"},{"hi", "bye"}}, true);
This is almost definitely not what you want.
Arrays in Java get their equality and hash code from Object -- which is to say, based on their reference identity. So:
String[] a = { "hello" }; // create one array
String[] b = { "hello" }; // create a different array with the same contents
assert a != b; // the two references are to different objects
assert ! a.equals(b); // they're not equal
assert a.hashCode() != b.hashCode(); // neither are their hashes (probably)
a and b will not be equal, and their hash codes will almost certainly not be equal, since they are different objects. This means that if you use an array as the key to a hash map, you won't be able to retrieve the value using an key but the exact one that you created it with: any other array will have a different hash code and will be non-equal, and therefore won't be considered an equivalent key.
The solution is to replace the String[][] with a List<List<String>>. Lists define equality and hash codes based on their contents, so a list containing [ "hello" ] is equal to any other list containing [ "hello" ]:
List<String> x = Arrays.asList("hello");
List<String> y = Arrays.asList("hello");
assert x != y; // the two lists are different objects
assert x.equals(y); // but they're equal
assert x.hashCode() == y.hashCode(); // and so are their hash codes
Now you can use the lists as keys. Keep in mind that once a list is a key to the map, it's not allowed to change values. Doing so will probably break the hash map, because the list's hash code will have changed, but the map won't know about it, so the map will look for it in the wrong hash bucket.
The easiest options here are:
be sure that nobody else has a reference to that same List object and might change it
create a deep copy of the List before you put it into the map (that is, copy the "inner" lists as well as the "outer" one)
For the second option, it'd be something like:
// copy the outer list
List<List<String>> outerCopy = new ArrayList<List<String>>( originalList );
ListIterator<List<String>> listIterator = outerCopy.listIterator();
while (listIterator.hasNext()) {
// make a copy of the inner list
List<String> innerCopy = new ArrayList<String>( listIterator.next() );
listIterator.set(innerCopy);
}
I am looking for a java data structure similar to an ArrayList that when I do an add or a push with only a value argument an index will be returned for me automatically.
For example:
ArrayList<String> elements = new ArrayList<String>();
String element = "foo";
String elementTwo = "bar";
int index1 = elements.add(element); //note this does not exist, i.e. returns bool in api
int index2 = elements.add(elementTwo);
System.out.println(elements.get(index1)); //would give "foo"
I could see writing a wrapper class around ArrayList that manages a counter that is incremented on every add operation and invoking:
ArrayList.add(int index, E element)
Do you really need to write a wrapper around ArrayList for this? This seems like something simple enough to be provided out of the box somewhere?
Edit:
I need the index (key) to be fixed and unique for this usecase. A map was suggested and I agree with that. Does anyone know of a map implementation that gives you an automatically (uniquely) generated key on a value insert? I am just trying to decide if I need to implement my own wrapper for this.
The element will be added at the end of the list. So you can use elements.size()-1 to get the new elements index.
Note that this will not work reliable if multiple threads are modifying the list at the same time.
EDIT: Also note that it might not be a good idea to use an ArrayLists index as a unique ID because an elements index can change (for example when you remove an element or insert a new one using add(int, Object)). If this is a problem depends on what you want to do with the index: If you only need it for a short time after adding an element and can be sure that the list is not modified in the meantime, there is no problem. In the other case even a method returning the index when calling add(Object) would not help because the index does not get updated in anyway. To prevent this issue you can:
Make sure you never remove elements from the list and never add elements using add(int, Object).
Instead of removing elements you could also set them to null using the method set(int, null). This way no elements index will change.
Use some other data structure like for example a map with a custom ID like helloannalil suggests in his answer.
EDIT 2: I did not find a appropriate, ready to use implementation (but this does not mean there is none, of course). To suggest a good solution, more information on the intended use of the data structure is needed, but here are some ideas and notes:
If the maximum number of elements is not to large, an ArrayList could be used and the elements index represents the ID. As stated above, to remove an element it can be set to null so that no indices are changed. When inserting, positions with null values can be reused.
You can also use one of the two methods show in this answer: https://stackoverflow.com/a/8939049/1347968 (keywords AtomicLong or IdentityHashMap)
Do not depend on the "uniqueness" of Object.hashCode() or System.identityHashCode(Object) as it is not guaranteed (try it by running the example at the bottom of Suns/Oracles Bug #6321873).
Well what I do in that cases (I love ArrayLists) is to get the last index by asking the size of the list:
String thing = "theThing";
List<String> strList = new ArrayList<String>();
strList.add(thing);
int indexOfThing = strList.size() - 1;
I mean, is easier than implement your own List and just works.
if you really want this function, you can use map but not list
Based on your comments and edited question I think you can extend a HashMap for your use like this:
public class MyMap<V> extends HashMap<Integer, V> {
private static final long serialVersionUID = 1L;
public int add(V elem) {
int key = System.identityHashCode(elem);
super.put(key, elem);
return key;
}
}
Then inside your class declare MyMap like this:
private MyMap<String> map = new MyMap<String>();
And then add your elements to MyMap like this:
.....
.....
String element = "foo";
String elementTwo = "bar";
int index1 = map.add(element);
int index2 = map.add(elementTwo);
Now you have index1 and index2 as indices of you inserted strings that you can use or pass around for the lifetime of your application. You can insert or remove elements in MyMap as many times you want but your indices (index1 and index2) will give you back your inserted elements like this:
String elem1 = map.get(index1); // will return "foo"
String elem2 = map.get(index2); // will return "bar"
String thing = "theThing";
List<String> strList = new ArrayList<String>();
strList.add(thing);
int indexOfThing = strList.size() - 1;
If you remove an item, this will no longer work.