ClassCastException when using TreeMap - java

I have a TreeMap<Integer, TreeMap<int[][], Integer>> jungle. When I try to execute the statements
TreeMap<int[][], Integer> tempMap = new TreeMap();
int[][] matrix = {{1}};
tempMap.put(matrix, 4);
this last line gives me the
java.lang.ClassCastException: [[I cannot be cast to java.base/java.lang.Comparable at java.base/java.util.TreeMap.compare
exception. Am I not allowed to use an int[][] as a key in a treeMap?

The purpose of a TreeMap is to have an ordered collection
A Red-Black tree based NavigableMap implementation. The map is sorted according to the natural ordering of its keys, or by a Comparator provided at map creation time, depending on which constructor is used.
You must pass a Comparator that deals with int[][]; here an example with one that sorted based on the full sum of the array
class Custom2DArrayComparator implements Comparator<int[][]> {
private static int sum(int[][] v) {
return Arrays.stream(v).map(arr -> Arrays.stream(arr).sum())
.mapToInt(Integer::intValue).sum();
}
#Override
public int compare(int[][] o1, int[][] o2) {
return Integer.compare(sum(o1), sum(o2));
}
}
Use
public static void main(String[] args) {
TreeMap<int[][], Integer> tempMap = new TreeMap<>(new Custom2DArrayComparator());
int[][] matrix = {{1}};
tempMap.put(matrix, 4);
}
You can use the anonymous class to avoid creating a one outside
public static void main(String[] args) {
TreeMap<int[][], Integer> tempMap = new TreeMap<>(new Comparator<>() {
#Override
public int compare(int[][] o1, int[][] o2) {
return Integer.compare(sum(o1), sum(o2));
}
int sum(int[][] v) {
return Arrays.stream(v).map(arr -> Arrays.stream(arr).sum())
.mapToInt(Integer::intValue).sum();
}
});
int[][] matrix = {{1}};
tempMap.put(matrix, 4);
}

You'll need to implement your own logic in order for your key to be Comparable, as Johannes' comment points out. You could create a class that implements Comparator and pass it as argument at the initialization of the TreeMap.
Note that the default/overriden compare method would also be valid, as arrays are just Objects.
You could even implement all the logics for non-comparable Object keys you wish to insert in different Maps in a single class:
public class NonCompObjectKeyComparator implements Comparator<Object>
{
#Override
public int compare(Object o1, Object o2)
{
if (o1 instanceof int[][])
{
//((int[][])o1),((int[][])o2)//
}
else if (o1 instanceof String[])
{
//((String[])o1),((String[])o2)//
}
else if (o1 instanceof <OtherNonComparableObjType>)
{
//...
}
//...
return 0;
}
}
And then you got your überComparator there:
Comparator<Object> maCompa = new NonCompObjectKeyComparator();
TreeMap<int[][], Integer> tempMap = new TreeMap(maCompa);
TreeMap<String[], String> sArrayMap = new TreeMap(maCompa);
int[][] matrix = {{1}};
tempMap.put(matrix, 4);
String[] sKey = {"a"};
sArrayMap.put(sKey, "anotherWierdMap");
In order to create an specific int[][] comparator, just:
public class CustomKeyComparator implements Comparator<int[][]> {
public int compare(int[][] a1, int[][] a2) {
//your logic here
return 0;
}
}
and
TreeMap<int[][], Integer> tempMap = new TreeMap(new CustomKeyComparator());
int[][] matrix = {{1}};
tempMap.put(matrix, 4);

Related

How to iterate over a list of Map of Strings and add to another list if map contains matching elements on a key value?

I have a List of Map<String, String> that I want to iterate over and find the common elements inside the map of string and add to another map.
I am confused what should go inside the if loop to get my expected output. I am looking for comparator type call but I couldn't find that anywhere.
for (int i = 0; i < list.size() - 1; i++) {
if (list.get(i).get("Journal ID").equals(list.get(i+1).get("Journal ID")))
// ???
}
}
I was using this method to sort list of Maps. I am expecting some thing like this
public Comparator<Map<String, String>> mapComparator = new Comparator<>() {
public int compare(Map<String, String> m1, Map<String, String> m2) {
return m1.get("Journal ID").compareTo(m2.get("Journal ID"));
}
}
Collections.sort(list, mapComparator);
// input and the expected output
my List = [{Journal ID=123, featureID=312},{Journal ID=123, featureID=313},{Journal ID=134,
featureID=314},{Journal ID=123, featureID=1255}]
expected output is one that matching the "Journal ID" [{Journal ID=123, featureID=312},
{ Journal ID=123, featureID=313},{Journal ID=123, featureID=1255}].
One approach is to construct a second map which will aggregate all maps.
It will reflect all keys form all maps. As value will be a list of each key value with counters. Implementation can be enhanced also, but the main aspect is how to proceed. Having the aggregate map, then is straight forward to transform in what ever structure needed.
public class TestEqMap {
public static void main(String[] args)
{
Map<String, String> m1 = Map.of("a","a1","b","b1");
Map<String, String> m2 = Map.of("a","a1","b","b2");
Map<String, String> m3 = Map.of("a","a2","b","b2");
Map<String, String> m4 = Map.of("a","a1","b","b2");
Map<String, String> m5 = Map.of("a","a3","b","b2");
AggMap amap = new AggMap();
amap.addMap(m1);
amap.addMap(m2);
amap.addMap(m3);
amap.addMap(m4);
amap.addMap(m5);
amap.map.forEach((k,v)->System.out.println("key="+k+"\n"+v));
}
static class AggMap
{
public Map<String, ListItem> map = new HashMap<String,ListItem>();
public void addMap(Map<String,String> m)
{
for(String key: m.keySet())
{
if(this.map.containsKey(key))
{
this.map.get(key).addItem(m.get(key));
}
else
{
ListItem li = new ListItem();
li.addItem(m.get(key));
this.map.put(key, li);
}
}
}
}
static class ListItem
{
public List<Item> li = new ArrayList<Item>();
public ListItem() {};
public void addItem(String str)
{
for(Item i: this.li)
{
if(i.val.equals(str))
{
i.count++;
return;
}
}
this.li.add(new Item(str));
}
public String toString()
{
StringBuffer sb= new StringBuffer();
this.li.forEach(i->sb.append(i+"\n"));
return sb.toString();
}
}
static class Item
{
public String val;
public int count=1;
public Item(String val)
{
this.val = val;
}
public String toString()
{
return "val="+this.val+" count="+this.count;
}
}
}
Output:
key=a
val=a1 count=3
val=a2 count=1
val=a3 count=1
key=b
val=b1 count=1
val=b2 count=4

How to write a custom Comparator for TreeMap in Java?

I want to store key-value pairs in TreeMap and sort the entries based on the value of Key as per following logic:
Sort by the length of the key. If the length of two keys is same then sort them alphabetically. Example, for the following key-value pairs.
IBARAKI MitoCity
TOCHIGI UtunomiyaCity
GUNMA MaehashiCity
SAITAMA SaitamaCity
CHIBA ChibaCity
TOKYO Sinjyuku
KANAGAWA YokohamaCity
The expected output is like this.
CHIBA : ChibaCity
GUNMA : MaehashiCity
TOKYO : Sinjyuku
IBARAKI : MitoCity
SAITAMA : SaitamaCity
TOCHIGI : UtunomiyaCity
KANAGAWA : YokohamaCity
You can pass the Comparator as a parameter to Map's constructor.
According to documentation it is used for Keys only:
/**
* Constructs a new, empty tree map, ordered according to the given
* comparator. All keys inserted into the map must be <em>mutually
* comparable</em> by the given comparator: {#code comparator.compare(k1,
* k2)} must not throw a {#code ClassCastException} for any keys
* {#code k1} and {#code k2} in the map. If the user attempts to put
* a key into the map that violates this constraint, the {#code put(Object
* key, Object value)} call will throw a
* {#code ClassCastException}.
*
* #param comparator the comparator that will be used to order this map.
* If {#code null}, the {#linkplain Comparable natural
* ordering} of the keys will be used.
*/
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
In this way you can pass comparator by length of your key like this:
new TreeMap<>(Comparator.comparingInt(String::length).thenComparing(Comparator.naturalOrder()))
You need to write your own comparator for this and use it in TreeMap, e.g.:
public class StringComparator implements Comparator<String> {
#Override
public int compare(String s1, String s2) {
return s1.length() == s2.length() ? s1.compareTo(s2) : s1.length() - s2.length();
}
public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {
Map<String, String> map = new TreeMap<>(new StringComparator());
map.put("IBARAKI", "MitoCity");
map.put("TOCHIGI", "UtunomiyaCity");
map.put("GUNMA", "MaehashiCity");
map.put("SAITAMA", "SaitamaCity");
map.put("CHIBA", "ChibaCity");
map.put("TOKYO", "Sinjyuku");
map.put("KANAGAWA", "YokohamaCity");
System.out.println(map);
}
}
This does not handle null values but you can add the handling if you are expecting null values in your use case.
You should create a unique comparator for comparing the keys of the map. But because you want to print the values too, you should compare the whole entrysets instead:
Comparator<Map.Entry<String, String>> c = new Comparator<Map.Entry<String, String>>() {
#Override
public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
int q = Integer.compare(o1.getKey().length(), o2.getKey().length());
return q != 0 ? q : o1.getKey().compareTo(o2.getKey());
}
};
Then you can use this comparator in sorting:
map.entrySet().stream().sorted(c).forEach(System.out::println);
You can do this as follows.
public static void main(String[] args) {
Map<String, String> map = new TreeMap<>(new CustomSortComparator());
map.put("IBARAKI", "MitoCity");
map.put("TOCHIGI", "UtunomiyaCity");
map.put("GUNMA", "MaehashiCity");
map.put("SAITAMA", "SaitamaCity");
map.put("CHIBA", "ChibaCity");
map.put("TOKYO", "Sinjyuku");
map.put("KANAGAWA", "YokohamaCity");
System.out.println(map);
}
The CustomSortComparator has been defined as follows.
public class CustomSortComparator implements Comparator<String> {
#Override
public int compare(String o1, String o2) {
if (o1.length() > o2.length()) {
return 1;
}
if (o1.length() < o2.length()) {
return -1;
}
return returnCompareBytes(o1, o2);
}
private int returnCompareBytes(String key1, String key2) {
for (int i = 0; i < key1.length() - 1; i++) {
if (key1.charAt(i) > key2.charAt(i)) {
return 1;
}
if (key1.charAt(i) < key2.charAt(i)) {
return -1;
}
}
return 0;
}
}
Instead of converting Map into TreeMap directly you can use this method
public static Map toTreeMap(Map hashMap)
{
// Create a new TreeMap
Map treeMap = new TreeMap<>(new Comparator<Map.Entry<String, String>>(){
public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2 )
{
if (o1.getKey().length() > o2.getKey().length()) {
return 1;
}
if (o1.getKey().length() > o2.getKey().length()) {
return -1;
}
return o1.getKey().compareTo(o2.getKey());
}
});
for(Map.entry e : hashmap){
treeMap.put(e.getKey(),e.getValue);
}
return treeMap;
}
You can define the Comparator<String> you need in the constructor call to the TreeMap:
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
public class Main {
static final Map<String, String> map =
new TreeMap<String, String> (new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
int diff_length = o1.length() - o2.length();
if (diff_length != 0) return diff_length;
return o1.compareTo(o2);
}
});
public static final void main(String[] args) {
map.put("IBARAKI", "MitoCity");
map.put("TOCHIGI", "UtunomiyaCity");
map.put("GUNMA", "MaehashiCity");
map.put("SAITAMA", "SaitamaCity");
map.put("CHIBA", "ChibaCity");
map.put("TOKYO", "Sinjyuku");
map.put("KANAGAWA", "YokohamaCity");
System.out.println(map);
}
}

