I want to check whether a List contains an object that has a field with a certain value. Now, I could use a loop to go through and check, but I was curious if there was anything more code efficient.
Something like;
if(list.contains(new Object().setName("John"))){
//Do some stuff
}
I know the above code doesn't do anything, it's just to demonstrate roughly what I am trying to achieve.
Also, just to clarify, the reason I don't want to use a simple loop is because this code will currently go inside a loop that is inside a loop which is inside a loop. For readability I don't want to keep adding loops to these loops. So I wondered if there were any simple(ish) alternatives.
Streams
If you are using Java 8, perhaps you could try something like this:
public boolean containsName(final List<MyObject> list, final String name){
return list.stream().filter(o -> o.getName().equals(name)).findFirst().isPresent();
}
Or alternatively, you could try something like this:
public boolean containsName(final List<MyObject> list, final String name){
return list.stream().map(MyObject::getName).filter(name::equals).findFirst().isPresent();
}
This method will return true if the List<MyObject> contains a MyObject with the name name. If you want to perform an operation on each of the MyObjects that getName().equals(name), then you could try something like this:
public void perform(final List<MyObject> list, final String name){
list.stream().filter(o -> o.getName().equals(name)).forEach(
o -> {
//...
}
);
}
Where o represents a MyObject instance.
Alternatively, as the comments suggest (Thanks MK10), you could use the Stream#anyMatch method:
public boolean containsName(final List<MyObject> list, final String name){
return list.stream().anyMatch(o -> name.equals(o.getName()));
}
You have two choices.
1. The first choice, which is preferable, is to override the `equals()` method in your Object class.
Let's say, for example, you have this Object class:
public class MyObject {
private String name;
private String location;
//getters and setters
}
Now let's say you only care about the MyObject's name, that it should be unique so if two `MyObject`s have the same name they should be considered equal. In that case, you would want to override the `equals()` method (and also the `hashcode()` method) so that it compares the names to determine equality.
Once you've done this, you can check to see if a Collection contains a MyObject with the name "foo" by like so:
MyObject object = new MyObject();
object.setName("foo");
collection.contains(object);
However, this might not be an option for you if:
You are using both the name and location to check for equality, but you only want to check if a Collection has any `MyObject`s with a certain location. In this case, you've already overridden `equals()`.
`MyObject` is part of an API that you don't have liberty to change.
If either of these are the case, you'll want option 2:
2. Write your own utility method:
public static boolean containsLocation(Collection<MyObject> c, String location) {
for(MyObject o : c) {
if(o != null && o.getLocation.equals(location)) {
return true;
}
}
return false;
}
Alternatively, you could extend ArrayList (or some other collection) and then add your own method to it:
public boolean containsLocation(String location) {
for(MyObject o : this) {
if(o != null && o.getLocation.equals(location)) {
return true;
}
}
return false;
}
Unfortunately there's not a better way around it.
This is how to do it using Java 8+ :
boolean isJohnAlive = list.stream().anyMatch(o -> "John".equals(o.getName());
Google Guava
If you're using Guava, you can take a functional approach and do the following
FluentIterable.from(list).find(new Predicate<MyObject>() {
public boolean apply(MyObject input) {
return "John".equals(input.getName());
}
}).Any();
which looks a little verbose. However the predicate is an object and you can provide different variants for different searches. Note how the library itself separates the iteration of the collection and the function you wish to apply. You don't have to override equals() for a particular behaviour.
As noted below, the java.util.Stream framework built into Java 8 and later provides something similar.
Collection.contains() is implemented by calling equals() on each object until one returns true.
So one way to implement this is to override equals() but of course, you can only have one equals.
Frameworks like Guava therefore use predicates for this. With Iterables.find(list, predicate), you can search for arbitrary fields by putting the test into the predicate.
Other languages built on top of the VM have this built in. In Groovy, for example, you simply write:
def result = list.find{ it.name == 'John' }
Java 8 made all our lives easier, too:
List<Foo> result = list.stream()
.filter(it -> "John".equals(it.getName())
.collect(Collectors.toList());
If you care about things like this, I suggest the book "Beyond Java". It contains many examples for the numerous shortcomings of Java and how other languages do better.
Binary Search
You can use Collections.binarySearch to search an element in your list (assuming the list is sorted):
Collections.binarySearch(list, new YourObject("a1", "b",
"c"), new Comparator<YourObject>() {
#Override
public int compare(YourObject o1, YourObject o2) {
return o1.getName().compareTo(o2.getName());
}
});
which will return a negative number if the object is not present in the collection or else it will return the index of the object. With this you can search for objects with different searching strategies.
Map
You could create a Hashmap<String, Object> using one of the values as a key, and then seeing if yourHashMap.keySet().contains(yourValue) returns true.
Eclipse Collections
If you're using Eclipse Collections, you can use the anySatisfy() method. Either adapt your List in a ListAdapter or change your List into a ListIterable if possible.
ListIterable<MyObject> list = ...;
boolean result =
list.anySatisfy(myObject -> myObject.getName().equals("John"));
If you'll do operations like this frequently, it's better to extract a method which answers whether the type has the attribute.
public class MyObject
{
private final String name;
public MyObject(String name)
{
this.name = name;
}
public boolean named(String name)
{
return Objects.equals(this.name, name);
}
}
You can use the alternate form anySatisfyWith() together with a method reference.
boolean result = list.anySatisfyWith(MyObject::named, "John");
If you cannot change your List into a ListIterable, here's how you'd use ListAdapter.
boolean result =
ListAdapter.adapt(list).anySatisfyWith(MyObject::named, "John");
Note: I am a committer for Eclipse ollections.
Predicate
If you dont use Java 8, or library which gives you more functionality for dealing with collections, you could implement something which can be more reusable than your solution.
interface Predicate<T>{
boolean contains(T item);
}
static class CollectionUtil{
public static <T> T find(final Collection<T> collection,final Predicate<T> predicate){
for (T item : collection){
if (predicate.contains(item)){
return item;
}
}
return null;
}
// and many more methods to deal with collection
}
i'm using something like that, i have predicate interface, and i'm passing it implementation to my util class.
What is advantage of doing this in my way? you have one method which deals with searching in any type collection. and you dont have to create separate methods if you want to search by different field. alll what you need to do is provide different predicate which can be destroyed as soon as it no longer usefull/
if you want to use it, all what you need to do is call method and define tyour predicate
CollectionUtil.find(list, new Predicate<MyObject>{
public boolean contains(T item){
return "John".equals(item.getName());
}
});
Here is a solution using Guava
private boolean checkUserListContainName(List<User> userList, final String targetName){
return FluentIterable.from(userList).anyMatch(new Predicate<User>() {
#Override
public boolean apply(#Nullable User input) {
return input.getName().equals(targetName);
}
});
}
contains method uses equals internally. So you need to override the equals method for your class as per your need.
Btw this does not look syntatically correct:
new Object().setName("John")
If you need to perform this List.contains(Object with field value equal to x) repeatedly, a simple and efficient workaround would be:
List<field obj type> fieldOfInterestValues = new ArrayList<field obj type>;
for(Object obj : List) {
fieldOfInterestValues.add(obj.getFieldOfInterest());
}
Then the List.contains(Object with field value equal to x) would be have the same result as fieldOfInterestValues.contains(x);
Despite JAVA 8 SDK there is a lot of collection tools libraries can help you to work with, for instance:
http://commons.apache.org/proper/commons-collections/
Predicate condition = new Predicate() {
boolean evaluate(Object obj) {
return ((Sample)obj).myField.equals("myVal");
}
};
List result = CollectionUtils.select( list, condition );
Related
CompareList removedList = new CompareList();
CompareList addedList = new CompareList();
This is how I'm adding elements inside
addedList.add(new Objec("Var_a", "1"));
and class Objec has two strings.
How can I compare that? I can't use contains like I could with ArrayList of Strings?
In CompareList I have
public boolean equals(CompareList l) {
if (l.containsAll(this)) {
if (this.containsAll(l)) {
return true;
}
}
return false;
}
and in Objec
public Objec(String n, String s) {
this.name=n;
this.surname=s;
}
public String toString() {
return " Name: " + name + ", Surname: " + surname;
}
I see that many people are confused with my question. So what I want?
List 1:
Samy Joe
Emma Than
Julia Rob
List 2:
Samy Joe
Emma Than
Anna Sky
Removed Julia Rob and added Anna Sky. But I don't know how to do it when my lists contains of object that have two strings?
This piece of code compares if the lists are equal, that is, contains the same elements.
static boolean same(Collection<?> a, Collection<?> b) {
if (a.size() != b.size()) {
return false;
}
List<?> c = new ArrayList<>(a);
c.removeAll(b);
return c.isEmpty();
}
If the sizes are not equal, then the lists are never equal.
Else, if the sizes are equal, then we know that both lists contain one or more elements that are not present in the other list. So we make a new list from one of the lists (list a in my case), and then we remove the elements of b.
You don't need to use your own class CompareList, instead you could just use an ArrayList or something.
In order to compare your Objec to another one, you'll need to implement equals(Object) and hashCode() correctly.
If you want to know which elements are not contained in the other list, then you can use this:
static HashMap<Collection<?>, Collection<?>> disjoints(Collection<?> a, Collection<?> b) {
List<?> aa = new ArrayList<>(a);
aa.removeAll(b);
List<?> bb = new ArrayList<>(b);
bb.removeAll(a);
HashMap<Collection<?>, Collection<?>> map = new HashMap<>();
map.put(a, aa);
map.put(b, bb);
return map;
}
It returns a map with as keys the two collections and as values the elements of the collection specified by the key, which are not contained in the other collection. For example, if you want to know the elements of a not present in b, then call disjoints(a, b).get(a).
Note: I call the lists collections, because they are. In Java, a List is a subtype of Collection.
You need to override the equals method in your custom object like this:
public class MyObject {
private String name;
private String surname;
#Override
public boolean equals(MyObject myObject) {
// assert that name and surename can not be null or check for this
if (!this.name.equals(myObject.name)) {
return false;
}
if (!this.surname.equals(myObject.surname)) {
return false;
}
return true;
}
}
The contains-Method of a list will be use the equals method to check the equality of your objects.
But the link that #Prakash has posted is the better way do do that. I think no one understand what your attention is to do with yout CompareList and why you use a custom one ;)
EDIT:
Sorry, had mistake in the Signature.
#Override
public boolean equals(Object obj) {
if (!(obj instanceof MyObject) {
return false;
}
// else cast and use code above
}
You're question: "How can I compare that?"
The first thing to keep in mind is that you can compare in 2 ways.
so think about that first.
Equality:
When comparing for equality
use equals() and while you're at it implement hashCode().
Comparing / sorting:
When are 2 objects considered lower then, same, or higher then the other?
implement "Comparable" interface and override compareTo.
You're post contains the equals() so lets say that is what you want to do.
If you want to use containsAll(), keep in mind that it uses equals from the class the list contains to figure out to return true/false. So you should probably:
add equals() to your "Objec" class.
I am not getting the right answer when I try to use indexOf() of an ArrayList made up of user defined objects. Here is the code that creates one of the objects:
State kansas = new State("KS", 5570.81, 2000)
So, the name of the object is "kansas"
Here is the code that creates the ArrayList and adds the object:
ArrayList<State> allStates = new ArrayList<State>();
allStates.add(kansas);
And here is the code that I try to use to find the index of this object:
System.out.println(allStates.indexOf(kansas));
This is the point at which my compiler (Eclipse) throws me a red X indicating that there is a problem with my code and the problem is that it does not recognize 'kansas'. So I tried this:
String s = "kansas";
System.out.println(allStates.indexOf(s));
and it will run but the result is -1.
I am calling a method from a different class to create the ArrayList as opposed to creating it in the same class as my main method but I'm new enough to coding that I"m not sure if that is where I am going wrong. However, in order for the program that I am writing to work, I need to have data about each of the State objects stored so that I can access it from the main method.
Any advice?
*This is my first time posting a questions and I wasn't sure how much detail to go into so if I'm missing relevant information please let me know :)
method indexOf uses equlas() method to compare objects.
That why you have to override equals method in your custom class (if you planning use class in Map override hashCode method as well).
most IDE can generate these methods (equals and hashCode).
here simple example.
public class State {
private String stateCode;
public State(String stateCode /* other parameters*/) {
this.stateCode = stateCode;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
State state = (State) o;
return stateCode.equals(state.stateCode);
}
#Override
public int hashCode() {
return stateCode.hashCode();
}
}
This is because, String is not your custom object State type. Your array list is a list of all 'State' types, which is why this -
String s = "kansas";
System.out.println(allStates.indexOf(s));
won't work.
What you can do is have a convenience method that iterates through the list and returns the index.
private int getIndexOfState(String stateName) {
for(State stateObject : allStates) {
if(stateObject.getName().equals(stateName))
return allStates.indexOf(stateObject);
}
return -1;
}
Now you can reuse this method to find index of any state name you pass, and whenever the method returns -1, it means the stateName(state) was not found in the list of states.You can pass in 'Kansas' or 'California' or anything as the parameter to the method.
In your method call you say
System.out.println(getIndexOfState("Kansas"));
System.out.println(getIndexOfState("Chicago"));
The return value is -1 because there is no String "kansas" in allStates, and ArrayList#indexOf returns -1 if the element is not present in the list. If you try to add s to allStates, the compiler won't even let you, because State is not a String.
I don't know why you instantiated a String with the value "kansas", but if you need to refer to the State from its name (maybe the name comes from a Scanner input), you will need a Map<String, State>, such as:
Map<String, State> map = new HashMap<>();
map.put("kansas", kansas) // a String and the object named kansas
Then, you can do:
System.out.println(allStates.indexOf(map.get("kansas")))
//or
String s = "kansas";
System.out.println(allStates.indexOf(map.get(s)))
I have a project in Java. First I need to create a collection with no duplicates in the name of object & its number, so I use the Set collection and this equals method:
public boolean equals(Object obj) {
Course<?> c=(Course<?>)obj;
return (c.number==number&& c.Name.equals(Name));
}
next ..i need to create collection of the same object but now i need to insure that only his name is not duplicate. so its a problem because i cant use two diffrents methods of equals
what can i do??
I'd use a TreeSet instead and specifying the comparator to use for that specific set instead of overriding equals.
https://docs.oracle.com/javase/8/docs/api/java/util/TreeSet.html#TreeSet-java.util.Comparator-
If you don't want them actually sorted, but just remove dupes, the comparator just has to return 0 when they are equal.
TreeSet<Course> tree1 = new TreeSet<Course>((c1, c2) -> c1.number==c2.number && c1.Name.equals(c2.Name) ? 0 : 1);
and
TreeSet<Course> tree2 = new TreeSet<Course>((c1, c2) -> c1.Name.equals(c2.Name) ? 0 : 1);
You can wrap your class in a wrapper class that will implement the hashcode and equals functions the way you want:
public NameWrapper {
private Course c;
public NameWrapper(Course c) {
this.c = c;
}
public void equals(Object other) {
// ...
return this.name.equals(other.name);
}
// + hashCode
// + getter
}
// Similarly with number and name wrapper
And then you can wrap, distinct and unwrap your elements:
Collection<Course> courses = // ...
Collection<Course> distincts =
courses.stream()
.map(NameWrapper::new) // wrap
.distinct()
.map(NameWrapper::getCourse) // unwrap
.map(NumberNameWrapper::new) // wrap
.distinct()
.map(NumberNameWrapper::getCourse) // unwrap
.collect(Collectors.toList())
One easy, but probably not really great solution would be to use two specific wrapper classes that each have different equals methods.
And instead of directly using your own class, you put objects of those "wrapper" classes into those collections.
Like:
class Course { ... your class
class CourseWrapperForName {
Course wrappedCourse;
...
Course getWrappedCourse() { return wrappedCourse; }
#Override
public boolean equals(Object other) {
... compares names
class CourseWrapperForNumber {
Course wrappedCourse;
...
#Override
public boolean equals(Object other) {
... compares numbers
Now, eliminating duplicates can be done by putting your Course objects into the corresponding wrapper; adding wrappers to Sets; and then retrieving the course.
But obviously, that is a lot of boilerplate; and more reasonable solutions could be
A) using a TreeSet with a different
B) the later can be enhanced with a lot of black lambda magic; there is a nice presentation how to do that (it is in German, but mainly code; the interesting part starts at page 40).
I would take the Set<Course> with this equals method implemented (that will give me the course which are unique in name & number both).
Further, I would make a subclass of Course 'SubCourse' and override the equals method:
class SubCourse extends Course{
public boolean equals(Object o){
if(o instanceof SubCourse){
return (this.Name.equals(((SubCourse)o).Name));
}else{
return false;
}
}
}
And then make a Set<SubCourse> that will give you a unique courses in terms of numbers (not in name as we excluded that condition). You need to make the instance variables of Course as protected.
I'm building simple phonebook. Thus a have created a class "Person":
public class Person implements Comparable<Person> {
String Name;
String number;
public Person(String name,String Num) {
Name=name;
number=Num;
}
public String getNumber() {
return number;
}
public String getName() {
return Name;
}
#Override
public int compareTo(Person another) {
return Name.compareTo(another.getName());
}
#Override
public String toString() {
return Name;
}
#Override
public boolean equals(Object obj) {
if(!(obj instanceof Person) && !(obj instanceof String))
{
return false;
}
else
{
if(obj instanceof Person)
return Name.toLowerCase().equals(((Person)obj).getName().toLowerCase());
else
return Name.toLowerCase().equals(((String)obj).toLowerCase());
}
}
#Override
public int hashCode() {
return Name.hashCode();
} }
In some other part of the program i'm creating a Vector, populate it with "Person" objects but when i try to search a person BY NAME using vctPerson.indexOf("John") I always get -1 as result (not found). What's wrong with my code? I have implemented custom "equals" that should work with strings, and according to docs, "indexOf" is using "equals" to compare objects...
EDIT: I KNOW, I SHOULD SEARCH AFTER PHONE NUMBER, NOT NAME BUT IT's IRRELEVANT FOR THIS EXAMPLE
What Vector does in indexOf:
if (o.equals(elementData[i]))
where o would be "John". So you would have to override Sting.equals to do the right comparison (just kidding). Or you could use
vector.indexOf(new Person("John", null));
which will call your equals. Strictly speaking that will solve your question.
But in the long run you should not use Vector for that, because every indexOf call will iterate through the list - this is not very efficient.
A better way is a Map like HashMap where you can store key-value pairs. Lookup using the key is much cheaper than Vector.indexOf if here are a couple of entries.
Map<String, Person> map = new HashMap<String, Person>();
Person p = new Person("John", "1234");
map.put(p.getName().toLowerCase(), p);
// loopup
Person john = map.get("John".toLowerCase());
you called vctPerson.indexOf("John") . In this case, Vector call "John".equals( vctPerson.get( indexValue ) . As equals of String is called, String's equals compare "John" and Person object.
But as String's equals() does not return true when target object is not an instance of String, "John".equals( vctPerson.get( indexValue ) always return false. So result is always -1.
So, you can't use vctPerson.indexOf("John"). If you want to use vector, you need to traverse vector manually.
Your equals is broken: You objects may equal to a String (and that's what you're trying to exploit), but no String may ever equal to you object. Breaking symmetry of equals breaks everything.
Well, to be on the safe side you can always use
1) Map<String,Person> to make the relation between a person and his name
2) Make your own class that extends java.util.Vector and overrides its indexOf method
3) place a breakpoint in your equals method and see what's going on when indexOf gets called. Whatever's going on, though, it's better that you don't rely on the current implementation of indexOf that's specified in the JDK documentation since it may get changed upon the release of a next version of the JDK :)
I have a list. The list can contain multiple items of the same enum type.
Lets say i have an enum : TOY which has values: BALL, DOLL, PLAYSTATION. I want to know how many PLAYSTATION items are in a list with the type TOY. (ie, List<Toy> toys)
What is the best possible solution for this? I don't want to keep iterating through the list everytime.
You can use Apache commons-collections' HashBag. It has a getCount(Object) method which will suit you.
java.util.Collections has a method called frequency(Collection c, Object type).
Usage in my question:
int amountOfPlayStations = Collections.frequency(toys, TOY.PLAYSTATION);
Why don't you create a decorator for the type of list you're using which stores a list of counts for each enum type have been added/removed internally. That way you could use it as a normal list but also add some extra functionality for querying how many of which type are currently contained.
All you'd need to do would be to override the add/remove/addAll etc methods and increment your counters before passing it on to the real list type. The best part about it would be that you could decorate any list type with your new wrapper.
At the very least, a utility method like:
public int count(List<Toy> haystack, Toy needle) {
int result;
for (Toy t : haystack) {
if (t == needle) {
result++;
}
}
return result;
}
Would let you concisely refer to the number of PLAYSTATIONs from elsewhere in the code. Alternatively if you knew the list was unlikely to change, building a Map<Toy, Integer> would let you build up the counts for all items once.
If you don't want to have to iterate over the entire collection each time, another alternative would be to write a ForwardingList implementation. The main benefits of this over the HashBag suggestion are:
it supports generics
it implements the List interface, so you can pass it to any method that expects a List
There is a downside to this approach however, in that you have to write a bit of plumbing code to get it up and running.
Below is a quick example of how you could do it. Note that if you do this you should override all methods that add/delete from the list, otherwise you may end up in an inconsistent state:
import com.google.common.collect.ForwardingList;
public class CountingList<E> extends ForwardingList<E> {
private List<E> backingList = new LinkedList<E>();
private Map<E, Integer> countMap = new HashMap<E, Integer>();
#Override
protected List<E> delegate() {
return backingList;
}
#Override
public boolean add(E element) {
backingList.add(element);
if(countMap.containsKey(element)) {
countMap.put(element, countMap.get(element) + 1);
} else {
countMap.put(element, 1);
}
return true;
}
public int getCount(E element) {
Integer count = countMap.get(element);
return count != null ? count.intValue() : 0;
}
}
Extend java.util.List method and override all mutator methods, i.e. the ones that are used for add or delete elements and also ones used to clear the list. Add a reference to a private java.util.Map which will hold the number of items per type. Add accessor methods which will return current number of elements per type.
The HashBag (by Bozho) seems to be your best bet. But a bit more general would be Googles Collections 2 with an appropriate Predicate:
List<Toy> toys;
List<Toy> playstations = Collections2.filter( toys, new Predicate() {
boolean apply(TOY toy){
return toy == TOY.PLAYSTATION;
}
});
Besides all those solutions (I have a weakness for the Collections.Frequency call), i would recommend you to take a look at google collections, and particularly to [Collections2.transform][2], which could give you a live view on items.
[2]: http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/collect/Collections2.html#transform(java.util.Collection, com.google.common.base.Function)