How to count unique values ​in a map that are complex objects - java

I have the following sample structure:
class MyObject {
private String type;
private String level;
}
Map<String,List<MyObject>> map = new HashMap<>();
MyObject myObject1 = new MyObject();
myObject1.setType("x");
myObject1.setLevel("5");
MyObject myObject2 = new MyObject();
myObject2.setType("y");
myObject2.setLevel("5");
List<MyObject> list1 = new ArrayList<>();
list1.add(myObject1);
list1.add(myObject2);
map.put("1",list1);
MyObject myObject3 = new MyObject();
myObject3.setType("x");
myObject3.setLevel("4");
List<MyObject> list2 = new ArrayList<>();
list2.add(myObject3);
map.put("2",list2);
MyObject myObject4 = new MyObject();
myObject4.setType("x");
myObject4.setLevel("5");
MyObject myObject5 = new MyObject();
myObject5.setType("y");
myObject5.setLevel("5");
List<MyObject> list3 = new ArrayList<>();
list3.add(myObject4);
list3.add(myObject5);
map.put("3",list3);
...
Based on this map, I need to create an intermediate object or some structure where I will store information about the unique values ​​of the map. In the example above, key 1 and key 3 are the same value
so I need to store the information that the combination x = 5, y = 5 occurred twice in the map. The combination x = 4 appeared once in the map. There can be many combinations.
Any suggestions on how to do it the easiest way?

Since this looks like a homework question asking how to generally do the task I will not include code.
Think through what you have to do, write methods you'll need, implement them when you think you have all the pieces you need. Start with a stub for the method that does what you want.
The thing you can have duplicates of in the map (why are they in the map, no idea) are lists. Write a method that compares lists and returns whether they are the same.
To write that method you need a method that can compare MyObject. Best way would be to override equals() method.
Next, it'll be a question if order in the lists matters. If yes, than List equals method will work for you (read the javadoc to see exactly what it does). If not you'll need to write custom code to handle that, or sort the lists before comparison (which would involve writing a comparator for MyObject), or use a library that has that functionality (there should be something in Apache Commons).
Now that we have all that all we come back to the main method, use the ones we wrote appropriately, and all we need is do something with the results. Generally anything will do, a map with the list as key and amount of occurences as value will be simplest unless you have some more constraints or operations to do on the results.

Related

Handle differences between two arraylists