Sorting List Data as per an Enum

I have a List of String l=[mean,max,min,std,flag]
I have another enum
public enum ListOrder{
mean(0),
std(1),
max(2),
min(3),
flag(100);
private int value;
private ListOrder(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
My try :-
Collections.sort(l, new Comparator<ListOrder>() {
#Override
public int compare(ListOrder o1, ListOrder o2) {
// TODO Auto-generated method stub
return 0;
}
});
But this is not working as ListOrder is not the superClass of String. Can anyone please help me how can i do it.
If the List contains String you need to provide a Comparator<String> which will look the Enum to sort :
List<String> l = Arrays.asList("mean", "max", "min", "std", "flag");
Collections.sort(l, (o1, o2) -> Integer.compare(ListOrder.valueOf(o1).getValue(),
ListOrder.valueOf(o2).getValue()));
Workable Demo
If the List directly contains ListOrder you just need to tell to sort on value if the Enum elements are not provided in the order your need
List<ListOrder> l = Arrays.asList(ListOrder.mean, ListOrder.max, ListOrder.min, ListOrder.std, ListOrder.flag);
Collections.sort(l, Comparator.comparing(ListOrder::valueOf));
Workable Demo
If they are in the order you need, a simple Collections.sort(l); is sufficient
Enums is already Comparable with simple Collections.sort(l);
ArrayList<ListOrder> l = new ArrayList<ListOrder>();
l.add(ListOrder.mean);
l.add(ListOrder.max);
l.add(ListOrder.min);
l.add(ListOrder.std);
l.add(ListOrder.flag);
Collections.sort(l);
System.out.println(l.toString());
Output :
[mean, std, max, min, flag]
I could know what you wand.
#Test
public void test() {
List<String> l = Arrays.asList("mean", "max", "min", "std", "flag");
Collections.sort(l, new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
return ListOrder.valueOf(o1).getValue() - ListOrder.valueOf(o2).getValue();
}
});
for (String s : l) {
System.out.println(s);
}
}

