Need to access multiple methods in order to compare objects - java

After reading the Oracle docs I did not found what I am looking for. I have an object that it can be compared in different ways, but one of its attributes is a List. How can I compare based on the size of the that List attribute?
Comparator comparator = Comparator.comparing(Product::getCommentList::size);
List<Product> soldL = new LinkedList();
soldL.addAll(sold);
Collections.sort(soldL,comparator);
I tried the code above without success.

You can simply use lambda and improve your existing code as :
Comparator<Product> comparator = Comparator.comparing(p -> p.getCommentList().size()); // type 'Product' bound
List<Product> soldL = new LinkedList<>(); // type inferred '<>'
soldL.addAll(sold);
soldL.sort(comparator); // use 'List.sort'
Edit: You can make use of the comparingInt instead of comparaing to avoid boxing as:
Comparator<Product> comparator = Comparator.comparingInt(p -> p.getCommentList().size());

You can simplify the comparison by implementing a method on Product that returns the size of the list (ex: public int getCommentListSize() { return commentList.size(); }).
So you can create a comparator this way:
Comparator comparator = Comparator.comparing(Product::getCommentListSize);
After that, you pass comparator to the sort method.

Related

TreeSet not arranging value in ascending order

I am trying to create a TreeSet to sort the strings which are inserted to be in an ascending order. I am using below code for entering values in TreeSet.
TreeSet<String> ts = new TreeSet<String>();
ts.add("#Test0");
ts.add("#Test1");
ts.add("#Test2");
ts.add("#Test3");
ts.add("#Test10");
ts.add("#Test4");
System.out.println("Tree set :: "+ts);
Output:
Tree set :: [#Test0, #Test1, #Test10, #Test2, #Test3, #Test4]
You've used the no-args TreeSet constructor. This means TreeSet will order its elements based on natural order. It's the way the objects compare themselves: It means the things you add must be of a type that implements Comparable<Self>. String does that: The String class is defined to implement Comparable<String>. However, the way strings compare themselves is lexicographically. 10 comes before 2 for the same reason that aa comes before b.
You have two routes available to fix this:
Don't put strings in there but some other object that implements Comparable and does it right. Perhaps a class Thingie {String name; int idx;}.
Pass a Comparator as first and only argument to your TreeSet class. Write code that determines that #Test10 comes before #Test2. Then, TreeSet uses this comparator to determine ordering and won't use the one built into strings.
Specify the Comparator to sort on the number part only. This removes all but the number portion, converts that to an integer and sorts on that.
TreeSet<String> ts = new TreeSet<String>(Comparator.comparing(
s -> Integer.valueOf(s.replace("#Test", ""))));
ts.add("#Test0");
ts.add("#Test1");
ts.add("#Test2");
ts.add("#Test3");
ts.add("#Test10");
ts.add("#Test4");
System.out.println(ts);
prints
[#Test0, #Test1, #Test2, #Test3, #Test4, #Test10]
This works for the shown example. You may need to modify it somewhat for more varied data. But it demonstrates the idea.
#Test10 comes before #Test2 because 1 comes before 2. That's how the default ordering of String works (String implements the interface Comparable to do this sorting).
To solve your issue you need to provide a custom Comparator to the TreeSet, and do the comparison by parsing the integer within the string:
TreeSet<String> ts = new TreeSet<String>(new Comparator<String>() {
#Override
public int compare(String s1, String s2) {
return Integer.parseInt(s1.substring(5)) - Integer.parseInt(s2.substring(5));
}
});
The comparator can be constructed using the static convenience method:
TreeSet<String> ts = new TreeSet<>(Comparator.comparing(s -> Integer.parseInt(s.substring(5))));
As #Jems noted in the comment, strings are sorted lexichographically, so "#Test10" will come before "#Test2". If could however, supply a custom Comparator to define the order you need. E.g., if you know all the strings will have the form of "#Test" followed by a number, you could extract this number and sort accordingly:
TreeSet<String> ts =
new TreeSet<>(Comparator.comparingInt(s -> Integer.parseInt(s.substring(5))));

Java filter list to only include distinct (Comparator does not return null for any pair of values) values

I created a Comparator for a specific class. With this, I could easily sort lists (respectively streams) of this class.
In my case, I need to know if there are pairs of objects that are "equal" in the sense of the Comparator. Is there a easy way to get an list of "distinct" objects, or to know if there are "equal" objects?
The Comparator counts the number of null values in fields. So I want to know or filter objects with same number of null values.
Use StreamEx to collect pairs from a stream and be able to pass a BiPredicate
Then pass a bipredicate to filter your data :
Comparator<Object> yourComparator = ...;
BiPredicate<Object,Object> bip = (d1,d2) -> comp.compare(d1,d2) == 0;
List<Object> data = ...;
List<Object> result = data.stream().pairMap(bip).collect(Collectors.toList());

any alternative to overriding "equals()" method in java?

I have several very large ArrayLists of objects which i would like to find their Symmetric Differences ( or disjunction). To do so i have decided to use Sets and their "contain()" methods. However, this method uses the equals() method to evaluate said objects.
Problem is, i cannot make any changes in my class. So, i cannot override any method. (my code is just a small part of a very bigger project)
so this leaves me here, is there any other alternative to altering the classes themselves ? or any other way that would not require me to make any changes to my classes ?
I've recently found out about this so I have an alternate solution (only for Java 8):
// Being T the class of the objects in the list
ArrayList<T> list1 = ...;
ArrayList<T> list2 = ...;
// A function to compare two elements
BiFunction<T, T, Boolean> funcEquals = (a,b) -> yourEquals(a,b);
// A function that given a List returns a predicate that states if an element is on that list
Function<List<T>, Predicate<T>> notIn = (s) -> (e) -> s.stream().filter((y) -> funcEquals.apply(e, y)).count() == 0;
// Get the elements in list1 that are not in list2
Stream<String> list1Filtered = list1.stream().filter(notIn.apply(list2));
// Get the elements in list2 that are not in list1
Stream<String> list2Filtered = list2.stream().filter(notIn.apply(list1));
/*
If you have more than two lists, comparisons can be concatenated:
Stream<String> list1Filtered = list1.stream().filter(notIn.apply(list2)).filter(notIn.apply(list3));
Stream<String> list2Filtered = list2.stream().filter(notIn.apply(list1)).filter(notIn.apply(list3));
Stream<String> list3Filtered = list3.stream().filter(notIn.apply(list1)).filter(notIn.apply(list2));
*/
// Add them all together
ArrayList<T> result = new ArrayList<T>();
result.addAll(list1Filtered.collect(Collectors.toList()));
result.addAll(list2Filtered.collect(Collectors.toList()));
It's a little confusing at first, but you don't have to create any more classes.
I ended up using a wrapper class, originally suggested by "Oliver Charlesworth" and other people in the comments.

spot the difference between two lists

In java suppose I have 2 lists
List<Object1> list1
List<Object2> list2
object1.getName(); returns a String
object2.getName(); return a String
is there any way to compare the names and get a difference of the two list
those 2 objects are defined in the 3rd party library, and I can't override the equals and compareto methods
I am in favour of googles Guava or commons collections library
but the Sets.symmetricDifference(Set1, Set2) ask for 2 to be passed in,
even i juse Sets.newHashSet(lis1) and Sets.newHashSet(lis2) to create two sets
but still they have difference type of objects in the sets.
or in commons CollectionUtils.disjunction(lis1, list2) the lists still has to contain the same object type
without doing 2 expensive for loops, is there any other way?
First, we'll build two maps, one for each list, mapping names to objects. Then we iterate over the differences between the key sets, processing whichever kind of object had that name. The maps let us avoid scanning through the list looking for the object with that name. (In using Map rather than Multimap, I'm relying on the asker's comment on another answer that within each list, names are unique. If you're still using Java 7, replace the method reference with a Function implementation.)
Map<String, Object1> map1 = Maps.uniqueIndex(list1, Object1::getName);
Map<String, Object2> map2 = Maps.uniqueIndex(list2, Object1::getName);
for (String name : Sets.difference(map1.keySet(), map2.keySet()))
processObject1(map1.get(name));
for (String name : Sets.difference(map2.keySet(), map1.keySet()))
processObject2(map2.get(name));
If all you want to do is build lists or sets of the objects in exactly one list, processObject1 and processObject2 can just add the objects to collections.
uniqueIndex's iteration order is that of the input iterable, and difference returns a SetView with the same iteration order as its first argument, so you can process objects in the order they appeared in the input lists, if that order is relevant to your problem.
Java 8 streams provide basically the same functionality:
Map<String, Object1> map1 = list1.stream().collect(Collectors.toMap(Function.identity(), Object1::getName));
Map<String, Object2> map2 = list2.stream().collect(Collectors.toMap(Function.identity(), Object2::getName));
map1.keySet().stream().filter(n -> !map2.keySet().contains(n)).map(map1::get).forEachOrdered(o1 -> processObject1(o1));
map2.keySet().stream().filter(n -> !map1.keySet().contains(n)).map(map2::get).forEachOrdered(o2 -> processObject1(o2));
Again, you can replace the forEachOrdered call with collect(Collectors.toList()) if you just want to collect the objects.
First you will have to transfor your lists to String based lists:
private static final class FromObject1ToName implements Function<Object1, String> {
#Override
public String apply(Object1 input) {
return input.name;
}
}
The same transformation has to be done for Object2
Then transform the input list:
Collection<String> transformed = Collections2.transform(list1, new FromObject1ToName());
//list1 is a List on Object1
Then create the multiset:
Multiset<String> multiset1 = HashMultiset.create();
multiset1.addAll(transformed);
Then simply do :
Multisets.difference(multiset1, multiset2) // multiset1 is from Object1 and multiset2 is from Object2
This will give you the difference and how many times it differes
If you need to know just the differences, then do the same transform, then load the Collection of strings in a Set adn then do Sets.symmetricDifference
Using Guava, try this. It works for me ->
Multisets.difference(multiset1,multiset2);
How to convert ArrayList to Multiset.
List x = new ArrayList();
x.add(3);.....
Multiset newX = HashMultiset.create();
newX.addAll(x);

how to define an arrayList with two columns in java?

I have a hashMap. Each "Value"is going to be a a list which will be mapped later on with my "Key"s. List is desired to look like this:
[length,time][length,time][length,time]
For example:
Key{srcAddr=x, dstAddr=y, srcPort=12345, dstPort=80}
value{(6523,0.001),(124,0.05), () , (), ...}
I just wonder how can I have a two-col arrayList.
package myclassifier;
import java.util.ArrayList;
public class FlowStatics {
int packetLength;
double timeArrival;
public FlowStatics(int pLength, double tArrival)
{
this.packetLength = pLength;
this.timeArrival = tArrival;
}
}
and here is how I used it:
final ArrayList<FlowStatics> staticsArray = new ArrayList<FlowStatics>();
final HashMap<Flows, ArrayList> myHashMap = new HashMap<Flows, ArrayList>();
FlowStatics flowStatics = new FlowStatics(packetLength,timeArrival);
staticsArray.add(flowStatics);
myHashMap.put(flows, staticsArray);
and here is the part that I am reading it:
Iterator<Flows> iterator = myHashMap.keySet().iterator();
while(iterator.hasNext()){
Flows key = iterator.next();
ArrayList value = myHashMap.get(key);
System.out.println("Fows"+key+"----------"+"Statics"+ value);
Well, your FlowStatics is the correct solution
List<FlowStatics> will give you the "two-column array list".
Update: as of your update, myHashMap.put(flows, flowStatics); is wrong. You are this putting an individual pair, rather than a list in the map. You should use:
staticsArray.add(flowStatics);
myHashMap.put(flows, staticsArray);
A List<E> is an abstraction for a homogeneous list of elements whose type is E. There are some restrictions (e.g. no primitives), but conceptually the type E can be defined to be whatever you want.
Suppose there's an abstraction of Pair<L,R>. Then a List<Pair<L,R>> is still a list of some E, but now that E is a Pair<L,R>. So it's still a "one-column" list, but each element in the list is a "pair", so it's sort of a "two-column" list.
Note that you don't always need a generic Pair<L,R>. Any type E that properly encapsulates all the information can be used in a List<E>.
And by the way, you can have a List<List<E>> too.
See also
Wikipedia/Encapsulation
Related questions
What is the equivalent of the C++ Pair<L,R> in Java?
List of Lists of Lists
Returning values analogy
Often people ask "How can I return two values in Java?". The answer is analogous. You return one value, a new type which encapsulates both information.
So instead of:
// attempt to return two values
// DOES NOT COMPILE
return "James Bond";
return "007";
You do:
return new SecretAgent("James Bond", "007");
Related questions
Using a java method to return multiple values?

Categories