Selecting only elements with specific attributes from Java List - java

So lets say I have a list, List<MyObject>, and a class MyObject as such:
public class MyObject {
Type t;
}
Where 't' is a non-unique identifier. How would I select all elements from my list with a specific value for t? For my purpose I am trying to return a count of how many objects in the list have a specific value for t.
I am assuming there is some clean way of doing this in Java without using an explicit loop?

Streams are your friend:
List<MyObject> list = ...;
long count = list.stream().filter(e -> e.getT().equals(<specific value>)).count();
You would of course need a way to access t, here I use getT() as an example.

If you don't want to use Java 8 use simple iteration as following:
int count(List<MyObject> list, Type x) {
int cnt = 0;
for(MyObject obj: list) {
if(obj.getT() == x) cnt++;
}
return cnt;
}

Related

Java <Streams> How to sort the list of my objects, based on the count of the components of the List

I have 2 classes in Java. One is a Car class that consists of 5 variables. Among them I have a List equipment variable. Another class contains the list of the Car class objects: List carlist.
My task is: I have to sort the list of car object, using Streams in Java based on the amount of the equipment items that the given car have.
How do I do that? I tried to build a separate method to count the items on the list of the object - but then within the Comparator I can't place an Object as an argument of this method.
Here's an excerpt of my code:
private int countEquipmentItems (Car s){
if (s == null){
return 0;
}
int countEquipment = 0;
List<String> a = s.getEquipment();
for (int i = 0; i <a.size() ; i++) {
countEquipment ++;
}
return countEquipment;
}
And I have tried to use this method within the Stream:
public void sortbyEquipment (List<Car> carList){
carList.stream()
.sorted(Comparator.comparing(countEquipmentItems(Car s)));
}
}
I appreciate any help
You don't need that countEquipmentItems method to count the amount of equipment. Just use car.getEquipment().size():
public void sortbyEquipment (List<Car> carList){
carList.stream()
.sorted(Comparator.comparing(car -> car.getEquipment().size()))
...
}
Of course, you can pass that Comparator directly to Collections.sort(), which will sort the list without having to create a Stream.
Your countEquipmentItems method is redundant and completely unnecessary.
Another solution to what Eran has provided would be to call the default sort method that is available for the List<T> type.
carList.sort(Comparator.comparingInt(car -> car.getEquipment().size()));
or if you want the sorted items to be in a new collection then you can do:
List<Car> clonedList = new ArrayList<>(carList); // clone the carList
clonedList.sort(Comparator.comparingInt(car -> car.getEquipment().size()));

Java 8 Stream get object from filter result