Sort a TreeMap based on the number of values

I would like to redefine the Comparator as the size of the HashSet, but I get the error that the compare method must override or implement a super type method.
How can I create a TreeMap with Hashset size comparation?
private Map<Integer, HashSet<Integer>> nodes = new TreeMap<>(
new Comparator(){
#Override
public int compare(HashSet<Integer> o1 , HashSet<Integer> o2) {
return (o1.size()).compareTo(o2.size());
//o2.size().compareTo(o1.size());
}
});
You can use Comparator and write compare code to sort values based on HashSet size as below:
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.TreeMap;
public class Solution {
public static void main(String[] args) {
Map<String, HashSet<Integer>> map = new HashMap<String, HashSet<Integer>>();
HashSet<Integer> aSet = new HashSet<>();
aSet.add(1);
aSet.add(2);
aSet.add(3);
aSet.add(4);
aSet.add(5);
map.put("a", aSet);
HashSet<Integer> bSet = new HashSet<>();
bSet.add(1);
bSet.add(2);
bSet.add(3);
bSet.add(4);
map.put("b", bSet);
HashSet<Integer> cSet = new HashSet<>();
cSet.add(1);
cSet.add(2);
cSet.add(3);
cSet.add(4);
cSet.add(5);
cSet.add(6);
cSet.add(7);
map.put("c", cSet);
System.out.println(map);
Map sortedMap = sortByValue(map);
System.out.println(sortedMap);
}
public static Map sortByValue(Map unsortedMap) {
Map sortedMap = new TreeMap(new ValueComparator(unsortedMap));
sortedMap.putAll(unsortedMap);
return sortedMap;
}
}
class ValueComparator implements Comparator {
Map map;
public ValueComparator(Map map) {
this.map = map;
}
public int compare(Object keyA, Object keyB) {
HashSet<Integer> valueA = (HashSet<Integer>) map.get(keyA);
HashSet<Integer> valueB = (HashSet<Integer>) map.get(keyB);
Integer valASize = valueA.size();
Integer valBSize = valueB.size();
return valASize.compareTo(valBSize);
}
}

