I have this following test code:
public static final String[] list = {
"apple","ball","cat","dog","egg","fan","girl","hat","igloo","jerk"
};
...
HashMap<DoubleKey<Integer, Integer>, String> hm = new HashMap<DoubleKey<Integer, Integer>, String>();
Set<DoubleKey<Integer, Integer>> s = new TreeSet<DoubleKey<Integer, Integer>>();
Random g = new Random();
for(int i=0; i<10; i++){
int first = g.nextInt(9999) + 1000;
int second = g.nextInt(9999) + 1000;
DoubleKey<Integer, Integer> k1 = new DoubleKey<Integer, Integer>(first, second);
DoubleKey<Integer, Integer> k2 = new DoubleKey<Integer, Integer>(first, second);
s.add(k1);
hm.put(k2, list[i]);
}
Set<DoubleKey<Integer, Integer>> ts = hm.keySet();
Iterator<DoubleKey<Integer, Integer>> itr = ts.iterator();
while(itr.hasNext()){
DoubleKey<Integer, Integer> k = itr.next();
System.out.println(k.getFirstKey().toString() + " + " + k.getSecondKey().toString() + " -> " + hm.get(k).toString());
}
System.out.println("----");
Iterator<DoubleKey<Integer, Integer>> sItr = s.iterator();
while(sItr.hasNext()){
DoubleKey<Integer, Integer> k = sItr.next();
String currStr = hm.get(k);
System.out.println(k.getFirstKey().toString() + " + " + k.getSecondKey().toString() + " -> " + currStr);
}
What I did is to create a Custom Generic Class DoubleKey<K, J> to contain a key having two parts. As you can see, the Set s and the keys of HashMap hm are have the same components, but was instantiated differently (k1 = k2). When I try to get a value using the keys on s to hm, it returns null, though at the first printing it shows the correct mapping.
Sample Output:
3922 + 2544 -> girl
9267 + 3750 -> hat
3107 + 10929 -> apple
5162 + 8834 -> fan
8786 + 1125 -> cat
10650 + 4078 -> egg
3808 + 7363 -> jerk
1364 + 7657 -> dog
1364 + 4412 -> ball
1583 + 1460 -> igloo
----
10650 + 4078 -> null
1364 + 4412 -> null
1364 + 7657 -> null
1583 + 1460 -> null
3107 + 10929 -> null
3808 + 7363 -> null
3922 + 2544 -> null
5162 + 8834 -> null
8786 + 1125 -> null
9267 + 3750 -> null
This is my DoubleKey implemention:
public class DoubleKey<K extends Comparable<K>,J extends Comparable<J>> implements Comparable<DoubleKey<K,J>>{
private K key1;
private J key2;
public DoubleKey(K key1, J key2){
this.key1 = key1;
this.key2 = key2;
}
public K getFirstKey(){
return this.key1;
}
public J getSecondKey(){
return this.key2;
}
// need for Comparable interface
public int compareTo(DoubleKey<K,J> aThat){
// NOTE: check for nulls
return (this.key1.toString() + this.key2.toString()).compareTo(aThat.key1.toString() + aThat.key2.toString());
}
public boolean equals(DoubleKey<K,J> aThat){
return (this.key1.toString() + this.key2.toString()).equals(aThat.key1.toString() + aThat.key2.toString());
}
}
How did it happened? Can two objecst (in this case from a custom generic) be different eve3n if they have instantiated with 2 same values? How can I correct this? I hope someone can help me here. Thanks!
Additionally to .hashCode(), you should have an implementation of equals(Object), not (only) equals(DoubleKey<...>), since otherwise you'll have two independent methods here (and only the first one is actually called by the HashMap). Here is a proposal:
public boolean equals(Object other) {
if(this == other)
return true;
if(!(other instanceof DoubleKey))
return false;
DoubleKey that = (DoubleKey)other;
return (this.key1 == null ? that.key1 == null : this.key1.equals(that.key1)) &&
(this.key2 == null ? that.key2 == null : this.key2.equals(that.key2));
}
The hashCode method should be made to fit this, too, for example like this:
public int hashCode() {
return key1.hashCode() * 3 + key2.hashCode() * 5;
}
Your key1.toString()+key2.toString() comparison is a bit dangerous, as it lets (1, 21).equals((12,1)) be true, which is usually not intended. The same is true for your compareTo method - compare the components using their compareTo method, not the concatenated String.
Learn this lesson now: If you override the equals method (as you have done), then you MUST override the hashcode method too. That method is used for various things, including looking up items in HashMaps.
Where is DoubleKey class's hashCode method override? I don't think that it will work as a proper key unless you implement this because otherwise your two objects will be considered different.
Related
I have a list of java objects, and I want to use stream to filter them at runtime. However, the variable I want to filter on is only known at runtime.
For eg, the user says I want a list of all cats whose fur length is longer than 3cm, I should be able to do
cats.stream().filter(cat -> cat.getFurLength() > 3).collect(Collectors.toList());
The getFurLength() getter however should be dynamically invoked - if the user instead wants to filter by eye colour then I should be able to call
cats.stream().filter(cat -> cat.getEyeColour() == Colour.BLUE).collect(Collectors.toList());
How do I achieve this without writing all possible filters beforehand?
Ideally the user should send something like:
{
eyeColour:{
operator: "equal_to",
value: "BLUE"
},
furLength: {
operator: "greater_than",
value: 3
}
}
and the code should be able to generate the filters dynamically based on these criteria.
Assuming your Cat class follows JavaBean convention you could use java.beans.PropertyDescriptor to access getter Method based on property name.
This allows us to learn what type of value we are dealing with. If it is numeric we can handle greater_than and other operators, but if it is non-numeric we should handle only equals_to operator.
"Simplified" and very limited solution could look like:
NOTE:
- Solution doesn't support primitive numeric types like int. Use Integer, Double etc. instead.
- I am converting all numbers to BigDecimal and use compareTo to simplify numerical type comparison, if you get any bugs for big numbers or very precise ones feel free to replace it with proper type comparison).
- for equality check it compares string representation of objects (result of toString()), so for Color you can't use BLUE but your JSON would need to hold java.awt.Color[r=0,g=0,b=255])
class PredicatesUtil {
static <T> Predicate<T> filters(Class<T> clazz, String filtersJson) {
JSONObject jsonObject = new JSONObject(filtersJson);
List<Predicate<T>> predicateList = new ArrayList<>();
for (String property : jsonObject.keySet()) {
JSONObject filterSettings = jsonObject.getJSONObject(property);
try {
String operator = filterSettings.getString("operator");
String value = filterSettings.getString("value");
predicateList.add(propertyPredicate(clazz, property, operator, value));
} catch (IntrospectionException e) {
throw new RuntimeException(e);
}
}
return combinePredicatesUsingAND(predicateList);
}
static <T> Predicate<T> combinePredicatesUsingAND(List<Predicate<T>> predicateList) {
return t -> {
for (Predicate<T> pr : predicateList) {
if (!pr.test(t))
return false;
}
return true;
};
}
static <T> Predicate<T> propertyPredicate(Class<T> clazz, String property,
String operator, String value)
throws IntrospectionException {
final Method m = new PropertyDescriptor(property, clazz).getReadMethod();
final Class<?> returnType = m.getReturnType();
return obj -> {
try {
Object getterValue = m.invoke(obj);
if (Number.class.isAssignableFrom(returnType)) {
BigDecimal getValue = new BigDecimal(getterValue.toString());
BigDecimal numValue = new BigDecimal(value);
int compared = getValue.compareTo(numValue);
if (operator.equalsIgnoreCase("equal_to")) {
return compared == 0;
} else if (operator.equalsIgnoreCase("lesser_than")) {
return compared < 0;
} else if (operator.equalsIgnoreCase("greater_than")) {
return compared > 0;
} else {
throw new RuntimeException("not recognized operator for numeric type: " + operator);
}
} else {
//System.out.println("testing non-numeric, only euals_to");
if (operator.equalsIgnoreCase("equal_to")) {
return value.equalsIgnoreCase(getterValue.toString());
}
throw new RuntimeException("not recognized operator: " + operator);
}
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
};
}
}
which can be used like:
class Cat {
private Color eyeColour;
private Integer furLength;
Cat(Color eyeColor, Integer furLength) {
this.eyeColour = eyeColor;
this.furLength = furLength;
}
public Color getEyeColour() {
return eyeColour;
}
public Integer getFurLength() {
return furLength;
}
public void setEyeColour(Color eyeColour) {
this.eyeColour = eyeColour;
}
public void setFurLength(Integer furLength) {
this.furLength = furLength;
}
#Override
public String toString() {
return "Cat{" +
"eyeColor=" + eyeColour +
", furLength=" + furLength +
'}';
}
}
class CatsDemo {
public static void main(String[] args) {
String json =
"{\n" +
" eyeColour:{\n" +
" operator: \"equal_to\",\n" +
" value: \"java.awt.Color[r=0,g=0,b=255]\"\n" +
" },\n" +
" furLength: {\n" +
" operator: \"greater_than\",\n" +
" value: \"3\"\n" +
" }\n" +
"}";
List<Cat> cats = List.of(
new Cat(Color.blue, 1),
new Cat(Color.blue, 2),
new Cat(Color.blue, 3),
new Cat(Color.blue, 4),
new Cat(Color.blue, 5),
new Cat(Color.yellow, 1),
new Cat(Color.yellow, 2),
new Cat(Color.yellow, 3),
new Cat(Color.yellow, 4),
new Cat(Color.yellow, 5)
);
cats.stream()
.filter(PredicatesUtil.filters(Cat.class, json))
.forEach(System.out::println);
}
}
Output:
Cat{eyeColor=java.awt.Color[r=0,g=0,b=255], furLength=4}
Cat{eyeColor=java.awt.Color[r=0,g=0,b=255], furLength=5}
Make it reusable with a function.
List<Cat> filterCats(cats, Predicate<Cat> filter) {
return cats.stream().filter(filter).collect(Collectors.toList());
}
And then use it with:
filterCats(cats, cat -> cat.getEyeColour() == Colour.BLUE)
Or,
filterCats(cats, cat -> cat.getFurLength() > 3)
For what it's worth: Apache Commons BeanUtils library is specialized in accessing bean properties in a dynamic way.
See BeanPropertyValueEqualsPredicate for an understanding. This is only a solution for equality matches.
the output should be as below:
foo (A, T1, T2) -> void
bar (A, T1, T2, T3) -> int
doo () -> double
public static class A {
void foo(int T1, double T2) { }
int bar(int T1, double T2, char T3) { return 1; }
static double doo() { return 1; }
}
static void displayMethodInfo(Object obj)
{
Class<?> a = obj.getClass();
Method[] methods = a.getDeclaredMethods();
for (Method y : methods) //print methods
{
System.out.print(y.getName() + "(" ); // + y.getDeclaringClass().getSimpleName());
Type[] types = y.getGenericParameterTypes(); //get parameter types
if (!(Modifier.isStatic((y.getModifiers()))))
{
//non-static method, output this class namr as the 1st argument
System.out.print(y.getName()); //display
if (types.length > 0)
{
//put a comma and space
System.out.print(", ");
}
}
for (Type z : types)
System.out.print(", " + z.toString());
System.out.println( ") " + " -> " + y.getGenericReturnType().toString()); //*/
/*
//print parameter of the method
int i = 0;
for (; i < types.length-1; i++)
{
System.out.print(removeClassFromName (types[i].toString()) + ", ");
}
if (types.length > 0) //print last parameter
{
System.out.print(removeClassFromName(types[i].toString()));
//print return type
System.out.println( " ) " + " -> " + removeClassFromName(y.getGenericReturnType().toString()));
} */
}
}
with my code after I run the code, it outputs the code as below, it does not print out the type correctly. how should I fixed and have that output correctly?
bar(int, double, char) -> int
doo() -> double
foo(int, double) -> void
And every time when I recompiled and run it, the output is having the different order.
#Zoe as #Sami Sarraj said, your code is doing what is supposed to. About the order, it is not possible to get the order it was wrote by the getDeclaredMethods() call, as you can see here.
About getting the parameters names you can find a hint here.
I need to compare for a single hashmap two by two successive values like in the example below:
key is: Nature ---- Value is: 2
key is: duck ---- Value is: 3
key is: sheep ---- Value is: 3
key is: WoodTable ---- Value is: 4
key is: PVCdoor ---- Value is: 4
What I'm asking for, is how can I compare :
the value of Nature with the value of duck
the value of duck of the value of sheep
the value of sheep with the value of woodTable
the value of woodTable with the value of PVCdoor
.... etc
I tried but I can't get the result I need. Please if you have any idea I need your help;
This is the function I use but the result isn't at all what I need as an output.
Thank you
public Map<String, Integer> setCoefffils(Map<String, Integer> map){
Map.Entry<String,Integer> entry=map.entrySet().iterator().next();
this.listCoeffConceptfilsfinal.put(entry.getKey(), coeffFils);
Set<Entry<String, Integer>> setHm = map.entrySet();
java.util.Iterator<Entry<String, Integer>> it = setHm.iterator();
Entry<String, Integer> e = it.next();
for( Entry<String, Integer> ee : setHm){
// Entry<String, Integer> eeee = it.next();
// for( Entry<String, Integer> eeee : setHm){
System.out.println("key current is: "+ee.getKey() + " ---- Value is: " + ee.getValue());
System.out.println("key following is: "+e.getKey() + " ---- Value is: " + e.getValue());
if(ee.getValue().equals(e.getValue()))
System.out.println(""+ee.getValue() + " et " + e.getValue()+" sont égaux ");
else
System.out.println(" ne sont pas égaux ");
// }
return this.listCoeffConceptfilsfinal;
}
One solution is to store all the keys in a list, then access them one after the other.
public static void foo(Map<String, Integer> map) {
Set<String> keySet = map.keySet();
String lastKey = null;
for (String key : keySet) {
if (null == lastKey) {
lastKey = key;
continue;
}
if (map.get(key).equals(map.get(lastKey))) {
System.out.println("Les valeurs associées aux clés " + lastKey + " et " + key + " sont égales.");
} else {
System.out.println("Les valeurs associées aux clés " + lastKey + " et " + key + " sont différentes.");
}
lastKey = key;
}
}
But be aware : Maps doesn't always guarantee that the keys stay in insertion order. Thus, your comparison could be false. If you want to conserve insertion order, you have to use a LinkedHashMap.
With key order
CODE:
import java.util.TreeMap;
import java.util.Map;
import java.util.Iterator;
/**
* Write a description of class sumAndMax here.
*
* #author (your name)
* #version (a version number or a date)
*/
public class compareMap
{
public static void main(String[] args){
Map<String, Integer> map = initializeMap();
compareMaps(map);
}
private static void compareMaps(
Map<String, Integer> map)
{
Iterator<Map.Entry<String, Integer>> it = map.entrySet().iterator();
String leftKey = "";
String rightKey = "";
int leftValue = -1;
int rightValue = -1;
while (it.hasNext()) {
// Get values
Map.Entry<String, Integer> pair = it.next();
leftKey = rightKey;
leftValue = rightValue;
rightKey = pair.getKey();
rightValue = pair.getValue();
if(!leftKey.equals("")){
// Compare keys
System.out.println("Comparing key "+leftKey+" with key "+rightKey);
System.out.println("Result: "+leftKey.equals(rightKey));
// Compare values
System.out.println("Comparing value "+leftValue+" with value "+rightValue);
System.out.println("Result: "+(leftValue==rightValue));
}
}
}
private static Map<String, Integer> initializeMap(){
// Use Tree map for have a key ordered map !!!
Map<String, Integer> map = new TreeMap<>();
map.put("keyA",3);
map.put("keyB",4);
map.put("keyC",8);
map.put("keyD",8);
map.put("keyE",89);
map.put("keyF",4);
map.put("keyG",4);
return map;
}
}
RESULT:
Comparing key keyA with key keyB
Result: false
Comparing value 3 with value 4
Result: false
Comparing key keyB with key keyC
Result: false
Comparing value 4 with value 8
Result: false
Comparing key keyC with key keyD
Result: false
Comparing value 8 with value 8
Result: true
Comparing key keyD with key keyE
Result: false
Comparing value 8 with value 89
Result: false
Comparing key keyE with key keyF
Result: false
Comparing value 89 with value 4
Result: false
Comparing key keyF with key keyG
Result: false
Comparing value 4 with value 4
Result: true
I am creating a table from ajax and the getting of values using a while loop:
while (rstdb.next()) {
rstdb.getInt(1)+"~"+ rstdb.getInt(2);
}
In my while loop my rstdb.getInt(1) will be 2,2,2,2,2,3,3...... and second values rstdb.getInt(2) are 10,20,30,40,50,10,20,.....
I need to sum up the values specific to 2 and values specific to 3 seperate.
ie,
It means 10+20+30+40+50 =150 for 2 and 10+20 =30 for 3.
It may contain single values also for example it may have 4,5,5,6,7,7....
How can I do that?
I need something like:
while (rstdb.next()) {
rstdb.getInt(1)+"~"+ rstdb.getInt(2)+"~"+sum;
}
The variable sum should contain the sum up value.
Use map for this. You can have a map which should be mapping the specific number with sum of it's corresponding value.
int c1, c2;
Map<Integer, Integer> sum = new HashMap<>();
while (rstdb.next()) {
c1 = rstdb.getInt(1);
c2 = rstdb.getInt(2);
if(sum.containsKey(c1)) {
sum.put(c1, sum.get(c1) + c2);
// ^ will return current sum of second column
} else {
sum.put(c1, c2);
}
rstdb.getInt(1)+"~"+ rstdb.getInt(2)+"~"+sum.get(c1);
}
You can use an integer to integer map:
Map<Integer, Integer> integerMap = new HashMap<>();
while (rstdb.next()) {
int column1 = rstdb.getInt(1);
int column2 = rstdb.getInt(2);
if (integerMap.containsKey(column1)) {
int currentSum = integerMap.get(column1);
integerMap.put(column1, currentSum + column2);
} else {
integerMap.put(column1, column2);
}
}
Edit: to print out the map, you can use loop through the entrySet of the map:
for (Map.Entry entry : integerMap.entrySet()) {
System.out.println(entry.getKey() + " : " + entry.getValue());
}
I am having trouble finding the most and least used String in an ArrayList. The program should go through a file of Strings and count how many multiple strings there are in the list. Then print the least and most used name in the list. The ArrayList Part is finished. It is just finding the most and least common name I am having trouble with. I have no idea how to even start with it. This is what I have found online but it is not working.
Map<String, Integer> dogNames = new HashMap<>();
for (Dog dog : dogs) {
Integer value = dogNames.get(dog);
if (value == null) {
value = 0;
}
value++;
dogNames.put(dog.getName(), value);
}
int leastCommon = Integer.MAX_VALUE;
String leastCommonName = null;
for (String name : dogNames.keySet()) {
int value = dogNames.get(name);
if (value < leastCommon) {
leastCommon = value;
leastCommonName = name;
}
}
System.out.println("Least common (" + leastCommon + ") is " + leastCommonName);
The problem with your code seems to be in this line:
Integer value = dogNames.get(dog);
Your map holds dog names (String), but you are getting the entry for the Dog, which does not exist! Thus, value stays 0 even if you've seen that name before. If you fix this, you code should work.
Instead of your loop for searching the least common name, you could also define a custom Comparator based on the counts in the map and then use Collections.min and Collections.max:
Comparator<Dog> comp = new Comparator<Dog>() {
#Override
public int compare(Dog o1, Dog o2) {
return Integer.compare(dogNames.get(o1.getName()), dogNames.get(o2.getName()));
}
};
System.out.println("least " + Collections.min(dogs, comp));
System.out.println("most " + Collections.max(dogs, comp));
With Java 8, you can make it even shorter, using Comparator.comparing:
List<Dog> dogs = ...
Map<String, Integer> dogNames = new HashMap<>();
dogs.forEach(dog -> dogNames.put(dog.getName(), dogNames.getOrDefault(dog.getName(), 0) + 1));
Comparator<Dog> comp = Comparator.comparing(d -> dogNames.get(d.getName()));
System.out.println("least " + Collections.min(dogs, comp));
System.out.println("most " + Collections.max(dogs, comp));
Or even shorter, using Collections.frequency instead of building your own map, and using that to compare. Note, however, that this will be wasteful if the list is very long, since this will search the list each time anew instead of caching the counts in the map.
List<Dog> dogs = ...
Comparator<Dog> comp = Comparator.comparing(d -> Collections.frequency(dogs, d.getName()));
System.out.println("least " + Collections.min(dogs, comp));
System.out.println("most " + Collections.max(dogs, comp));
Your code should look something like this...
Map<String,int> frequencyOfDogNames = new HashMap<String,int>();
for(String dogName:dogNames) {
if(frequencyOfDogNames.contains(dogName)) {
continue;
}
frequencyOfDogNames.put(dogName, Collections.frequency(dogs, "dogName"));
}
This will give you the map of all the names with the occurrences.
Now we should loop thought the map to see which one are the max and min...
int leastCommon = Integer.MAX_VALUE;
int mostCommon = 0;
String leastCommonName, mostCommonName;
int occurrence;
for(String dogName: frequencyOfDogNames.keySet()) {
occurrence = frequencyOfDogNames.get(dogName);
if(leastCommon > occurrence){
leastCommon = occurrence;
leastCommonName = dogName;
}
if(mostCommon < occurrence){
mostCommon = occurrence;
mostCommonName = dogName;
}
}