Note: I don't know if I titled this correctly, so please feel free to alter it to something more appropriate, I'm quite new to the terminology of Java 8.
Question: I have some object A, I want to filter it based on a numerical value that it holds, say, an integer. I want to find the object with the highest value and then return that Object. How is this done using streams?
public SomeObject getObjectWithHighestValue()
{
int max = Integer.MIN_VALUE;
SomeObject maxObj = null;
for(SomeObject someObj : someList)
{
if(someObj.getValue() > max)
{
max = someObj.getValue();
maxObj = someObj;
}
}
return maxObj;
}
Above I have included a java 7 way of doing roughly what I want.
There's not necessarily a need for streams, you could also use Collections.max with a custom comparator:
import static java.util.Collections.max;
import static java.util.Comparator.comparing;
...
SomeObject o = max(someList, comparing(SomeObject::getValue));
The advantages with the stream approach is that you can parallelize the task if needed, and you get back an empty Optional if the list is empty (whereas it would throw an exception with an empty list using Collections.max, but you can check the size before).
return list.stream()
.max(Comparator.comparing(SomeObject::getValue))
.orElse(null);
SomeObject maxObject = someList.stream().max(Comparator.comparing(SomeObject::getValue).get();

Comparing lists of different types

I have two lists as follows
List<MyObject1> list1
List<Long> list2
The list2 is basically the list of Id's of MyObject1 which is a property in the object
Public Class MyObject1 implements Serializable{
private Long myObjId;
.....
.....
//other elements go here
public Long getMyObjId() {
return myObjId;
}
public void setMyObjId(Long myObjId) {
this.myObjId = myObjId;
}
I want to compare the two lists to check if all the objects in list1 are contained in list2
One way is to iterate over the list1, create a new list of Id's out of it and then use the containsAll method on it.
Is there a simpler way to achieve the same result?
In Java 8 you can write what you described: "One way is to iterate over the list1, create a new list of Id's out of it and then use the containsAll method on it." in one line as:
list1.stream().map(a -> a.getMyObjId()).collect(Collectors.toList()).containsAll(list2);
map converts a each MyObeject to an id by calling a.getMyObjectId and collect creates a list
as a result.
There are at least two conceptually different approaches:
One could collect the IDs in a list, and work on the resulting list
One could do the check on-the-fly
The first one would boil down to something like
boolean allIdsContained(List<MyObject> myObjects, List<Long> validIds) {
List<Long> ids = new ArrayList<Integer>();
for (MyObject m : myObjects) ids.add(m.getID());
return validIds.continsAll(ids);
}
The second one could be written as
boolean allIdsContained(List<MyObject> myObjects, List<Long> validIds) {
for (MyObject m : myObjects) {
if (!validIds.contains(m.getID()) {
return false;
}
}
return true;
}
Note that the method signature is the same in both cases, so you are free to change the implementation according to your needs. Particularly, if the list if validIds is large, then it could be more efficient to first convert it to a Set. (The contains method on a List is O(n), whereas on a Set, it is O(1)). Then the method could be implemented as
boolean allIdsContained(List<MyObject> myObjects, List<Long> validIds) {
Set<Long> set = new HashSet<Long>(validIds);
for (MyObject m : myObjects) {
if (!set.contains(m.getID()) {
return false;
}
}
return true;
}
In any case, all these methods could be written more concisely with Java 8 Lambdas, but I think that this should be an implementation detail, and should be hidden in such a helper method regardless of how it is implemented.

How do filter out this list with java 8 streams and functional interfaces?

if I have a list of arrays like this (pseudo java code):
Note the list valsSorted will be always sorted with x[0] asc and x[1] desc order.
List valsSorted = {[1 5][1 4][1 3][2 1][3 2][3 1][4 2][4 1][5 1][6 2][6 1]};
How do I filter this list with Java 8 streams and lambdas so that I get:
result = {[1 5][2 1][3 2][4 2][5 1][6 2]}
The first item of the array (x[0]) is ID and the second is a version number. So the rule is give all distinct IDs with the highest version back.
If I would use a for loop the following code would be fine:
ArrayList<int[]> result= new ArrayList();
int keep = -1;
for (int[] x : valsSorted) {
int id = x[0];
int version = x[1];
if(keep == id) continue;
keep = id;
result.add(x);
}
Your use of the word "distinct" suggests using the distinct() stream operation. Unfortunately that operation is hardwired to use the equals() method of the stream elements, which isn't useful for arrays. One approach for dealing with this would be to wrap the arrays in a wrapper object that has the semantics of equality that you're looking for:
class Wrapper {
final int[] array;
Wrapper(int[] array) { this.array = array; }
int[] getArray() { return array; }
#Override
public boolean equals(Object other) {
if (! (other instanceof Wrapper))
return false;
else
return this.array[0] == ((Wrapper)other).array[0];
}
#Override
public int hashCode() { ... }
}
Then wrap up your object before distinct() and unwrap it after:
List<int[]> valsDistinct =
valsSorted.stream()
.map(Wrapper::new)
.distinct()
.map(Wrapper::getArray)
.collect(toList());
This makes one pass over the data but it generates a garbage object per value. This also relies on the stream elements being processed in-order since you want the first one.
Another approach would be to use some kind of stateful collector, but that will end up storing the entire result list before any subsequent processing begins, which you said you wanted to avoid.
It might be worth considering making the data elements be actual classes instead of two-element arrays. This way you can provide a reasonable notion of equality, and you can also make the values comparable so that you can sort them easily.
(Credit: technique stolen from this answer.)
class Test{
List<Point> valsSorted = Arrays.asList(new Point(1,5),
new Point(1,4),
new Point(1,3),
new Point(2,1),
new Point(3,2),
new Point(3,1),
new Point(4,2),
new Point(4,1),
new Point(5,1),
new Point(6,2),
new Point(6,1));
public Test(){
List<Point> c = valsSorted.stream()
.collect(Collectors.groupingBy(Point::getX))
.values()
.stream()
.map(j -> j.get(0))
.collect(Collectors.toList());
for(int i=0; i < c.size(); i++){
System.out.println(c.get(i));
}
}
public static void main(String []args){
Test t = new Test()
}
}
I decided to use the point class and represent the ID field as x and the version number as Y. So from there if you create a stream and group them by ID. You can call the values method which returns a Collection of Lists Collection<List<Point>>. You can then call the stream for this Collection and get the first value from each list which according to your specifications is ordered with descending version number so it should be the the highest version number. From there all you have to do is collect them into a list, array or whatever you see necessary and assign it as needed.
The only problem here is that they are printed out of order. That should be an easy fix though.

ClassCastException using Iterables from Guava

I am trying to use the Iterables class to filter an ArrayList, but when I try to cast the result back to the original type I get a ClassCastException at runtime.
// domains is an ArrayList<Domain> which is defined earlier in the class
Iterable<Domain> temp = Iterables.filter(domains, new Predicate<Domain>() {
public boolean apply(Domain input) {
if (input.getName().toLowerCase().contains(filter.toString().toLowerCase())) {
return true ;
} else {
return false;
}
}
}) ;
ArrayList<Domain> filteredDomains = (ArrayList<Domain>) temp ; // Error occurs here
To be complete, I am trying to use this in an Android application with a target of 1.6.
temp is not an ArrayList<Domain>. It is an Iterable<Domain>.
If you absolutely need an ArrayList<Domain> (or a List<Domain> in general), then you need to take a slightly different approach.
First, use Collections2.filter() instead of Iterables.filter(): to produce temp and then create a new ArrayList from the resulting Collection:
Collection<Domain> temp = Collections2.filter(domains, myPredicate) ;
List<Domain> filteredDomains = new ArrayList<Domain>(temp);
But you should really think if you need a List or ArrayList and if a Collection is not enough for what you want. If an Iterable is sufficient (for example if you only iterate over the content), then you can even keep using Iterables.filter().
The real return type is an anonymous class that extends IterableWithToString - and we cannot cast that type to ArrayList.
Here's the implementation on grepcode.
Temp is an Iterable not an ArrayList. Further more an ArrayList is an Iterable but not vice versa. What if you had
Iterable<?> iterable = new HashSet<?>();
You can see why a class cast exception would happen here.
To create an ArrayList from the Iterable you would have to iterate over the iterable itself and add to a new ArrayList
List<Domain> filteredDomains = new ArrayList<Domain>();
for(Iterator<Domain> i = temp.iterator(); i.hasNext();){
filteredDomains.add(i.next());
}

Categories

Resources