Using Super Class and Extend From It

I want to sort some team base on their points (win: 3 points, draw: 1 points and defeat: 0 points). I have an array which contains some teams' names and their results, I have extracted their scores and then sorted them descending. Here is the code I have used:
import java.util.*;
import java.util.Map.Entry;
public class SCAN{
public static void main(String[] args) {
Map<String, Integer> points = new HashMap<>();
String[] challenges = new String[]{
"Team(A) 2 - 1 Team(C)",
"Team(D) 3 - 1 Team(A)",
"Team(B) 3 - 3 Team(D)",
"Team(C) 0 - 4 Team(B)",
"Team(B) 2 - 0 Team(A)"
};
calcualte(challenges, points);
List<Entry<String, Integer>> entries = new ArrayList<Entry<String, Integer>>(
points.entrySet());
Collections.sort(entries, new Comparator<Entry<String, Integer>>() {
public int compare(Entry<String, Integer> e1,
Entry<String, Integer> e2) {
return e2.getValue().compareTo(e1.getValue()); // Sorts
// descending.
}
});
Map<String, Integer> orderedMap = new LinkedHashMap<String, Integer>();
for (Entry<String, Integer> entry : entries) {
orderedMap.put(entry.getKey(), entry.getValue());
}
for (Entry<String, Integer> element : entries) {
System.out.println(element);
}
}
private static void calcualte(String[] challenges, Map<String, Integer> points) {
List<String> challengeList = new ArrayList<>();
for (String str : challenges) {
String[] bits = str.trim().split(" ");
String firstTeam = bits[0];
String lastTeam = bits[bits.length - 1];
if (!challengeList.contains(firstTeam)) {
challengeList.add(firstTeam);
}
if (!challengeList.contains(lastTeam)) {
challengeList.add(lastTeam);
}
int firstScore = Integer.parseInt(bits[1]);
int lastScore = Integer.parseInt(bits[3]);
if (firstScore > lastScore) {
insert(3, points, firstTeam);
insert(0, points, lastTeam);
} else if (firstScore < lastScore) {
insert(3, points, lastTeam);
insert(0, points, firstTeam);
} else {
insert(1, points, firstTeam);
insert(1, points, lastTeam);
}
{
}
}
}
private static void insert(int i, Map<String, Integer> points, String team) {
int score = points.containsKey(team) ? points.get(team) : 0;
score += i;
points.put(team, score);
}
}
I know this, but I should do it by using a superclass So I need a subclass which its name is Plays and should be extended from a super class which its name is Playclass . The Playclass should not be changed. This class needs three abstracted method which should be overridden, they are won(),drawn() and defeated(). The other method which should to be overridden, is Print() which shows each team's information, like bellow:
public abstract class Playclass {
private String name;
Playclass(String name){
this.name=name;
}
public String getName() {
return name;
}
public abstract void print();
public abstract void won();
public abstract void drawn();
public abstract void defeated();
}
The main class name could be Playleage and it should contain the String[] challenges = new String[]{…} which I showed in the first code. I know how to sort them base on their scores (like the first code) but I don’t know how to use the classes which I said. May someone help me? thanks

Categories