Would it be possible to add an ArrayList as the key of HashMap. I would like to keep the frequency count of bigrams. The bigram is the key and the value is its frequency.
For each of the bigrams like "he is", I create an ArrayList for it and insert it into the HashMap. But I am not getting the correct output.
public HashMap<ArrayList<String>, Integer> getBigramMap(String word1, String word2) {
HashMap<ArrayList<String>, Integer> hm = new HashMap<ArrayList<String>, Integer>();
ArrayList<String> arrList1 = new ArrayList<String>();
arrList1 = getBigram(word1, word2);
if (hm.get(arrList1) != null) {
hm.put(arrList1, hm.get(arrList1) + 1);
} else {
hm.put(arrList1, 1);
}
System.out.println(hm.get(arrList1));
return hm;
}
public ArrayList<String> getBigram(String word1, String word2) {
ArrayList<String> arrList2 = new ArrayList<String>();
arrList2.add(word1);
arrList2.add(word2);
return arrList2;
}
Yes you can have ArrayLists as a keys in a hash map, but it is a very bad idea since they are mutable.
If you change the ArrayList in any way (or any of its elements), the mapping will basically be lost, since the key won't have the same hashCode as it had when it was inserted.
The rule of thumb is to use only immutable data types as keys in a hash map. As suggested by Alex Stybaev, you probably want to create a Bigram class like this:
final class Bigram {
private final String word1, word2;
public Bigram(String word1, String word2) {
this.word1 = word1;
this.word2 = word2;
}
public String getWord1() {
return word1;
}
public String getWord2() {
return word2;
}
#Override
public int hashCode() {
return word1.hashCode() ^ word2.hashCode();
}
#Override
public boolean equals(Object obj) {
return (obj instanceof Bigram) && ((Bigram) obj).word1.equals(word1)
&& ((Bigram) obj).word2.equals(word2);
}
}
Why can't you use something like this:
class Bigram{
private String firstItem;
private String secondItem;
<getters/setters>
#Override
public int hashCode(){
...
}
#Override
public boolean equals(){
...
}
}
instead of using the dynamic collection for limited number of items (two).
From the documentation:
Note: great care must be exercised if mutable objects are used as map
keys. The behavior of a map is not specified if the value of an object is
changed in a manner that affects equals comparisons while the
object is a key in the map. A special case of this prohibition is that it
is not permissible for a map to contain itself as a key. While it is
permissible for a map to contain itself as a value, extreme caution is
advised: the equals and hashCode methods are no longer
well defined on such a map.
You have to take care when you are using mutable objects as keys for the sake of hashCode and equals.
The bottom line is that it is better to use immutable objects as keys.
Try this ,this will work.
public Map<List, Integer> getBigramMap (String word1,String word2){
Map<List,Integer> hm = new HashMap<List, Integer>();
List<String> arrList1 = new ArrayList<String>();
arrList1 = getBigram(word1, word2);
if(hm.get(arrList1) !=null){
hm.put(arrList1, hm.get(arrList1)+1);
}
else {
hm.put(arrList1, 1);
}
System.out.println(hm.get(arrList1));
return hm;
}
I've come up with this solution. It is obviously not usable in all cases, for example over stepping the hashcodes int capacity, or list.clone() complications(if the input list gets changed, key stays the same as intended, but when the items of List are mutable, cloned list has the same reference to its items, which would result in changing the key itself).
import java.util.ArrayList;
public class ListKey<T> {
private ArrayList<T> list;
public ListKey(ArrayList<T> list) {
this.list = (ArrayList<T>) list.clone();
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
for (int i = 0; i < this.list.size(); i++) {
T item = this.list.get(i);
result = prime * result + ((item == null) ? 0 : item.hashCode());
}
return result;
}
#Override
public boolean equals(Object obj) {
return this.list.equals(obj);
}
}
---------
public static void main(String[] args) {
ArrayList<Float> createFloatList = createFloatList();
ArrayList<Float> createFloatList2 = createFloatList();
Hashtable<ListKey<Float>, String> table = new Hashtable<>();
table.put(new ListKey(createFloatList2), "IT WORKS!");
System.out.println(table.get(createFloatList2));
createFloatList2.add(1f);
System.out.println(table.get(createFloatList2));
createFloatList2.remove(3);
System.out.println(table.get(createFloatList2));
}
public static ArrayList<Float> createFloatList() {
ArrayList<Float> floatee = new ArrayList<>();
floatee.add(34.234f);
floatee.add(new Float(33));
floatee.add(null);
return floatee;
}
Output:
IT WORKS!
null
IT WORKS!
Sure it possible. I suppose the issue in your put. Try obtain key for bigram, increment it, remove entry with this bigram and insert updated value
Unlike Array, List can be used as the key of a HashMap, but it is not a good idea, since we should always try to use an immutable object as the key.
.toString() method getting the String represtenation is a good key choice in many cases, since String is an immuteable object and can prefectly stands for the array or list.
Please check below my code in order to understand if key is ArrayList in Map and how JVM will do it for inputs:
here i write hashCode and equals method for TesthashCodeEquals class.
package com.msq;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
class TesthashCodeEquals {
private int a;
private int b;
public TesthashCodeEquals() {
// TODO Auto-generated constructor stub
}
public TesthashCodeEquals(int a, int b) {
super();
this.a = a;
this.b = b;
}
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public int getB() {
return b;
}
public void setB(int b) {
this.b = b;
}
public int hashCode() {
return this.a + this.b;
}
public boolean equals(Object o) {
if (o instanceof TesthashCodeEquals && o != null) {
TesthashCodeEquals c = (TesthashCodeEquals) o;
return ((this.a == c.a) && (this.b == c.b));
} else
return false;
}
}
public class HasCodeEquals {
public static void main(String[] args) {
Map<List<TesthashCodeEquals>, String> m = new HashMap<>();
List<TesthashCodeEquals> list1=new ArrayList<>();
list1.add(new TesthashCodeEquals(1, 2));
list1.add(new TesthashCodeEquals(3, 4));
List<TesthashCodeEquals> list2=new ArrayList<>();
list2.add(new TesthashCodeEquals(10, 20));
list2.add(new TesthashCodeEquals(30, 40));
List<TesthashCodeEquals> list3=new ArrayList<>();
list3.add(new TesthashCodeEquals(1, 2));
list3.add(new TesthashCodeEquals(3, 4));
m.put(list1, "List1");
m.put(list2, "List2");
m.put(list3, "List3");
for(Map.Entry<List<TesthashCodeEquals>,String> entry:m.entrySet()){
for(TesthashCodeEquals t:entry.getKey()){
System.out.print("value of a: "+t.getA()+", value of b: "+t.getB()+", map value is:"+entry.getValue() );
System.out.println();
}
System.out.println("######################");
}
}
}
.
output:
value of a: 10, value of b: 20, map value is:List2
value of a: 30, value of b: 40, map value is:List2
######################
value of a: 1, value of b: 2, map value is:List3
value of a: 3, value of b: 4, map value is:List3
######################
so this will check the number of objects in List and the values of valriabe in object. if number of objects are same and the values of instance variables is also same then it will consider duplicate key and override the key.
now if i change only the value of object on list3
list3.add(new TesthashCodeEquals(2, 2));
then it will print:
output
value of a: 2, value of b: 2, map value is:List3
value of a: 3, value of b: 4, map value is:List3
######################
value of a: 10, value of b: 20, map value is:List2
value of a: 30, value of b: 40, map value is:List2
######################
value of a: 1, value of b: 2, map value is:List1
value of a: 3, value of b: 4, map value is:List1
######################
so that It always check the number of objects in List and the value of instance variable of object.
thanks
ArrayList.equals() is inherited from java.lang.Object - therefore equals() on ArrayList is independent of the content of the list.
If you want to use an ArrayList as a map key, you will need to override equals() and hashcode() in order to make two arraylists with the same content in the same order return true on a call to equals() and return the same hashcode on a call to hashcode().
Is there any particular reason you have to use an ArrayList as opposed to say a simple String as the key?
edit: Ignore me, as Joachim Sauer pointed out below, I am so wrong it's not even funny.
Related
The commonly occurring question of finding k most frequent words in a book ,(words can dynamically be added), is usually solved using combination of trie and heap.
However, I think even using a TreeSet should suffice and be cleaner with log(n) performance for insert and retrievals.
The treeset would contain a custom object:
class MyObj implements Comparable{
String value;
int count;
public int incrementCount(){count++;}
//override equals and hashcode to make this object unique by string 'value'
//override compareTo to compare count
}
Whenever we insert object in the treeset we first check if the element is already present in the treeset if yes then we get the obj and increment the count variable of that object.
Whenever, we want to find the k largest words , we just iterate over the first k elements of the treeset
What are your views on the above approach? I feel this approach is easier to code and understand and also matches the time complexity of the trie and heap approach to get k largest elements
EDIT: As stated in one of the answers , incrementing count variable after myobj has been inserted wouldn't re-sort the treeset/treemap. So ,after incrementing the count , I will additionally need to remove and reinsert the object in the treeset/treemap
Once you enter an object into the TreeSet, if the properties used in the comparison of the compareTo method changes, the TreeSet (or the underlying TreeMap) does not reorder the elements. Hence, this approach does not work as you expect.
Here's a simple example to demonstrate it
public static class MyObj implements Comparable<MyObj> {
String value;
int count;
MyObj(String v, int c) {
this.value = v;
this.count = c;
}
public void incrementCount(){
count++;
}
#Override
public int compareTo(MyObj o) {
return Integer.compare(this.count, o.count); //This does the reverse. Orders by freqency
}
}
public static void main(String[] args) {
Set<MyObj> set = new TreeSet<>();
MyObj o1 = new MyObj("a", 1);
MyObj o2 = new MyObj("b", 4);
MyObj o3 = new MyObj("c", 2);
set.add(o1);
set.add(o2);
set.add(o3);
System.out.println(set);
//The above prints [a-1, c-2, b-4]
//Increment the count of c 4 times
o3.incrementCount();
o3.incrementCount();
o3.incrementCount();
o3.incrementCount();
System.out.println(set);
//The above prints [a-1, c-6, b-4]
As we can see the object corresponding to c-6 does not get pushed to the last.
//Insert a new object
set.add(new MyObj("d", 3));
System.out.println(set);
//this prints [a-1, d-3, c-6, b-4]
}
EDIT:
Caveats/Problems:
Using count when comparing two words would remove one word if both words have the same frequency. So, you need to compare the actual words if their frequencies are same.
It would work if we remove and reinsert the object with the updated frequency. But for that, you need to get that object(MyObj instance for a specified value to know the frequency so far) from the TreeSet. A Set does not have a get method. Its contains method just delegates to the underlying TreeMap's containsKey method which identifies the object by using the compareTo logic (and not equals). The compareTo function also takes into account the frequency of the word, so we cannot identify the word in the set to remove it (unless we iterate the whole set on each add)
A TreeMap should work if you remove and insert the object, with an integer key as a frequency and a list of MyObj as a value, the keys are sorted by frequency. An update of the above code demonstrate it:
public class MyObj {
String value;
int count;
MyObj(String v, int c) {
this.value = v;
this.count = c;
}
public int getCount() {
return count;
}
public void incrementCount() {
count++;
}
#Override
public String toString() {
return value + " " + count;
}
public static void put(Map<Integer, List<MyObj>> map, MyObj value) {
List<MyObj> myObjs = map.get(value.getCount());
if (myObjs == null) {
myObjs = new ArrayList<>();
map.put(value.getCount(),myObjs);
}
myObjs.add(value);
}
public static void main(String[] args) {
TreeMap<Integer, List<MyObj>> set = new TreeMap<>();
MyObj o1 = new MyObj("a", 1);
MyObj o2 = new MyObj("b", 4);
MyObj o3 = new MyObj("c", 2);
MyObj o4 = new MyObj("f", 4);
put(set,o1);
put(set,o2);
put(set,o3);
System.out.println(set);
put(set,o4);
System.out.println(set);
}
}
A LinkedList contains a set of Integer[]. Each Integer[] in the list has 2 numbers.
Ex of the linked list:
Integer[]{1,2}, Integer[]{2,3}.....
Before adding another Integer[] to this LinkedList, I wanto check if another Integer[] with the same data already exists.
Ex: Object to add = Integer[] {2,3}. But this already exists in the LinkedList.
So I want to avoid adding this.
How to verify that object already exists? Is there an inbuild function that can be used? contains() didnt do the trick.
I think you better use a specific class if you are treating coordinates, as an Integer[] is useless for only two numbers, and will cause some problems with contains() and other List methods like .sort() as well.
You better create a Coordinate class, which will hold the two values:
public class Coordinate{
private int x;
private int y;
//getters and setters, constructor
#Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Coord)) {
return false;
}
Coordinate coord = (Coordinate) o;
return coord.x == x &&
coord.y == y;
}
#Override
public int hashCode() {
int result = 17;
result = 31 * result + x;
result = 31 * result + y;
return result;
}
}
Then you can use:
LinkedList<Coordinate>
Note:
Note that using a Set implementation will be better here, it will prevent having duplicates in the set of coordinates, so we don't need to check for it manually.
Well, you can do it the dumb way:
boolean exists = false;
for (Integer[] integers : list) { // list being the LinkedList
if (Arrays.equals(integers, value)) {
exists = true;
break;
}
}
if (!exists) {
list.add(value);
}
You can use Stream with Set to solve your problem like below:
List<Set<Integer>> list = new LinkedList<>();
list.add(Stream.of(1, 2).collect(Collectors.toSet()));
Set<Integer> s1 = new HashSet<>();
s1.add(1);
s1.add(2);
System.out.println(list.contains(s1));
Set<Integer> s2 = new HashSet<>();
s2.add(1);
s2.add(4);
System.out.println(list.contains(s2));
O/P:
true
false
N.B: You can use ArrayList because yo preserve the sequence as well.
If you really really want to do that with contains() (or have no other choice by whatever reason), you can implement it like that:
final Integer[] newPair = {2, 3};
final boolean exists = values.contains(new Object()
{
// note that List.contains() javadoc explicitly specifies that
// newPair is used as the receiver not the argument for equals()
#Override
public final boolean equals(final Object listElement)
{
final Integer[] otherPair = (Integer[]) listElement;
return Arrays.equals(newPair, otherPair);
}
});
I'm developing a Java Application that reads a lot of strings data likes this:
1 cat (first read)
2 dog
3 fish
4 dog
5 fish
6 dog
7 dog
8 cat
9 horse
...(last read)
I need a way to keep all couple [string, occurrences] in order from last read to first read.
string occurrences
horse 1 (first print)
cat 2
dog 4
fish 2 (last print)
Actually i use two list:
1) List<string> input; where i add all data
In my example:
input.add("cat");
input.add("dog");
input.add("fish");
...
2)List<string> possibilities; where I insert the strings once in this way:
if(possibilities.contains("cat")){
possibilities.remove("cat");
}
possibilities.add("cat");
In this way I've got a sorted list where all possibilities.
I use it like that:
int occurrence;
for(String possible:possibilities){
occurrence = Collections.frequency(input, possible);
System.out.println(possible + " " + occurrence);
}
That trick works good but it's too slow(i've got millions of input)... any help?
(English isn’t my first language, so please excuse any mistakes.)
Use a Map<String, Integer>, as #radoslaw pointed, to keep the insertion sorting use LinkedHashMap and not a TreeMap as described here:
LinkedHashMap keeps the keys in the order they were inserted, while a TreeMap is kept sorted via a Comparator or the natural Comparable ordering of the elements.
Imagine you have all the strings in some array, call it listOfAllStrings, iterate over this array and use the string as key in your map, if it does not exists, put in the map, if it exists, sum 1 to actual result...
Map<String, Integer> results = new LinkedHashMap<String, Integer>();
for (String s : listOfAllStrings) {
if (results.get(s) != null) {
results.put(s, results.get(s) + 1);
} else {
results.put(s, 1);
}
}
Make use of a TreeMap, which will keep ordering on the keys as specified by the compare of your MyStringComparator class handling MyString class which wraps String adding insertion indexes, like this:
// this better be immutable
class MyString {
private MyString() {}
public static MyString valueOf(String s, Long l) { ... }
private String string;
private Long index;
public hashcode(){ return string.hashcode(); }
public boolean equals() { // return rely on string.equals() }
}
class MyStringComparator implements Comparator<MyString> {
public int compare(MyString s1, MyString s2) {
return -s1.getIndex().compareTo(s2.gtIndex());
}
}
Pass the comparator while constructing the map:
Map<MyString,Integer> map = new TreeMap<>(new MyStringComparator());
Then, while parsing your input, do
Long counter = 0;
while (...) {
MyString item = MyString.valueOf(readString, counter++);
if (map.contains(item)) {
map.put(map.get(item)+1);
} else {
map.put(item,1);
}
}
There will be a lot of instantiation because of the immutable class, and the comparator will not be consistent with equals, but it should work.
Disclaimer: this is untested code just to show what I'd do, I'll come back and recheck it when I get my hands on a compiler.
Here is the complete solution for your problem,
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DataDto implements Comparable<DataDto>{
public int count = 0;
public String string;
public long lastSeenTime;
public DataDto(String string) {
this.string = string;
this.lastSeenTime = System.currentTimeMillis();
}
public boolean equals(Object object) {
if(object != null && object instanceof DataDto) {
DataDto temp = (DataDto) object;
if(temp.string != null && temp.string.equals(this.string)) {
return true;
}
}
return false;
}
public int hashcode() {
return string.hashCode();
}
public int compareTo(DataDto o) {
if(o != null) {
return o.lastSeenTime < this.lastSeenTime ? -1 : 1;
}
return 0;
}
public String toString() {
return this.string + " : " + this.count;
}
public static final void main(String[] args) {
String[] listOfAllStrings = {"horse", "cat", "dog", "fish", "cat", "fish", "dog", "cat", "horse", "fish"};
Map<String, DataDto> results = new HashMap<String, DataDto>();
for (String s : listOfAllStrings) {
DataDto dataDto = results.get(s);
if(dataDto != null) {
dataDto.count = dataDto.count + 1;
dataDto.lastSeenTime = System.nanoTime();
} else {
dataDto = new DataDto(s);
results.put(s, dataDto);
}
}
List<DataDto> finalResults = new ArrayList<DataDto>(results.values());
System.out.println(finalResults);
Collections.sort(finalResults);
System.out.println(finalResults);
}
}
Ans
[horse : 1, cat : 2, fish : 2, dog : 1]
[fish : 2, horse : 1, cat : 2, dog : 1]
I think this solution will be suitable for your requirement.
If you know that your data is not going to exceed your memory capacity when you read it all into memory, then the solution is simple - using a LinkedList or a and a LinkedHashMap.
For example, if you use a Linked list:
LinkedList<String> input = new LinkedList();
You then proceed to use input.add() as you did originally. But when the input list is full, you basically use Jordi Castilla's solution - but put the entries in the linked list in reverse order. To do that, you do:
Iterator<String> iter = list.descendingIterator();
LinkedHashMap<String,Integer> map = new LinkedHashMap<>();
while (iter.hasNext()) {
String s = iter.next();
if ( map.containsKey(s)) {
map.put( s, map.get(s) + 1);
} else {
map.put(s, 1);
}
}
Now, the only real difference between his solution and mine is that I'm using list.descendingIterator() which is a method in LinkedList that gives you the entries in backwards order, from "horse" to "cat".
The LinkedHashMap will keep the proper order - whatever was entered first will be printed first, and because we entered things in reverse order, then whatever was read last will be printed first. So if you print your map the result will be:
{horse=1, cat=2, dog=4, fish=2}
If you have a very long file, and you can't load the entire list of strings into memory, you had better keep just the map of frequencies. In this case, in order to keep the order of entry, we'll use an object such as this:
private static class Entry implements Comparable<Entry> {
private static long nextOrder = Long.MIN_VALUE;
private String str;
private int frequency = 1;
private long order = nextOrder++;
public Entry(String str) {
this.str = str;
}
public String getString() {
return str;
}
public int getFrequency() {
return frequency;
}
public void updateEntry() {
frequency++;
order = nextOrder++;
}
#Override
public int compareTo(Entry e) {
if ( order > e.order )
return -1;
if ( order < e.order )
return 1;
return 0;
}
#Override
public String toString() {
return String.format( "%s: %d", str, frequency );
}
}
The trick here is that every time you update the entry (add one to the frequency), it also updates the order. But the compareTo() method orders Entry objects from high order (updated/inserted later) to low order (updated/inserted earlier).
Now you can use a simple HashMap<String,Entry> to store the information as you read it (I'm assuming you are reading from some sort of scanner):
Map<String,Entry> m = new HashMap<>();
while ( scanner.hasNextLine() ) {
String str = scanner.nextLine();
Entry entry = m.get(str);
if ( entry == null ) {
entry = new Entry(str);
m.put(str, entry);
} else {
entry.updateEntry();
}
}
Scanner.close();
Now you can sort the values of the entries:
List<Entry> orderedList = new ArrayList<Entry>(m.values());
m = null;
Collections.sort(orderedList);
Running System.out.println(orderedList) will give you:
[horse: 1, cat: 2, dog: 4, fish: 2]
In principle, you could use a TreeMap whose keys contained the "order" stuff, rather than a plain HashMap like this followed by sorting, but I prefer not having either mutable keys in a map, nor changing the keys constantly. Here we are only changing the values as we fill the map, and each key is inserted into the map only once.
What you could do:
Reverse the order of the list using
Collections.reverse(input). This runs in linear time - O(n);
Create a Set from the input list. A Set garantees uniqueness.
To preserve insertion order, you'll need a LinkedHashSet;
Iterate over this set, just as you did above.
Code:
/* I don't know what logic you use to create the input list,
* so I'm using your input example. */
List<String> input = Arrays.asList("cat", "dog", "fish", "dog",
"fish", "dog", "dog", "cat", "horse");
/* by the way, this changes the input list!
* Copy it in case you need to preserve the original input. */
Collections.reverse(input);
Set<String> possibilities = new LinkedHashSet<String>(strings);
for (String s : possibilities) {
System.out.println(s + " " + Collections.frequency(strings, s));
}
Output:
horse 1
cat 2
dog 4
fish 2
I wrote a class that is to be stored in a linkedlist, with 3 fields in the class. One of these fields is a String, which I would like to search for in the linked list.
Example
LinkedList
Obj1
String name = "first";
int age = 2;
int size = 4;
Obj2
String name = "second";
int age = 3;
int size = 6;
Obj3
String name = "third";
int age = 5;
int size = 8;
If this is the linkedlist storing these three objects with the given fields, is there a way to search the linked list for the object with the name "second"?
You can search for an item in the list by iteration
// Iterate over each object within the list
for(YourClass obj : yourLinkedList) {
// Check if the object's name matches the criteria, in this case, the name
// of the object has to match "second"
if (obj.name.equals("second")) {
// If we are within this block, it means that we found the object that has
// its name set as "second".
return obj;
}
}
You could also make a method to make things more elegant
public YourClass findByName(String name) {
for(YourClass obj : yourLinkedList) {
if (obj.name.equals(name)) {
return obj;
}
}
return null;
}
And use it the following way
YourClass object = findByName("second");
The easiest way to do this would be to of course, iterate through each element in the collection, checking if it matched your filter condition, and selecting the matches found. However this gets tedious the more times you need to do it, and the more complex your filter condition is. I would recommend utilizing pre-existing libraries to get the task done efficiently. Here is an example using Google-Collections:
final List<SomeObj> listObjs = Arrays.asList(
new SomeObj("first", 2, 4), new SomeObj("second", 3, 6),
new SomeObj("third", 5, 8));
final Iterable<SomeObj> filtered = Iterables.filter(listObjs,
new Predicate<SomeObj>() {
#Override
public boolean apply(final SomeObj obj) {
return "second".equals(obj.getName());
}
});
for (final SomeObj obj : filtered) {
System.out.println(obj);
}
The code shown would select all objects in the list with a name property of "second". Obviously, the predicate doesn't have to be an anonymous inner class - if you needed to reuse it you would just break it out to a standalone class.
Here's another way to implement a Comparator (just in case it helps).
I find it's easier to understand if you implement the Comparator explicitly:
class PersonAgeComparator implements Comparator<Person> {
#Override
public int compare(Person p1, Person person2) {
return p1.getAge().compareTo(p2.getAge());
}
}
You might use the above like this:
Comparator ageComparator = new PersonAgeComparator();
List<Person> personList = // populate list somehow
Person fourYearOld = new Person();
fourYearOld.setAge(4);
for (Person p : personList) {
if (ageComparator.compare(fourYearOld, p) == 0) {
System.out.println(p.getName() + " is 4 years old");
}
}
This doesn't make much sense for this simple example.
It would be ideal if you had several complicated ways to compare people (by height, by adjusted income, by how many states they've lived in, etc...).
Take a look at the java.util.Comprator interface. You can write a method that iterates over a List and uses a comparator to find the one you are after.
Something like (not compiled):
for(final T value : list)
{
if(comparator.compare(value, desired) == 0)
{
// match
}
}
In your comparator you have it perform whatever comparison you want.
Here is a working example:
public class JavaApplication4
{
public static void main(String[] args)
{
final List<Data> list;
final List<Data> a;
final List<Data> b;
list = new ArrayList<Data>();
list.add(new Data("Foo", 1));
list.add(new Data("Bar", 10));
list.add(new Data("Car", 10));
a = find(list,
new Data("Bar", 0),
new Comparator<Data>()
{
#Override
public int compare(final Data o1,
final Data o2)
{
return (o1.name.compareTo(o2.name));
}
});
b = find(list,
new Data(null, 10),
new Comparator<Data>()
{
#Override
public int compare(final Data o1,
final Data o2)
{
return (o1.count - o2.count);
}
});
System.out.println(a.size());
System.out.println(b.size());
}
private static List<Data> find(final List<Data> list,
final Data desired,
final Comparator<Data> comprator)
{
final List<Data> results;
results = new ArrayList(list.size());
for(final Data data : list)
{
if(comprator.compare(desired, data) == 0)
{
results.add(data);
}
}
return (results);
}
private static class Data
{
private final String name;
private final int count;
Data(final String nm,
final int c)
{
name = nm;
count = c;
}
}
}
And here is a generic version of the find method. Using this method you would never have to write the find method again, using a method that embeds the logic for matching in the iteration code means that you would have to re-write the iteration logic for each new set of matching logic.
private static <T> List<T> find(final List<T> list,
final T desired,
final Comparator<T> comprator)
{
final List<T> results;
results = new ArrayList(list.size());
for(final T value : list)
{
if(comprator.compare(desired, value) == 0)
{
results.add(value);
}
}
return (results);
}
You can go through it and get it done or there's another way.
You need to override the equals method in your class (and the hashcode method as well).
After you override the equals to your desire, in this case to compare the names, create a new object with the same name and call the remove(Object o) method of the LinkedList and get the object.
You should note that with this approach you objects equality will be defined by name and that the entry will be removed from the LinkedList
Below is code for the Comparator, however after SortedMap.putAll(), the SortedMap has lesser number of Map Entries as compared to the source Map.
Could anyone please help?
Comparator<ArrayList> arrayListComparer = new Comparator<ArrayList>() {
#Override
public int compare(ArrayList arrA, ArrayList arrB) {
DateFormat formatter = new SimpleDateFormat("MMM-yyyy");
Date dateA = new Date();
Date dateB = new Date();
try {
dateA = formatter.parse(arrA.get(0).toString());
dateB = formatter.parse(arrB.get(0).toString());
} catch (ParseException ex) {
Logger.getLogger(ValueComparator.class.getName()).log(Level.SEVERE, null, ex);
}
if (dateA.before(dateB)) {
return 0;
} else if (dateA.after(dateB)) {
return 1;
} else {
return -1;
}
}
};
SortedMap sorted_map = new TreeMap(arrayListComparer);
sorted_map.putAll(map);
When you're using SortedMap or SortedSet your Comparator should return 0 only if two objects are equals. Because it treats key equality by this criteria.
See natural ordering: http://docs.oracle.com/javase/6/docs/api/java/lang/Comparable.html
For example, if one adds two keys a and b such that (!a.equals(b) &&
a.compareTo(b) == 0) to a sorted set that does not use an explicit
comparator, the second add operation returns false (and the size of
the sorted set does not increase) because a and b are equivalent from
the sorted set's perspective.
It would help if you could show us an example of your Map, but I'll risk an answer anyway.
What's probably causing this is that you have entries in your Map to which your compare method (in your comparator), returns 0, this means the keys are equal. If they are equal, then it's the same key... and you can't have duplicate keys in a Map.
Can you see the problem?
I'll put a little example on:
public static void main(String[] args) {
Map<Bla, String> map = new HashMap<Bla, String>();
map.put(new Bla(1,1), "bla1");
map.put(new Bla(1,2), "bla2");
map.put(new Bla(1,3), "bla3");
System.out.println(map.size());
TreeMap<Bla, String> treeMap = new TreeMap<Bla,String>(new Comparator<Bla>() {
#Override
public int compare(Bla bla, Bla bla1) {
return new Integer(bla.a).compareTo(bla1.a);
}
});
treeMap.putAll(map);
System.out.println(treeMap.size());
}
private static class Bla{
private Bla(int a, int b) {
this.a = a;
this.b = b;
}
public int a;
public int b;
}
This will output
3
1
Because my comparator says that all three keys are the same (only comparing the "a" value).