My question is that I am going to compare two arraylists in Java
e.g
String prop1 = "String"
String prop2 = "OtherString"
MyObject obj1 = new MyObject(prop1,prop2);
MyObject obj2 = new MyObject(prop1,prop2);
MyObject obj3= new MyObject(prop1,prop2);
ArrayList<MyObject> array1 = new Arraylist<>();
ArrayList<MyObject> array2 = new Arraylist<>();
//array 1 has 3 objects
array1.add(obj1);array1.add(obj2);array1.add(obj3);
//array 2 has 2 objects
array2.add(obj1);array2.add(obj2);
With a comparison method i know these arrays are different
(My method returns false if the arrays have the same elements even if they are not in the same order, and true if they have the same elements)
So, the method is going to return FALSE
My question is:
if(!methodToCompareArrays(array1,array2)){
//HOW TO GET THE DIFFERENT objects (IN THIS CASE, obj3 is the different object)
//this is the question :)
}else{
//If there is no difference, well, it doesn't matter too much
Notice that I'm going to have multiple objects into these arraylists, and also the method efficiency is important (not crucial, but important at least). I've seen the answers here But I'm not sure which one would be better or worst
Thanks in advance.
You should probably use java's set interfaces for this.
Now, one thing that's going to be important is having a good equals method on MyObject to be able to compare whether two MyObjects are the same.
Then you could use that documentation link above to check the intersection of two sets. If the items that are in both sets are the same number of items as in one set, then they're the same set (irrespective of order).
HashSet<MyObject> set1 = new HashSet<MyObject>(array1);
HashSet<MyObject> set2 = new HashSet<MyObject>(array2);
Set<MyObject> intersection = new HashSet<MyObject>(set1);
intersection.retainAll(set2);
if(intersection.size() == set2.size()) {
// They're the same.
} else {
HashSet<MyObject> itemsOnlyInSet1 = intersection;
HashSet<MyObject> itemsOnlyInSet2 = set2.retainAll(set1);
}
If the objects in these lists aren't important for you, you can do something like:
array1.removeAll(array2);
This will remove from array1, all the elements that exist in array2.
So if array1 = [obj1, obj2, obj3] and
array2 = [obj1, obj2]
After the removeAll:
array1 = [obj3] and
array2 = [obj1, obj2]
If you cannot remove the objects from either list then make a temp List and remove from there to get extra object.
I am not sure I understand what your question is but if you are trying to compare and sort list of objects, the best option is to look up TreeMaps from the Collections API. Try this:
Difference between HashMap, LinkedHashMap and TreeMap

Java removeAll() and Objects

I have two arrayLists that contain objects of my Class Report Object.
Report Object has couple of fields but most important are Tag(String) and Attr(String).
During the execution of the program two arrayLists are populated with that objects. These arrayLists represent old collection of the ReportObjects and the new one.
I want to know what objects were added to the new arrayList and what objects were removed from old ArrayList.
ReportObject obj1 = new ReportObject("Tag", "Attr", "XPath", "Parent", null);
ReportObject obj2 = new ReportObject("Tag2", "Attr", "XPath", "Parent", null);
ReportObject obj3 = new ReportObject("Tag", "Attr", "XPath", "Parent", null);
ArrayList<ReportObject> newList = new ArrayList<>();
ArrayList<ReportObject> oldList = new ArrayList<>();
ArrayList<ReportObject> added = new ArrayList<>();
ArrayList<ReportObject> removed = new ArrayList<>();
newList.add(obj1);
newList.add(obj2);
oldList.add(obj3);
added.addAll(newList);
added.removeAll(oldList);
Problem is I still have two elements in added ArrayList. That because obj1 and obj3 are different objects?
When I do two loops and iterate over them while checking if objects has same field values i still get same results.
So in added arrayList I should have only obj2 and in removed arrayList should be empty.
A String is an Object in Java. This will work. Java Collections make heavy use of equals to determine if an object is present in the collection. If you don't override equals, this will remove only objects that are present in both collections. That usually is the intended result though.
I guess you have an ID on the class you're making an object, then go like this:
for(ReportObject obj : xmlModel_New)
{
int i = 0;
for(ReportObject obj2 : xmlModel_Old){
if(obj.get(i).getId() != obj2.get(i).getId())
{
addedTags.add(obj);
}
i++;
}
}
Something like that.
You need to override the equals(Object obj) and hashCode() method in the ReportObject class. The default uses the memory location which I doubt is how you are trying to compare them. What makes them equal? One attribute or all the attributes? You have to define that. Your example would change to...
if(!obj.equals(obj2))
{
addedTags.add(obj);
}
I think this question is being downvoted probably because it has been answered before or maybe because it is kind of unclear what you are asking. Post more details and I will try to help.
Also, check out some of these links:
What issues should be considered when overriding equals and hashCode in Java?
How do I compare strings in Java?

Java - Initialize a HashMap of HashMaps

I am new to java and practicing by creating a simplistic NaiveBayes classifier. I am still new to object instantiation, and wonder what to do to initialize a HashMap of HashMaps. When inserting new observations into the classifier, I can create a new HashMap for an unseen feature name in a given class, but do I need to initialize?
import java.util.HashMap;
public class NaiveBayes {
private HashMap<String, Integer> class_counts;
private HashMap<String, HashMap<String, Integer>> class_feature_counts;
public NaiveBayes() {
class_counts = new HashMap<String, Integer>();
// do I need to initialize class_feature_counts?
}
public void insert() {
// todo
// I think I can create new hashmaps on the fly here for class_feature_counts
}
public String classify() {
// stub
return "";
}
// Naive Scoring:
// p( c | f_1, ... f_n) =~ p(c) * p(f_1|c) ... * p(f_n|c)
private double get_score(String category, HashMap features) {
// stub
return 0.0;
}
public static void main(String[] args) {
NaiveBayes bayes = new NaiveBayes();
// todo
}
}
Note this question is not specific to Naive Bayes classifiers, just thought I would provide some context.
Yes, you need to initialize it.
class_feature_counts = new HashMap<String, HashMap<String, Integer>>();
When you want to add a value to class_feature_counts, you need to instantiate it too:
HashMap<String, Integer> val = new HashMap<String, Integer>();
// Do what you want to do with val
class_feature_counts.put("myKey", val);
Recursive generic data structures, like maps of maps, while not an outright bad idea, are often indicative of something you could refactor - the inner map often could be a first order object (with named fields or an internal map), rather than simply a map. You'll still have to initialize these inner objects, but it often is a much cleaner, clearer way to develop.
For instance, if you have a Map<A,Map<B,C>> you're often really storing a map of A to Thing, but the way Thing is being stored is coincidentally a map. You'll often find it cleaner and easier to hide the fact that Thing is a map, and instead store a mapping of Map<A,Thing> where thing is defined as:
public class Thing {
// Map is guaranteed to be initialized if a Thing exists
private Map<B,C> data = new Map<B,C>();
// operations on data, like get and put
// now can have sanity checks you couldn't enforce when the map was public
}
Also, look into Guava's Mulitmap/Multiset utilities, they're very useful for cases like this, in particular they do the inner-object initializations automatically. Of note for your case, just about any time you implement Map<E, Integer> you really want a Guava Multiset. Cleaner and clearer.
You must create an object before using it via a reference variable. It doesn't matter how complex that object is. You aren't required to initialize it in the constructor, although that is the most common case. Depending on your needs, you might want to use "lazy initialization" instead.
Do not declare your variables with HashMap. It's too limiting.
Yes, you need to initialize class_feature_counts. You'll be adding entries to it, so it has to be a valid map. In fact, initialize both at declaration and not in the constructor since there is only one way for each to start. I hope you're using Java 7 by now; it's simpler this way.
private Map< String, Integer> classCounts = new HashMap<>();
private Map< String, Map< String, Integer>> classFeatureCounts = new HashMap<>();
The compiler will deduce the types from the <>. Also, I changed the variable names to standard Java camel-case style. Are classCounts and classFeatureCounts connected?

How to make a new list with a property of an object which is in another list

Imagine that I have a list of certain objects:
List<Student>
And I need to generate another list including the ids of Students in the above list:
List<Integer>
Avoiding using a loop, is it possible to achieve this by using apache collections or guava?
Which methods should be useful for my case?
Java 8 way of doing it:-
List<Integer> idList = students.stream().map(Student::getId).collect(Collectors.toList());
With Guava you can use Function like -
private enum StudentToId implements Function<Student, Integer> {
INSTANCE;
#Override
public Integer apply(Student input) {
return input.getId();
}
}
and you can use this function to convert List of students to ids like -
Lists.transform(studentList, StudentToId.INSTANCE);
Surely it will loop in order to extract all ids, but remember guava methods returns view and Function will only be applied when you try to iterate over the List<Integer>
If you don't iterate, it will never apply the loop.
Note: Remember this is the view and if you want to iterate multiple times it will be better to copy the content in some other List<Integer> like
ImmutableList.copyOf(Iterables.transform(students, StudentToId.INSTANCE));
Thanks to Premraj for the alternative cool option, upvoted.
I have used apache CollectionUtils and BeanUtils. Accordingly, I am satisfied with performance of the following code:
List<Long> idList = (List<Long>) CollectionUtils.collect(objectList,
new BeanToPropertyValueTransformer("id"));
It is worth mentioning that, I will compare the performance of guava (Premraj provided) and collectionUtils I used above, and decide the faster one.
Java 8 lambda expression solution:
List<Integer> iDList = students.stream().map((student) -> student.getId()).collect(Collectors.toList());
If someone get here after a few years:
List<String> stringProperty = (List<String>) CollectionUtils.collect(listOfBeans, TransformerUtils.invokerTransformer("getProperty"));
You can use Eclipse Collections for this purpose
Student first = new Student(1);
Student second = new Student(2);
Student third = new Student(3);
MutableList<Student> list = Lists.mutable.of(first, second, third);
List<Integer> result = list.collect(Student::getId);
System.out.println(result); // [1, 2, 3]
The accepted answer can be written in a further shorter form in JDK 16 which includes a toList() method directly on Stream instances.
Java 16 solution
List<Integer> idList = students.stream().map(Student::getId).toList();
It is Mathematically impossible to do this without a loop. In order to create a mapping, F, of a discrete set of values to another discrete set of values, F must operate on each element in the originating set. (A loop is required to do this, basically.)
That being said:
Why do you need a new list? You could be approaching whatever problem you are solving in the wrong way.
If you have a list of Student, then you are only a step or two away, when iterating through this list, from iterating over the I.D. numbers of the students.
for(Student s : list)
{
int current_id = s.getID();
// Do something with current_id
}
If you have a different sort of problem, then comment/update the question and we'll try to help you.

Compare new Integer Objects in ArrayList Question

I am storing Integer objects representing an index of objects I want to track. Later in my code I want to check to see if a particular object's index corresponds to one of those Integers I stored earlier. I am doing this by creating an ArrayList and creating a new Integer from the index of a for loop:
ArrayList<Integer> courseselectItems = new ArrayList();
//Find the course elements that are within a courseselect element and add their indicies to the ArrayList
for(int i=0; i<numberElementsInNodeList; i++) {
if (nodeList.item(i).getParentNode().getNodeName().equals("courseselect")) {
courseselectItems.add(new Integer(i));
}
}
I then want to check later if the ArrayList contains a particular index:
//Cycle through the namedNodeMap array to find each of the course codes
for(int i=0; i<numberElementsInNodeList; i++) {
if(!courseselectItems.contains(new Integer(i))) {
//Do Stuff
}
}
My question is, when I create a new Integer by using new Integer(i) will I be able to compare integers using ArrayList.contains()? That is to say, when I create a new object using new Integer(i), will that be the same as the previously created Integer object if the int value used to create them are the same?
I hope I didn't make this too unclear. Thanks for the help!
Yes, you can use List.contains() as that uses equals() and an Integer supports that when comparing to other Integers.
Also, because of auto-boxing you can simply write:
List<Integer> list = new ArrayList<Integer>();
...
if (list.contains(37)) { // auto-boxed to Integer
...
}
It's worth mentioning that:
List list = new ArrayList();
list.add(new Integer(37));
if (list.contains(new Long(37)) {
...
}
will always return false because an Integer is not a Long. This trips up most people at some point.
Lastly, try and make your variables that are Java Collections of the interface type not the concrete type so:
List<Integer> courseselectItems = new ArrayList();
not
ArrayList<Integer> courseselectItems = new ArrayList();
My question is, when I create a new Integer by using new Integer(i) will I be able to compare integers using ArrayList.contains()? That is to say, when I create a new object using new Integer(i), will that be the same as the previously created Integer object if the int value used to create them are the same?
The short answer is yes.
The long answer is ...
That is to say, when I create a new object using new Integer(i), will that be the same as the previously created Integer object if the int value used to create them are the same?
I assume you mean "... will that be the same instance as ..."? The answer to that is no - calling new will always create a distinct instance separate from the previous instance, even if the constructor parameters are identical.
However, despite having separate identity, these two objects will have equivalent value, i.e. calling .equals() between them will return true.
Collection.contains()
It turns out that having separate instances of equivalent value (.equals() returns true) is okay. The .contains() method is in the Collection interface. The Javadoc description for .contains() says:
http://java.sun.com/javase/6/docs/api/java/util/Collection.html#contains(java.lang.Object)
boolean contains(Object o)
Returns true if this collection
contains the specified element. More
formally, returns true if and only if
this collection contains at least one
element e such that (o==null ? e==null
: o.equals(e)).
Thus, it will do what you want.
Data Structure
You should also consider whether you have the right data structure.
Is the list solely about containment? is the order important? Do you care about duplicates? Since a list is order, using a list can imply that your code cares about ordering. Or that you need to maintain duplicates in the data structure.
However, if order is not important, if you don't want or won't have duplicates, and if you really only use this data structure to test whether contains a specific value, then you might want to consider whether you should be using a Set instead.
Short answer is yes, you should be able to do ArrayList.contains(new Integer(14)), for example, to see if 14 is in the list. The reason is that Integer overrides the equals method to compare itself correctly against other instances with the same value.
Yes it will, because List.contains() use the equals() method of the object to be compared. And Integer.equals() does compare the integer value.
As cletus and DJ mentioned, your approach will work.
I don't know the context of your code, but if you don't care about the particular indices, consider the following style also:
List<Node> courseSelectNodes = new ArrayList<Node>();
//Find the course elements that are within a courseselect element
//and add them to the ArrayList
for(Node node : numberElementsInNodeList) {
if (node.getParentNode().getNodeName().equals("courseselect")) {
courseSelectNodes.add(node);
}
}
// Do stuff with courseSelectNodes
for(Node node : courseSelectNodes) {
//Do Stuff
}
I'm putting my answer in the form of a (passing) test, as an example of how you might research this yourself. Not to discourage you from using SO - it's great - just to try to promote characterization tests.
import java.util.ArrayList;
import junit.framework.TestCase;
public class ContainsTest extends TestCase {
public void testContains() throws Exception {
ArrayList<Integer> list = new ArrayList<Integer>();
assertFalse(list.contains(new Integer(17)));
list.add(new Integer(17));
assertTrue(list.contains(new Integer(17)));
}
}
Yes, automatic boxing occurs but this results in a performance penalty. Its not clear from your example why you would want to solve the problem in this manner.
Also, because of boxing, creating the Integer class by hand is superfluous.

Categories

Resources