I've a Vector of objects, and have to search inside for a random attribute of those objects (For example, a Plane class, a Vector containing Plane; and I've to search sometimes for destination, and others to pilotName).
I know I can traverse the Vector using an Iterator, but I've got stuck at how do I change the comparison made between a String and the attribute on the object. I thought of using switch, but a another opinion would be cool.
Update 1:
The code I've written is something like this (Java n00b alert!):
public int search(String whatSearch, String query){
int place = -1;
boolean found = false;
for ( Iterator<Plane> iteraPlane = this.planes.iterator(); iteraPlane.hasNext() && found == false; ) {
Plane temp = (Plane) iteraPlane.next();
/* Here is where I have to search for one of many attributes (delimited by whatSearch */
}
return place;
}
Seems I've to stick to linear search (and that's a price I've able to pay). Anyway, I was thinking if Java had something like variable variable name (ouch!)
I assume that your problem is that you want to have a method that searches for a result based on some property of the collection type. Java is weak on this because it is best expressed in a language which has closures. What you need is something like:
public interface Predicate<T> {
public boolean evaluate(T t);
}
And then your search method looks like:
public static <T> T findFirst(List<T> l, Predicate<T> p) { //use List, not Vector
for (T t : l) { if (p.evaluate(t)) return t; }
return null;
}
Then anyone can use this general-purpose search method. For example, to search for an number in a vector of Integers:
List<Integer> is = ...
findFirst(is, new Predicate<Integer> {
public boolean evaluate(Integer i) { return i % 2 == 0; }
});
But you could implement the predicate in any way you want; for any arbitrary search
Use Collections.binarySearch and provide a Comparator.
EDIT: This assumes that the Vector is sorted. Otherwise, one has to do a linear search.
the equals() method is the best option. For these iterations you could do something like this:
for (Plane plane: planes) {
if ("JFK".equals(plane.getDestination())) {
// do your work in here;
}
}
or you could override the equals() method within Plane to see if the String passed in matches your destination (or pilot). this will allow you to use the indexOf(Object) and indexOf(Object, index) methods on Vector to return you the index(es) of the object(s). Once you have that, you could use Vector.get(index) to return to Object for you.
in Plane.java:
public boolean equals(Object o) {
return o.equals(getDestination()) ||
o.equals(getPilot()) ||
super.equals(o);
}
there is more work to be done with this option, as you will need to override hashCode() as well (see documentation).
See #oxbow_lakes above -- I think what you want isn't to pass a String as whatSearch, it's to pass a little snippet of code that knows how to get the property you're interested in. For a less general version:
public static interface PlaneMatcher {
boolean matches(Plane plane, String query);
}
public int search(PlaneMatcher matcher, String query){
int place = -1;
boolean found = false;
for ( Iterator<Plane> iteraPlane = this.planes.iterator(); iteraPlane.hasNext() && found == false; ) {
Plane temp = (Plane) iteraPlane.next();
if (matcher.matches(temp, query) {
found = true;
}
place++;
}
return place;
}
...
// example
int pilotNameIndex = search(new PlaneMatcher() {
boolean matches(Plane plane, String query) {
// note: assumes query non-null; you probably want to check that earlier
return query.equals(plane.getPilotName());
}
}, "Orville Wright");
(By the way, if it's the index you're interested in rather than the Plane itself, I wouldn't bother with an Iterator -- just use an old-fashioned for (int i = 0; i < planes.size(); i++) loop, and when you have a match, return i.)
Now, the tricky bit here is if what you have to search for is really identified by arbitrary strings at run-time. If that's the case, I can suggest two alternatives:
Don't store these values as object fields -- plane.pilotName, plane.destination -- at all. Just have a Map<String, String> (or better yet, a Map<Field, String> where Field is an Enum of all the valid fields) called something like plane.metadata.
Store them as object fields, but prepopulate a map from the field names to PlaneMatcher instances as described above.
For instance:
private static final Map<String, PlaneMatcher> MATCHERS = Collections.unmodifiableMap(new HashMap<String, PlaneMatcher>() {{
put("pilotName", new PlaneMatcher() {
boolean matches(Plane plane, String query) {
return query.equals(plane.getPilotName());
});
...
put("destination", new PlaneMatcher() {
boolean matches(Plane plane, String query) {
return query.equals(plane.getDestination());
});
}}
...
public int search(String whatSearch, String query){
PlaneMatcher matcher = MATCHERS.get(whatSearch);
int place = -1;
boolean found = false;
for ( Iterator<Plane> iteraPlane = this.planes.iterator(); iteraPlane.hasNext() && found == false; ) {
Plane temp = (Plane) iteraPlane.next();
if (matcher.matches(temp, query) {
found = true;
}
place++;
}
return place;
}
Oh, and you might be tempted to use reflection. Don't. :)
A simple way is to pass a comparison function to your search routine. Or, if you need more speed, use generics.
Related
This question already has answers here:
How to compare objects by multiple fields
(23 answers)
Closed 3 years ago.
I don't think that's the best way to word that title but I can't think of a better way to word it. Here's my problem: I have to write a method that compares in several different ways. If the last names are the same, I then need to compare by first name. If the first names are the same, then I need to sort by section. What would be the most effective way to sort a data structure in this hierarchy? Here's what I've currently got and I think I understand why it doesn't work but I can't come up with a different way to write this function:
//Student class structure, each field has a public get/set method
public class Student implements Comparable<Student>
{
private String fname;
private String lname;
private int section;
}
//My current compareTo method
#Override
public int compareTo(Student s)
{
/*
-compare by last name
-if the same, compare by first name
-if the same, compare by section
*/
String slast = s.getLastName();
if(lname.compareTo(slast) == 0)
{
String sfirst = s.getFirstName();
if(fname.compareTo(sfirst) == 0)
{
int sclass = s.getSection();
return Integer.compare(section, sclass);
}
else
{
return fname.compareTo(sfirst);
}
}
else
{
return lname.compareTo(slast);
}
}
You can create a Comparator for your Student class this way:
Comparator<Student> comparator = Comparator
.comparing(Student::getLastName)
.thenComparing(Student::getFirstName)
.thenComparing(Student::getSection);
And then use this comparator (instead of implementing Comparable interface) to sort a list with Student objects, or to create a TreeMap with these objects:
Collections.sort(listOfStudents, comparator);
TreeMap<Student> mapOfStudents = new TreeMap<>(comparator);
You don't have to use getters or setters if you're overriding compareTo. You can also forgo the else/return statements since they're terminal return statements, and just use return.
#Override
public int compareTo(Student s) {
if (lname.compareTo(s.lname) == 0) {
if (fname.compareTo(s.fname) == 0) {
return section.compareTo(s.section);
}
return fname.compareTo(s.fname);
}
return lname.compareTo(s.lname);
}
Your code looks correct to me.
What would be the most effective way to sort a data structure in this
hierarchy?
Well, it's worth mentioning that you are potentially doing the first two comparisons (first name and last name) multiple times
if(lname.compareTo(slast) == 0)
{
//...
}
else
{
return lname.compareTo(slast);
}
It should be fairly obvious that you are doing lname.compareTo(slast) twice. You can store the result in a variable instead.
int lastNameComparison = lname.compareTo(slast);
if(lastNameComparison == 0)
{
//...
}
else
{
return lastNameComparison;
}
It is a matter of style, but I would not bother to store the result of getters into variables. Just call them when you need them.
Combining both of the above points, you get:
int lastNameComparison = lname.compareTo(s.getLastName();
if (lastNameComparison == 0)
{
int firstNameComparison = fname.compareTo(s.getFirstName());
if (firstNameComparison == 0)
{
return Integer.compare(section, s.getSection());
}
else
{
return firstNameComparison;
}
}
else
{
return lastNameComparison;
}
The nesting is quite ugly and if we need to add another criteria, it would get even worse.
We can solve that by inverting the conditions and using multiple return statements.
int lastNameComparison = lname.compareTo(s.getLastName());
if (lastNameComparison != 0) return lastNameComparison;
// Last names must be equal
int firstNameComparison = fname.compareTo(s.getFirstName());
if (firstNameComparison != 0) return firstNameComparison;
// First names must be equal
return Integer.compare(section, s.getSection());
I would personally use the declarative style of writing this, but if this code is for an assignment, that is likely not what they are expecting.
I need help with removing just added element from the arrayList.
I have a private static ArrayList<Position> positions = new ArrayList<>() to which I'm adding objects of the class Position with parameters name, quantity, and price.
Than I have a method adding objects to the list, and in case if the same product is added for the second time, it is supposed to add the quantity to the first object of that name and remove that second one.
So far I have this method:
public void addPosition(Position p) {
for (Position poz: positions) {
if (poz.getname().equals(p.getname())) {
poz.setquantity(poz.getquantity() + p.getquantity());
}
} positions.add(p);
}
Adding quantities works just fine, but I've got problem with removing the element with recurring name.
Please help.
You shouldn't add duplicate items and then remove them. Just declare a method which handles adding items correctly; that is, it adds the item if it does not exist, and it updates the quantity if it does exist.
It should look like this:
public void addPosition(Position addition) {
//flag to track whether the new item exists in the list
boolean itemExists = false;
//go through the list looking for an item with the passed name to update
for (Position existing : positions) {
if (existing.getName().equals(addition.getName())) {
existing.setQuantity(existing.getQuantity() + addition.getQuantity());
itemExists = true;
}
}
//if no matching item was found, add the new item
if (!itemExists) {
positions.add(addition);
}
}
The above should work. If you care about performance, it might be better to use a HashMap so you can look up the Position by name instead of looping through the whole list each time.
If you are interested to know other data Structure , i want suggest you HashSet , by default it will not insert duplicates for primitive objects .
In your case the only thing you need to do to your Position class , is to add
equals and hashCode methods . As getters and setters Eclipse for example will create by him self .
hashCode()
As you know this method provides the has code of an object. Basically the default implementation of hashCode() provided by Object is derived by mapping the memory address to an integer value. If look into the source of Object class , you will find the following code for the hashCode. public native int hashCode(); It indicates that hashCode is the native implementation which provides the memory address to a certain extent. However it is possible to override the hashCode method in your implementation class.
equals()
This particular method is used to make equal comparison between two objects. There are two types of comparisons in Java. One is using “= =” operator and another is “equals()”. I hope that you know the difference between this two. More specifically the “.equals()” refers to equivalence relations. So in broad sense you say that two objects are equivalent they satisfy the “equals()” condition. If you look into the source code of Object class you will find the following code for the equals() method.
Here a complete working example ( you can modify your class following this cose)
import java.util.HashSet;
public class Zhashset{
private int num;
public Zhashset(){
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + num;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Zhashset other = (Zhashset) obj;
if (num != other.num)
return false;
return true;
}
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
HashSet<Zhashset> hs = new HashSet<Zhashset>();
hs.add(new Zhashset());
hs.add(new Zhashset());
for(Zhashset item : hs)
System.out.println(item.getNum());
}
}
Output will be : 0 written only once.
I'm trying to use a method to compare t2o different lists. Basically I want to pass two different lists to a method which will return true or false if the elements of one array list are contained in the other using .contains. Right now it only returns true - and I'm not sure why. I'd like it to return false. If someone could help me figure this out, that would be great.
public class ArrayListTest {
public static void main(String[] args) {
List<String> list1 = new ArrayList<String>();
List<String> list2 = new ArrayList<String>();
list1.add("cat");
list1.add("dog");
list1.add("zebra");
list1.add("lion");
list1.add("mouse");
//Test Values
//list2.add("cat");
list2.add("lizard");
boolean doesitcontain = contains(list1, list2);
System.out.println(doesitcontain);
}
public static boolean contains (List<String>list1, List<String>list2){
boolean yesitcontains;
for(int i = 0; i < list1.size(); i++){
if(list2.contains(list1.get(i))){
System.out.println("Duplicate: "+list1.get(i));
yesitcontains = true;
System.out.println(yesitcontains);
}else{
yesitcontains = false;
System.out.println(yesitcontains);
}
}
if (yesitcontains = true){
return true;
}else
return false;
}
}
You have inadvertently used the assignment operator where you intended the equality operator. In your specific case you should rewrite all this:
if (yesitcontains = true){
return true;
}else
return false;
}
to just
return yesitcontains;
and avoid any chance of confusion.
Furthermore, your algorithm will not work because you should return true immediately when you see a duplicate. Instead you go on with the loop and "forget" your finding. You can expect this to always return false except if the very last elements coincide.
In a wider context, I should also give you the following general advice:
Avoid indexed iteration over lists. Not all lists are ArrayLists and may show O(n) complexity for get(i). Instead use the enhanced for loop, which is safer, more concise, and more obvious;
Know the library: if you're just after confirming there are no duplicates, just Collections.disjoint(list1, list2) would give you what you need;
Be aware of algorithmic complexity: checking for duplicates in two lists is O(n2), but if you turn one of them into a HashSet, you'll get O(n).
Taking everything said above into account, the following would be an appropriate implementation:
static boolean disjoint(Collection<?> c1, Collection<?> c2) {
for(Object o : c1)
if (c2.contains(o))
return true;
return false;
}
If you look at Collections.disjoint, you'll find this exact same loop, preceded by a piece of code which optimizes the usage of sets for reasons described above.
Seems to me your method should be rewritten to:
public static boolean contains(List<String>list1, List<String>list2) {
return list2.containsAll(list1);
}
The code you currently have actually only checks if the last element of list1 is also in list2.
If you're actually looking for a contains any, this simple solution will do:
public static boolean contains(List<String>list1, List<String>list2) {
for (String str : list1) {
if (list2.contains(str)) {
return true;
}
}
return false;
}
if (yesitcontains = true){
should be
if (yesitcontains == true){
== is for comparison and = is for assignment.
if (yesitcontains = true){
will always evaluate to if(true) which causing return true;
EDIT:
(OR)
simply return yesitcontains; as commented.
if (yesitcontains == true) { } // use `==` here
or just
if (yesitcontains) { }
The below code assigns true to yesitcontains , and the expression will always be true.
if (yesitcontains = true) { }
There is no point of if() in your code , you can simple return yesitcontains;
Why cannot I retrieve an element from a HashSet?
Consider my HashSet containing a list of MyHashObjects with their hashCode() and equals() methods overridden correctly. I was hoping to construct a MyHashObject myself, and set the relevant hash code properties to certain values.
I can query the HashSet to see if there "equivalent" objects in the set using the contains() method. So even though contains() returns true for the two objects, they may not be == true.
How come then there isn’t any get() method similar to how the contains() works?
What is the thinking behind this API decision?
If you know what element you want to retrieve, then you already have the element. The only question for a Set to answer, given an element, is whether it contains() it or not.
If you want to iterator over the elements, just use a Set.iterator().
It sounds like what you're trying to do is designate a canonical element for an equivalence class of elements. You can use a Map<MyObject,MyObject> to do this. See this Stack Overflow question or this one for a discussion.
If you are really determined to find an element that .equals() your original element with the constraint that you must use the HashSet, I think you're stuck with iterating over it and checking equals() yourself. The API doesn't let you grab something by its hash code. So you could do:
MyObject findIfPresent(MyObject source, HashSet<MyObject> set)
{
if (set.contains(source)) {
for (MyObject obj : set) {
if (obj.equals(source))
return obj;
}
}
return null;
}
It is brute-force and O(n) ugly, but if that's what you need to do...
You can use HashMap<MyHashObject, MyHashObject> instead of HashSet<MyHashObject>.
Calling containsKey() on your "reconstructed" MyHashObject will first hashCode() - check the collection, and if a duplicate hashcode is hit, finally equals() - check your "reconstructed" against the original, at which you can retrieve the original using get()
Complexity is O(1) but the downside is you will likely have to override both equals() and hashCode() methods.
It sounds like you're essentially trying to use the hash code as a key in a map (which is what HashSets do behind the scenes). You could just do it explicitly, by declaring HashMap<Integer, MyHashObject>.
There is no get for HashSets because typically the object you would supply to the get method as a parameter is the same object you would get back.
If you know the order of elements in your Set, you can retrieve them by converting the Set to an Array. Something like this:
Set mySet = MyStorageObject.getMyStringSet();
Object[] myArr = mySet.toArray();
String value1 = myArr[0].toString();
String value2 = myArr[1].toString();
The idea that you need to get the reference to the object that is contained inside a Set object is common. It can be archived by 2 ways:
Use HashSet as you wanted, then:
public Object getObjectReference(HashSet<Xobject> set, Xobject obj) {
if (set.contains(obj)) {
for (Xobject o : set) {
if (obj.equals(o))
return o;
}
}
return null;
}
For this approach to work, you need to override both hashCode() and equals(Object o) methods
In the worst scenario we have O(n)
Second approach is to use TreeSet
public Object getObjectReference(TreeSet<Xobject> set, Xobject obj) {
if (set.contains(obj)) {
return set.floor(obj);
}
return null;
}
This approach gives O(log(n)), more efficient.
You don't need to override hashCode for this approach but you have to implement Comparable interface. ( define function compareTo(Object o)).
One of the easiest ways is to convert to Array:
for(int i = 0; i < set.size(); i++) {
System.out.println(set.toArray()[i]);
}
If I know for sure in my application that the object is not used in search in any of the list or hash data structure and not used equals method elsewhere except the one used indirectly in hash data structure while adding. Is it advisable to update the existing object in set in equals method. Refer the below code. If I add the this bean to HashSet, I can do group aggregation on the matching object on key (id). By this way I am able to achieve aggregation functions such as sum, max, min, ... as well. If not advisable, please feel free to share me your thoughts.
public class MyBean {
String id,
name;
double amountSpent;
#Override
public int hashCode() {
return id.hashCode();
}
#Override
public boolean equals(Object obj) {
if(obj!=null && obj instanceof MyBean ) {
MyBean tmpObj = (MyBean) obj;
if(tmpObj.id!=null && tmpObj.id.equals(this.id)) {
tmpObj.amountSpent += this.amountSpent;
return true;
}
}
return false;
}
}
First of all, convert your set to an array. Then, get the item by indexing the array.
Set uniqueItem = new HashSet();
uniqueItem.add("0");
uniqueItem.add("1");
uniqueItem.add("0");
Object[] arrayItem = uniqueItem.toArray();
for(int i = 0; i < uniqueItem.size(); i++) {
System.out.println("Item " + i + " " + arrayItem[i].toString());
}
If you could use List as a data structure to store your data, instead of using Map to store the result in the value of the Map, you can use following snippet and store the result in the same object.
Here is a Node class:
private class Node {
public int row, col, distance;
public Node(int row, int col, int distance) {
this.row = row;
this.col = col;
this.distance = distance;
}
public boolean equals(Object o) {
return (o instanceof Node &&
row == ((Node) o).row &&
col == ((Node) o).col);
}
}
If you store your result in distance variable and the items in the list are checked based on their coordinates, you can use the following to change the distance to a new one with the help of lastIndexOf method as long as you only need to store one element for each data:
List<Node> nodeList;
nodeList = new ArrayList<>(Arrays.asList(new Node(1, 2, 1), new Node(3, 4, 5)));
Node tempNode = new Node(1, 2, 10);
if(nodeList.contains(tempNode))
nodeList.get(nodeList.lastIndexOf(tempNode)).distance += tempNode.distance;
It is basically reimplementing Set whose items can be accessed and changed.
If you want to have a reference to the real object using the same performance as HashSet, I think the best way is to use HashMap.
Example (in Kotlin, but similar in Java) of finding an object, changing some field in it if it exists, or adding it in case it doesn't exist:
val map = HashMap<DbData, DbData>()
val dbData = map[objectToFind]
if(dbData!=null){
++dbData.someIntField
}
else {
map[dbData] = dbData
}
I have a code to return an arrayList with the duplicates of an ArrayList
but seems it's not working, I am comparing all items in the array...
public ArrayList<ObjectList> duplicates(ArrayList<ObjectList> someObjectsList) {
ArrayList<ObjectList> ret = new ArrayList<ObjectList>();
for ( ObjectList aSomeObjectsList: someObjectsList) {
String field1 = aSomeObjectsList.get1();
String field2 = aSomeObjectsList.get2();
String field3 = aSomeObjectsList.get3();
String field4 = aSomeObjectsList.get4();
for (ObjectList someObject : ret) {
if (
field1.trim().equals(someObject.get1())&&
field2.trim().equals(someObject.get2())&&
field3.trim().equals(someObject.get3())&&
field4.trim().equals(someObject.get4())
){
ret.add(aSomeObjectsList);
}
}
}
return ret;
}
But i guess I am doing something wrong because it doesn't return anything, and I know it has duplictates under this 4 field criteria
Thanks in advance
for (Object someObject : ret) {
if (
field1.trim().equals(someObject.get1())&&
field2.trim().equals(someObject.get2())&&
field3.trim().equals(someObject.get3())&&
field4.trim().equals(someObject.get4())
){
ret.add(aSomeObjectsList);
}
}
The above loop wouldn't work, since it has the size of zero.
Here you go,
public Set<ObjectList> duplicates(ArrayList<ObjectList> someObjectsList) {
Set<ObjectList> originals = new HashSet<ObjectList>();
Set<ObjectList> duplicates = new HashSet<ObjectList>();
for ( ObjectList aSomeObjectsList: someObjectsList) {
boolean added = originals.add(aSomeObjectsList);
if(!added){
duplicates.add(aSomeObjectsList);
}
}
return duplicates;
}
This would work, provided your ObjectList class have the correct implementation of hashCode() and equals() methods.
Disclaimer: This implementation will not provide the information about how many times a particular object was duplicated in the provided list. It will just tell you that a particular object was duplicated. I assumed that that was your real intention. If you wanna count, how many times, you have to modify the code accordingly.
Hint/Suggestion: You should override the equals() method and place your field equality check in there instead, once and for all.
This shouldn't compile - if aSomeObjectsList is an Object then it doesn't have methods get1(), get2(), etc.
Your logic won't work because you aren't checking each element in your input List against the other elements in the input List; rather, you're trying to check the return List.
Also, this is not a really efficient way to check for duplicates in a collection. A better way would be to use a HashMap, where you could check set membership in roughly constant time. If you have to use a List, then sort it first (assuming your objects have a natural ordering) and check adjacent members for equality.
Barring those two, just use List.contains().
Here's a way you can do this. I have defined a basic class ObjectList that shows a way to implement equals and hashCode. Note that this assumes that all the internal variables are non-null. If these variables can contain null then you will need to check for that when computing the equals/hashCode. Also, the objects in this class must also themselves properly implement equals/hashCode.
public class ObjectList {
private int h;
private Object obj1;
private Object obj2;
private Object obj3;
private Object obj4;
#Override
public boolean equals(final Object o) {
if (!(o instanceof ObjectList))
return false;
final ObjectList that = (ObjectList) o;
return that.obj1.equals(obj1) && that.obj2.equals(obj2)
&& that.obj3.equals(obj3) && that.obj4.equals(obj4);
}
#Override
public int hashCode() {
// caches the hashcode since it could be costly to recompute every time
// but this assumes that your object is essentially immutable
// (which it should be if you are using equals/hashCode. If this is not
// true and you want to just temporarily use this when doing the duplicate
// test, move the h variable definition from the object level to this method
// and remove this if statement.
if (h != 0)
return h;
h = obj1.hashCode();
h = h * 31 + obj2.hashCode();
h = h * 31 + obj3.hashCode();
h = h * 31 + obj4.hashCode();
return h;
}
}
public Collection<ObjectList> duplicates(
final Collection<ObjectList> someObjectsList) {
final Set<ObjectList> unique = new HashSet<ObjectList>(someObjectsList);
final ArrayList<ObjectList> ret = new ArrayList<ObjectList>(someObjectsList);
for (final ObjectList o : unique) {
ret.remove(o);
}
// The ret list now contains the duplicate instances; instances
// with more than two occurrences will occur multiple times still in
// this list.
return ret;
// If you want a list of unique duplicate instances then, comment out the above
// return and uncomment this one.
// return new HashSet<ObjectList>(ret);
}
Using Collection<ObjectList> is better, if you can do that, for both the parameter and returned value so you can vary the implementations (ArrayList, Set, etc).