I am currently throwing an obfuscation program for school homework together. I am trying to make the program read a file and then create a new file that replaces each letter in the file with some a corresponding value that I pull from a HashMap. I set up a whole bunch of keys and values, but later in the process of writing the new file I try to check if the map contains a key before appending to the new String. The characters that are checked are in fact in the file that I am reading from to test, and the file is read correctly. Yet it fails with the encryptionDict.containsKey() (my hashmap) method.
I hope some Java expert can help me figure this out! I'm pretty clueless, I'm more of a C and D guy. The only thought that struck me was that it would be something like with Strings where "foo" != "foo". But chars aren't objects.
The code is in a pastebin below, the key parts to look at is the class constructor, the method encrypt, and the method initDictionary, also can someone tell me why HashMap<char, String> is invalid, is it because I have to use an object?
The code: http://pastebin.com/NcHTHPfw
private HashMap<char [], String> initDictionary() {
HashMap<char [], String> d = new HashMap<char [], String>();
d.put("a".toCharArray(), "!\"#¤");
d.put("b".toCharArray(), "¤#\"!");
d.put("c".toCharArray(), "\"#¤%");
d.put("d".toCharArray(), "%¤#\"");
d.put("e".toCharArray(), "#¤%&");
d.put("f".toCharArray(), "&%¤#");
d.put("g".toCharArray(), "¤%&/");
d.put("h".toCharArray(), "/&%¤");
d.put("i".toCharArray(), "%&/(");
d.put("j".toCharArray(), "(/&%");
d.put("k".toCharArray(), "&/()");
d.put("l".toCharArray(), ")(/&");
d.put("m".toCharArray(), "/()=");
d.put("n".toCharArray(), "=)(/");
d.put("o".toCharArray(), "()=?");
d.put("p".toCharArray(), "?=)(");
d.put("q".toCharArray(), ")=?!");
d.put("r".toCharArray(), "!?=)");
d.put("s".toCharArray(), "=?!\"");
d.put("t".toCharArray(), "\"!?=");
d.put("u".toCharArray(), "?!\"#");
d.put("v".toCharArray(), "#\"!?");
d.put("w".toCharArray(), ";:*^");
d.put("x".toCharArray(), "^*:;");
d.put("y".toCharArray(), ":*^>");
d.put("z".toCharArray(), ">^*:");
// etc.
This is the problematic bit. You can't use arrays as hash keys in Java, as Array does not override the equals() and hashCode() methods.
The hashCode is used to find the correct bucket that contains the object you are looking for, and the equals() method compares the actual Objects. To make use of a HashMap, you need to override both of these methods in a sensible way, which you can't as array classes are final. So the only thing you could do if you absolutely insist on using char arrays is to use a wrapper class as key that has a char array.
Something like this:
public class Key {
private final char[] array;
public Key(final String string) {
this(string.toCharArray());
}
public Key(final char[] array) {
this.array = array;
}
public char[] getArray() {
return array;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Key key = (Key) o;
if (!Arrays.equals(array, key.array)) return false;
return true;
}
#Override
public int hashCode() {
return array != null ? Arrays.hashCode(array) : 0;
}
}
Now you can have a Map<Key, String> and bould a Key Object from either a String or a char[]
The problem is that all char[] are unique, regardless of their contents. It appears you really want to be using a String. Normally, you can write such a program without using a char[]
Instead you can write
private Map<Character, String> initDictionary() {
Map<Character, String> d = new HashMap<Character, String>();
d.put('a', "!\"#¤");
// etc
Don't use arrays as key, they are not comparable, so when it compares the value does not match
You are using a HashMap<char [], String>. This doesn't work as .equals and .hashCode are not defined on arrays. This simple test program verifiesthis:
public static void main(String[] args) {
char[] a = String.valueOf('a').toCharArray();
char[] b = String.valueOf('a').toCharArray();
System.out.println(a.hashCode() == b.hashCode());
}
You could use a Map<Character, String> for your purposes. Another solution would be to use an array String[MAX_INDEX] which is indexed by filecontent.charAt(index) - this may be even simpler if MAX_INDEX is small (like 'z') in your case.
Related
CompareObj is a class in java It consists of three attributes String rowKey, Integer hitCount, Long recency
public CompareObj(String string, Integer i) {
this.rowKey = string;
this.hitCount = i%10;
this.recency= (Long) i*1000;
}
Now I created a treeMap
Comparator<CompareObj> comp1 = (e1,e2) -> e1.getHitCount().compareTo(e2.getHitCount());
Comparator<CompareObj> comp2 = (e1,e2) -> e2.getRecency().compareTo(e1.getRecency());
Comparator<CompareObj> result = comp1.thenComparing(comp2);
TreeMap<CompareObj, CompareObj> tM = new TreeMap<CompareObj, CompareObj>(result);
for(int i=0;i<=1000;i++)
{
CompareObj cO = new CompareObj("A"+i, i);
tM.put(cO,cO);
}
for(int i=0;i<=1000;i++)
{
CompareObj cO = new CompareObj("A"+i, i);
CompareObj values = tM.get(cO);
System.out.println(values.getRowKey()); // Line 28: get Null Pointer Exception
}
Also I overide hashCode and Equals. Still I get nullponter exception.
#Override
public int hashCode() {
return Objects.hash(getRowKey());
}
#Override
public boolean equals(Object obj) {
if(this==obj) return true;
if(!(obj instanceof CompareObj)) return false;
CompareObj compareObj = (CompareObj) obj;
return Objects.equals(this.getRowKey(), compareObj.getRowKey());
}
Here when I try to retrive value from treemap back I get Null Pointer exception in the line mentioned. How to solve this
If I want to implement comapareTo() of Comaprable interface, how should I implement if there is multiple sort conditions.
The first thing to understand, is the NullPointerException. If you get that exception on the exact line
System.out.println(values.getRowKey());
then either System.out or values is null. Since we can preclude System.out being null, it’s the values variable, which contains the result of get and can be null if the lookup failed.
Since you are initializing the TreeMap with a custom Comparator, that Comparatordetermines equality. Your Comparator is based on the properties getHitCount() and getRecency() which must match, which implies that when the lookup fails, the map doesn’t contain an object having the same values as reported by these two methods.
You show that you construct objects with the same values but not the code of these getters. There must be an inconsistency. As Misha pointed out, your posted code can’t be the code you have ran when getting the exception, therefore we can’t help you further (unless you post the real code you ran).
Hello I am trying to avoid creating object by using normal array to store 2 key value, but it does not seems to work.
May I know is there any solution to avoid that one object creation or am I just trying too hard?
Forgotten to ADD:
1) I know why it does not work... I won't be implementing the equals() and hashcode() for key if I don't.
2) Basically I am trying to avoid 1 object creation when retrieving the key.
Usually in the service class there will be a method
public void get(String key1, String key2){
return keyMap.get(new Key(key1,key2)); <>>avoiding the new Key()
}
BREAK LINE
import java.util.HashMap;
import java.util.Map;
public class ArrayMap {
/**
* #param args
*/
public static void main(String[] args) {
/*start A Possible to get this to work? */
Map<String[], String> arrMap = new HashMap<>();
arrMap.put(new String[] { "hello", "hi" }, "hello motto");
System.out.println(arrMap);
System.out.println(arrMap.get(new String[] { "hello", "hi" })); // print
// null
/* end of A */
/*Start of B: Reason: to avoid this */
Map<Key, String> keyMap = new HashMap<Key, String>();
keyMap.put(new Key("hello", "hi"), "hello motto"); // I wish to avoid one object creation
System.out.println(keyMap.get(new Key("hello", "hi"))); // print
// "hello motto"
/*End of B: Reason: to avoid this */
}
}
class Key {
private final String key1;
private final String key2;
public Key(String key1, String key2) {
this.key1 = key1;
this.key2 = key2;
}
public String getKey1() {
return key1;
}
public String getKey2() {
return key2;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((key1 == null) ? 0 : key1.hashCode());
result = prime * result + ((key2 == null) ? 0 : key2.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Key other = (Key) obj;
if (key1 == null) {
if (other.key1 != null)
return false;
} else if (!key1.equals(other.key1))
return false;
if (key2 == null) {
if (other.key2 != null)
return false;
} else if (!key2.equals(other.key2))
return false;
return true;
}
}
There are a couple of problems with this approach.
1. You can't override the equals()/hashcode() methods for arrays - This is an issue because the HashMap won't be able to correctly determine whether it has looked up the right key or not.
2. You're still creating new objects every time you want to create a key. Arrays are objects - you save nothing by creating a new one every time. Might as well use your original Key object.
Possible Solution
So I'm going to assume the reason why you want to avoid creating a new object every time is because you're going to be calling get(key) on that HashMap a LOT. If that's the case, why not create a mutable Key instance that remains internal to your ArrayMap object. Every time you want to key on two Strings, simply set them in your mutable Key instance and use that mutable instance for the lookup. Then you don't create a new Key every time you want to look up a value for a pair of Strings.
May I know is there any solution to avoid that one object creation or am I just trying too hard?
If you are unsure whether you're trying too hard, then you probably are. Based on the information you've given us, this looks like premature optimization.
A couple of pertinent points:
Object creation is not expensive, especially if the object is short-lived.
The only way to be sure of the performance characteristics of a piece of software is through profiling.
In these lines
arrMap.put(new String[] { "hello", "hi" }, "hello motto");
System.out.println(arrMap);
System.out.println(arrMap.get(new String[] { "hello", "hi" })); // print
// null
you use a String[] as a key. That object doesn't have a custom equals() method like the one you have in your Key class where you can compare the contents. So when you try to do map.get() passing in a new String[] (but with the same content), it won't find anything because it's not the same object.
What you might want to do is this
String[] array = new String[] { "hello", "hi" };
arrMap.put(array , "hello motto");
System.out.println(arrMap);
System.out.println(arrMap.get(array)); // print hello motto
You really shouldn't use array types as keys to Maps.
In java, array1.equals(array2) only if array1 == array2, i.e. they are the same exact instance in memory. So that causes the Map to view them as separate keys. You're better off going with your Key class for the map's key
I have a file of Integer[]s that is too large to put in memory. I would like to search for all arrays with a last member of x and use them in other code. Is there a way to use Guava's multimap to do this, where x is the key and stored in memory and the Integer[] is the value and that is stored on disk? In this scenario, the keys are not unique, but key-value pairs are unique. Reading of this multimap (assuming that it's possible) will be concurrent. I'm also open to suggestions of other ways to approach this.
Thanks
You could create a class representing an array on disk (based on its index in the file of arrays), let's call it FileBackedIntArray, and put instances of that as the values of a HashMultimap<Integer, FileBackedIntArray>:
public class FileBackedIntArray {
// Index of the array in the file of arrays
private final int index;
private final int lastElement;
public FileBackedIntArray(int index, int lastElement) {
this.index = index;
this.lastElement = lastElement;
}
public int getIndex() {
return index;
}
public int[] readArray() {
// Read the file and deserialize the array at the associated index
return smth;
}
public int getLastElement() {
return lastElement;
}
#Override
public int hashCode() {
return index;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || o.getClass() != getClass()) {
return false;
}
return index == ((FileBackedIntArray) o).index;
}
}
Do you actually need an Integer[] and not an int[], by the way (i.e. you can have null values)? As you've said in the comments, you don't really need an Integer[], so using intss everywhere will avoid boxing/unboxing and will save a lot of space since you appear to have lots of them. Hopefully you don't have a huge number of possible values for the last element (x).
You then create an instance for each array and read the last element to put it the Multimap without keeping the array around. Populating the Multimap needs to be either sequential or protected with a lock if concurrent, but reading can be concurrent without any protection. You could even create an ImmutableMultimap once the HashMultimap has been populated, to guard against any modification, a safe practice in a concurrent environment.
Why cannot I retrieve an element from a HashSet?
Consider my HashSet containing a list of MyHashObjects with their hashCode() and equals() methods overridden correctly. I was hoping to construct a MyHashObject myself, and set the relevant hash code properties to certain values.
I can query the HashSet to see if there "equivalent" objects in the set using the contains() method. So even though contains() returns true for the two objects, they may not be == true.
How come then there isn’t any get() method similar to how the contains() works?
What is the thinking behind this API decision?
If you know what element you want to retrieve, then you already have the element. The only question for a Set to answer, given an element, is whether it contains() it or not.
If you want to iterator over the elements, just use a Set.iterator().
It sounds like what you're trying to do is designate a canonical element for an equivalence class of elements. You can use a Map<MyObject,MyObject> to do this. See this Stack Overflow question or this one for a discussion.
If you are really determined to find an element that .equals() your original element with the constraint that you must use the HashSet, I think you're stuck with iterating over it and checking equals() yourself. The API doesn't let you grab something by its hash code. So you could do:
MyObject findIfPresent(MyObject source, HashSet<MyObject> set)
{
if (set.contains(source)) {
for (MyObject obj : set) {
if (obj.equals(source))
return obj;
}
}
return null;
}
It is brute-force and O(n) ugly, but if that's what you need to do...
You can use HashMap<MyHashObject, MyHashObject> instead of HashSet<MyHashObject>.
Calling containsKey() on your "reconstructed" MyHashObject will first hashCode() - check the collection, and if a duplicate hashcode is hit, finally equals() - check your "reconstructed" against the original, at which you can retrieve the original using get()
Complexity is O(1) but the downside is you will likely have to override both equals() and hashCode() methods.
It sounds like you're essentially trying to use the hash code as a key in a map (which is what HashSets do behind the scenes). You could just do it explicitly, by declaring HashMap<Integer, MyHashObject>.
There is no get for HashSets because typically the object you would supply to the get method as a parameter is the same object you would get back.
If you know the order of elements in your Set, you can retrieve them by converting the Set to an Array. Something like this:
Set mySet = MyStorageObject.getMyStringSet();
Object[] myArr = mySet.toArray();
String value1 = myArr[0].toString();
String value2 = myArr[1].toString();
The idea that you need to get the reference to the object that is contained inside a Set object is common. It can be archived by 2 ways:
Use HashSet as you wanted, then:
public Object getObjectReference(HashSet<Xobject> set, Xobject obj) {
if (set.contains(obj)) {
for (Xobject o : set) {
if (obj.equals(o))
return o;
}
}
return null;
}
For this approach to work, you need to override both hashCode() and equals(Object o) methods
In the worst scenario we have O(n)
Second approach is to use TreeSet
public Object getObjectReference(TreeSet<Xobject> set, Xobject obj) {
if (set.contains(obj)) {
return set.floor(obj);
}
return null;
}
This approach gives O(log(n)), more efficient.
You don't need to override hashCode for this approach but you have to implement Comparable interface. ( define function compareTo(Object o)).
One of the easiest ways is to convert to Array:
for(int i = 0; i < set.size(); i++) {
System.out.println(set.toArray()[i]);
}
If I know for sure in my application that the object is not used in search in any of the list or hash data structure and not used equals method elsewhere except the one used indirectly in hash data structure while adding. Is it advisable to update the existing object in set in equals method. Refer the below code. If I add the this bean to HashSet, I can do group aggregation on the matching object on key (id). By this way I am able to achieve aggregation functions such as sum, max, min, ... as well. If not advisable, please feel free to share me your thoughts.
public class MyBean {
String id,
name;
double amountSpent;
#Override
public int hashCode() {
return id.hashCode();
}
#Override
public boolean equals(Object obj) {
if(obj!=null && obj instanceof MyBean ) {
MyBean tmpObj = (MyBean) obj;
if(tmpObj.id!=null && tmpObj.id.equals(this.id)) {
tmpObj.amountSpent += this.amountSpent;
return true;
}
}
return false;
}
}
First of all, convert your set to an array. Then, get the item by indexing the array.
Set uniqueItem = new HashSet();
uniqueItem.add("0");
uniqueItem.add("1");
uniqueItem.add("0");
Object[] arrayItem = uniqueItem.toArray();
for(int i = 0; i < uniqueItem.size(); i++) {
System.out.println("Item " + i + " " + arrayItem[i].toString());
}
If you could use List as a data structure to store your data, instead of using Map to store the result in the value of the Map, you can use following snippet and store the result in the same object.
Here is a Node class:
private class Node {
public int row, col, distance;
public Node(int row, int col, int distance) {
this.row = row;
this.col = col;
this.distance = distance;
}
public boolean equals(Object o) {
return (o instanceof Node &&
row == ((Node) o).row &&
col == ((Node) o).col);
}
}
If you store your result in distance variable and the items in the list are checked based on their coordinates, you can use the following to change the distance to a new one with the help of lastIndexOf method as long as you only need to store one element for each data:
List<Node> nodeList;
nodeList = new ArrayList<>(Arrays.asList(new Node(1, 2, 1), new Node(3, 4, 5)));
Node tempNode = new Node(1, 2, 10);
if(nodeList.contains(tempNode))
nodeList.get(nodeList.lastIndexOf(tempNode)).distance += tempNode.distance;
It is basically reimplementing Set whose items can be accessed and changed.
If you want to have a reference to the real object using the same performance as HashSet, I think the best way is to use HashMap.
Example (in Kotlin, but similar in Java) of finding an object, changing some field in it if it exists, or adding it in case it doesn't exist:
val map = HashMap<DbData, DbData>()
val dbData = map[objectToFind]
if(dbData!=null){
++dbData.someIntField
}
else {
map[dbData] = dbData
}
I have a code to return an arrayList with the duplicates of an ArrayList
but seems it's not working, I am comparing all items in the array...
public ArrayList<ObjectList> duplicates(ArrayList<ObjectList> someObjectsList) {
ArrayList<ObjectList> ret = new ArrayList<ObjectList>();
for ( ObjectList aSomeObjectsList: someObjectsList) {
String field1 = aSomeObjectsList.get1();
String field2 = aSomeObjectsList.get2();
String field3 = aSomeObjectsList.get3();
String field4 = aSomeObjectsList.get4();
for (ObjectList someObject : ret) {
if (
field1.trim().equals(someObject.get1())&&
field2.trim().equals(someObject.get2())&&
field3.trim().equals(someObject.get3())&&
field4.trim().equals(someObject.get4())
){
ret.add(aSomeObjectsList);
}
}
}
return ret;
}
But i guess I am doing something wrong because it doesn't return anything, and I know it has duplictates under this 4 field criteria
Thanks in advance
for (Object someObject : ret) {
if (
field1.trim().equals(someObject.get1())&&
field2.trim().equals(someObject.get2())&&
field3.trim().equals(someObject.get3())&&
field4.trim().equals(someObject.get4())
){
ret.add(aSomeObjectsList);
}
}
The above loop wouldn't work, since it has the size of zero.
Here you go,
public Set<ObjectList> duplicates(ArrayList<ObjectList> someObjectsList) {
Set<ObjectList> originals = new HashSet<ObjectList>();
Set<ObjectList> duplicates = new HashSet<ObjectList>();
for ( ObjectList aSomeObjectsList: someObjectsList) {
boolean added = originals.add(aSomeObjectsList);
if(!added){
duplicates.add(aSomeObjectsList);
}
}
return duplicates;
}
This would work, provided your ObjectList class have the correct implementation of hashCode() and equals() methods.
Disclaimer: This implementation will not provide the information about how many times a particular object was duplicated in the provided list. It will just tell you that a particular object was duplicated. I assumed that that was your real intention. If you wanna count, how many times, you have to modify the code accordingly.
Hint/Suggestion: You should override the equals() method and place your field equality check in there instead, once and for all.
This shouldn't compile - if aSomeObjectsList is an Object then it doesn't have methods get1(), get2(), etc.
Your logic won't work because you aren't checking each element in your input List against the other elements in the input List; rather, you're trying to check the return List.
Also, this is not a really efficient way to check for duplicates in a collection. A better way would be to use a HashMap, where you could check set membership in roughly constant time. If you have to use a List, then sort it first (assuming your objects have a natural ordering) and check adjacent members for equality.
Barring those two, just use List.contains().
Here's a way you can do this. I have defined a basic class ObjectList that shows a way to implement equals and hashCode. Note that this assumes that all the internal variables are non-null. If these variables can contain null then you will need to check for that when computing the equals/hashCode. Also, the objects in this class must also themselves properly implement equals/hashCode.
public class ObjectList {
private int h;
private Object obj1;
private Object obj2;
private Object obj3;
private Object obj4;
#Override
public boolean equals(final Object o) {
if (!(o instanceof ObjectList))
return false;
final ObjectList that = (ObjectList) o;
return that.obj1.equals(obj1) && that.obj2.equals(obj2)
&& that.obj3.equals(obj3) && that.obj4.equals(obj4);
}
#Override
public int hashCode() {
// caches the hashcode since it could be costly to recompute every time
// but this assumes that your object is essentially immutable
// (which it should be if you are using equals/hashCode. If this is not
// true and you want to just temporarily use this when doing the duplicate
// test, move the h variable definition from the object level to this method
// and remove this if statement.
if (h != 0)
return h;
h = obj1.hashCode();
h = h * 31 + obj2.hashCode();
h = h * 31 + obj3.hashCode();
h = h * 31 + obj4.hashCode();
return h;
}
}
public Collection<ObjectList> duplicates(
final Collection<ObjectList> someObjectsList) {
final Set<ObjectList> unique = new HashSet<ObjectList>(someObjectsList);
final ArrayList<ObjectList> ret = new ArrayList<ObjectList>(someObjectsList);
for (final ObjectList o : unique) {
ret.remove(o);
}
// The ret list now contains the duplicate instances; instances
// with more than two occurrences will occur multiple times still in
// this list.
return ret;
// If you want a list of unique duplicate instances then, comment out the above
// return and uncomment this one.
// return new HashSet<ObjectList>(ret);
}
Using Collection<ObjectList> is better, if you can do that, for both the parameter and returned value so you can vary the implementations (ArrayList, Set, etc).