Unit Test : check if a particular Comparable has been called - java

Consider this method, I want to write a unit test for it.
public void update(List toUpdate) {
//...
Collections.sort(toUpdate, new SpecificComparator());
//...
}
I would like to ensure we are ordering the given list using the SpecificComparator implementation. Anyway to do this?
I was thinking of using a factory to retrieve the SpecificComparator implementation, so that I could verify with Mockito that we are calling the factory, but that would not ensure me that the list is ordered given this comparator, we could imagine that someone is calling the factory in the method but not ordering the list with it...
Or, I could also verify the ordering of the toUpdate list object, order another list with the same comparator in my unit test and check they are ordered in the same way?

So many answer, all containing one piece of "truth" but missing others. Sad.
Thus: you should absolutely avoid using a mock to test your comparator on sorting calls. Instead you do test two aspects:
You write unit tests for your comparator. Heck, the comparator has one method; with very clear defined semantics. You focus on writing unit tests to test that thing in isolation. Because that is A) easy to do and B) the whole idea of unit tests: you do as much testing on the smallest unit as possible.
You write a few unit tests to ensure your "plumbing" is correct. And you don't mock anything in there. You make sure that a list with known content gets sorted using your comparator; and then you assertThat(actualList, is(expectedList));
Long story short: the Comparator itself is a unit. You start testing that thing; with all corner cases and what not. Then you make sure that the methods that are supposed to sort lists do come back with a sorted list.
And if your code is such that you still need to think about mocking then chances are that your design could be improved to be easy-to-test (and not hard-to-test).

I would say that this isn't a reasonable thing to want to do.
A unit test should only be testing a class at its public interfaces, and since the comparator is a hidden implementation detail, it's none of the unit test's business how the sort order is achieved.
This means that if the implementation changes some day, to achieve the same sort order by some other means, the test will continue to pass -- and that's how things should be.
So, you should test that the output is sorted in the order you expect, but you should not assert anything about an object that's private to the method.
Of course, if you made the Comparator part of the class's API, it would be a different matter:
public class Updater {
private final Comparator sortComparator;
public Updater(Comparator sortComparator) {
this.sortComparator = sortComparator;
}
public void update(List toUpdate) {
//...
Collections.sort(toUpdate, sortComparator);
//...
}
}
... then it would be appropriate to pass a mock Comparator to the constructor, and assert that it has been used -- because now it's of interest to the caller, that the Comparator it's passed in is the one that's being used, and therefore something that should be tested.

The idea is to have lists sorted already as IF the particular comparator was used. This is important because the behavior of the comparator should not change and if it does then new requirements should be written which means your lists used for testing must change. However for the most part you shouldn't be changing the behavior of a single comparator too often. Therefore you should create lists that are sorted already as if the comparator was used, then you can call the following method
public boolean unitTestComparator(List sorted, Comparator comp)
{
List shuffled = new List(sorted);
Collections.shuffle(shuffled);
return sorted.equals(Collections.sort(shuffled, comp));
}
Now you can use multiple tests for various lists to exercise all edge cases for your different comparators. The key to all this is that you know what the lists should look like after the comparator is used, the only tedious part is finding all the edge cases per comparator. You could also run a for loop and on this method to test it as many times as you'd like because you provide the correct format of the list to the method. All it does is randomize the shuffle.
Note this is random testing, you could also add another parameter to the method which could be the list shuffled in the way you want, to find particular edge cases.

which you mentioned above I called it as a "cross test", but I think it is unnecessary, if you really want to achieve it, I give it to you, and let you to think it more. test update a list twice with different comparators and assert the results is you expected.
public class CrossingTest {
#Test
public void sortWithSpecificComparator() throws Throwable {
List<Integer> list = asList(0, 1, 2);
List<Integer> reversedList = asList(2, 1, 0);
Comparator<Integer> originalOrder = (a, b) -> a < b ? -1 : 1;
assertThat(update(list, with(originalOrder)), equalTo(list));
assertThat(update(list, with(originalOrder.reversed()))
, equalTo(reversedList));
}
private List<Integer> update(List<Integer> input, Comparator comparator) {
List<Integer> list = new ArrayList<>(input);
SUT it = new SUT(comparator);
it.update(list);
return list;
}
private Comparator with(Comparator comparator) { return comparator; }
}
class SUT {
private Comparator comparator;
public SUT(Comparator comparator) {
this.comparator = comparator;
}
public void update(List toUpdate) {
Collections.sort(toUpdate, comparator);
}
}

Although you can mock your new object with the following:
SpecificComparator comparator = Mockito.mock(SpecificComparator.class);
Mockito.whenNew(SpecificComparator.class).thenReturn(comparator);
Best approach is always to check the ordering of elements after the method is called rather than checking when/how comparator is used. You can do that by using equals method of List, this is what it says:
Compares the specified object with this list for equality. Returns
true if and only if the specified object is also a list, both lists
have the same size, and all corresponding pairs of elements in the two
lists are equal.

Related

Check all values of object in list are unique [duplicate]

I want to remove duplicates from a list but what I am doing is not working:
List<Customer> listCustomer = new ArrayList<Customer>();
for (Customer customer: tmpListCustomer)
{
if (!listCustomer.contains(customer))
{
listCustomer.add(customer);
}
}
Assuming you want to keep the current order and don't want a Set, perhaps the easiest is:
List<Customer> depdupeCustomers =
new ArrayList<>(new LinkedHashSet<>(customers));
If you want to change the original list:
Set<Customer> depdupeCustomers = new LinkedHashSet<>(customers);
customers.clear();
customers.addAll(dedupeCustomers);
If the code in your question doesn't work, you probably have not implemented equals(Object) on the Customer class appropriately.
Presumably there is some key (let us call it customerId) that uniquely identifies a customer; e.g.
class Customer {
private String customerId;
...
An appropriate definition of equals(Object) would look like this:
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof Customer)) {
return false;
}
Customer other = (Customer) obj;
return this.customerId.equals(other.customerId);
}
For completeness, you should also implement hashCode so that two Customer objects that are equal will return the same hash value. A matching hashCode for the above definition of equals would be:
public int hashCode() {
return customerId.hashCode();
}
It is also worth noting that this is not an efficient way to remove duplicates if the list is large. (For a list with N customers, you will need to perform N*(N-1)/2 comparisons in the worst case; i.e. when there are no duplicates.) For a more efficient solution you could use a HashSet to do the duplicate checking. Another option would be to use a LinkedHashSet as explained in Tom Hawtin's answer.
java 8 update
you can use stream of array as below:
Arrays.stream(yourArray).distinct()
.collect(Collectors.toList());
Does Customer implement the equals() contract?
If it doesn't implement equals() and hashCode(), then listCustomer.contains(customer) will check to see if the exact same instance already exists in the list (By instance I mean the exact same object--memory address, etc). If what you are looking for is to test whether or not the same Customer( perhaps it's the same customer if they have the same customer name, or customer number) is in the list already, then you would need to override equals() to ensure that it checks whether or not the relevant fields(e.g. customer names) match.
Note: Don't forget to override hashCode() if you are going to override equals()! Otherwise, you might get trouble with your HashMaps and other data structures. For a good coverage of why this is and what pitfalls to avoid, consider having a look at Josh Bloch's Effective Java chapters on equals() and hashCode() (The link only contains iformation about why you must implement hashCode() when you implement equals(), but there is good coverage about how to override equals() too).
By the way, is there an ordering restriction on your set? If there isn't, a slightly easier way to solve this problem is use a Set<Customer> like so:
Set<Customer> noDups = new HashSet<Customer>();
noDups.addAll(tmpListCustomer);
return new ArrayList<Customer>(noDups);
Which will nicely remove duplicates for you, since Sets don't allow duplicates. However, this will lose any ordering that was applied to tmpListCustomer, since HashSet has no explicit ordering (You can get around that by using a TreeSet, but that's not exactly related to your question). This can simplify your code a little bit.
List → Set → List (distinct)
Just add all your elements to a Set: it does not allow it's elements to be repeated. If you need a list afterwards, use new ArrayList(theSet) constructor afterwards (where theSet is your resulting set).
I suspect you might not have Customer.equals() implemented properly (or at all).
List.contains() uses equals() to verify whether any of its elements is identical to the object passed as parameter. However, the default implementation of equals tests for physical identity, not value identity. So if you have not overwritten it in Customer, it will return false for two distinct Customer objects having identical state.
Here are the nitty-gritty details of how to implement equals (and hashCode, which is its pair - you must practically always implement both if you need to implement either of them). Since you haven't shown us the Customer class, it is difficult to give more concrete advice.
As others have noted, you are better off using a Set rather than doing the job by hand, but even for that, you still need to implement those methods.
private void removeTheDuplicates(List<Customer>myList) {
for(ListIterator<Customer>iterator = myList.listIterator(); iterator.hasNext();) {
Customer customer = iterator.next();
if(Collections.frequency(myList, customer) > 1) {
iterator.remove();
}
}
System.out.println(myList.toString());
}
The "contains" method searched for whether the list contains an entry that returns true from Customer.equals(Object o). If you have not overridden equals(Object) in Customer or one of its parents then it will only search for an existing occurrence of the same object. It may be this was what you wanted, in which case your code should work. But if you were looking for not having two objects both representing the same customer, then you need to override equals(Object) to return true when that is the case.
It is also true that using one of the implementations of Set instead of List would give you duplicate removal automatically, and faster (for anything other than very small Lists). You will still need to provide code for equals.
You should also override hashCode() when you override equals().
Nearly all of the above answers are right but what I suggest is to use a Map or Set while creating the related list, not after to gain performance. Because converting a list to a Set or Map and then reconverting it to a List again is a trivial work.
Sample Code:
Set<String> stringsSet = new LinkedHashSet<String>();//A Linked hash set
//prevents the adding order of the elements
for (String string: stringsList) {
stringsSet.add(string);
}
return new ArrayList<String>(stringsSet);
Two suggestions:
Use a HashSet instead of an ArrayList. This will speed up the contains() checks considerably if you have a long list
Make sure Customer.equals() and Customer.hashCode() are implemented properly, i.e. they should be based on the combined values of the underlying fields in the customer object.
As others have mentioned, you are probably not implementing equals() correctly.
However, you should also note that this code is considered quite inefficient, since the runtime could be the number of elements squared.
You might want to consider using a Set structure instead of a List instead, or building a Set first and then turning it into a list.
The cleanest way is:
List<XXX> lstConsultada = dao.findByPropertyList(YYY);
List<XXX> lstFinal = new ArrayList<XXX>(new LinkedHashSet<GrupoOrigen>(XXX));
and override hascode and equals over the Id's properties of each entity
IMHO best way how to do it these days:
Suppose you have a Collection "dups" and you want to create another Collection containing the same elements but with all duplicates eliminated. The following one-liner does the trick.
Collection<collectionType> noDups = new HashSet<collectionType>(dups);
It works by creating a Set which, by definition, cannot contain duplicates.
Based on oracle doc.
The correct answer for Java is use a Set. If you already have a List<Customer> and want to de duplicate it
Set<Customer> s = new HashSet<Customer>(listCustomer);
Otherise just use a Set implemenation HashSet, TreeSet directly and skip the List construction phase.
You will need to override hashCode() and equals() on your domain classes that are put in the Set as well to make sure that the behavior you want actually what you get. equals() can be as simple as comparing unique ids of the objects to as complex as comparing every field. hashCode() can be as simple as returning the hashCode() of the unique id' String representation or the hashCode().
Using java 8 stream api.
List<String> list = new ArrayList<>();
list.add("one");
list.add("one");
list.add("two");
System.out.println(list);
Collection<String> c = list.stream().collect(Collectors.toSet());
System.out.println(c);
Output:
Before values : [one, one, two]
After Values : [one, two]

Why doesn't invalid compareTo cause Collections.sort to crash?

Consider the following compareTo method, implementing the Comparable<T> interface.:
#Override
public int compareTo(MyObject o)
{
if (o.value.equals(value)
return 0;
return 1;
}
Apparantly, the programmer implemented the compareTo as if it was equals(). Obviously a mistake. I would expect this to cause Collections.sort() to crash, but it doesn't. Instead it will just give an arbitrairy result: the sorted result is dependant on the initial ordering.
public class MyObject implements Comparable<MyObject>
{
public static void main(String[] args)
{
List<MyObject> objects =
Arrays.asList(new MyObject[] {
new MyObject(1), new MyObject(2), new MyObject(3)
});
Collections.sort(objects);
System.out.println(objects);
List<MyObject> objects2 =
Arrays.asList(new MyObject[] {
new MyObject(3), new MyObject(1), new MyObject(2)
});
Collections.sort(objects2);
System.out.println(objects2);
}
public int value;
public MyObject(int value)
{
this.value = value;
}
#Override
public int compareTo(MyObject o)
{
if (value == o.value)
return 0;
return 1;
}
public String toString()
{
return "" + value;
}
}
Result:
[3, 2, 1]
[2, 1, 3]
Can we come up with a use case for this curious implementation of the compareTo, or is it always invalid. And in case of the latter, should it throw an exception, or perhaps not even compile?
There's no reason for it to crash or throw an exception.
You're required to fulfil the contract when you implement the method, but if you don't, it just means that you'll get arbitrary results from anything that relies on it. Nothing is going to go out of its way to check the correctness of your implementation, because that would just slow everything down.
A sorting algorithm's efficiency is defined in terms of the number of comparisons it makes. That means that it's not going to add in extra comparisons just to check that your implementation is consistent, any more than a HashMap is going to call .hashcode() on everything twice just to check it gives the same result both times.
If it happens to spot a problem during the course of sorting, then it might throw an exception; but don't rely on it.
Violating the contract of Comparable or Comparator does not necessarily result in an exception. The sort method won’t spend additional efforts to detect such a situation. Therefore, it might result in an inconsistent order, an apparently correct result or in an exception being thrown.
The actual behavior depends on the input data and the current implementation. E.g. Java 7 introduced TimSort in it’s sort implementation which is more likely to throw an exception for inconsistent Comparable or Comparator implementations than the implementations of earlier Java releases. This might spot errors that remained undetected when using previous Java versions, however, that’s not a feature to aid debugging in the first place, it’s just a side-effect of more sophisticated optimizations.
Note that it isn’t entirely impossible for a compiler or code audit tool to detect asymmetrical behavior of a compare method for simple cases like in your question. However, as far as I know, there are no compilers performing such a check automatically. If you want to be on the safe side, you should always implement unit tests for classes implementing Comparable or Comparator.
According to the documentation, Collections.sort() uses a variant of merge sort, which divides the list into multiple sublists and then repeatedly merge those lists repeatedly; the sorting part is done during the merging of those lists; if your comparison method is arbitrary, what will happen is that this merging will be done in an arbitrary order.
As a result of this every element is bigger than all the other elements.
Depending on the mathematical group or order you want to represent it may be a valid case and therefore there is no reason to throw any errors. However the example you show does not represent the natural ordering of numbers as you know them by standard.
The presented order is not total.
==EDIT==
Thanks for the comment, indeed it is not allowed by the specification to use the compareTo method to implement non-total or non-antisymmetric orders.

How to JUnit test that two List<E> contain the same elements in the same order?

Context
I am writing a simple JUnit test for the MyObject class.
A MyObject can be created from a static factory method that takes a varargs of String.
MyObject.ofComponents("Uno", "Dos", "Tres");
At any time during the existence of MyObject, clients can inspect the parameters it was created by in the form of a List<E>, through the .getComponents() method.
myObject.ofComponents(); // -> List<String>: { "Uno", "Dos", "Tres" }
In other words, a MyObject both remembers and exposes the list of parameters that brought it into existence. More details about this contract:
The order of getComponents will be the same as the one chosen for object creation
Duplicate subsequent String components are allowed and retained in order
Behaviour on null is undefined (other code guarantees no null gets to the factory)
There are no ways to alter the list of components after object instantiation
I am writing a simple test that creates a MyObject from a list of String and checks that it can return the same list via .getComponents(). I do this immediately but this is supposed to happen at a distance in a realistic code path.
Code
Here my attempt:
List<String> argumentComponents = Lists.newArrayList("One", "Two", "Three");
List<String> returnedComponents =
MyObject.ofComponents(
argumentComponents.toArray(new String[argumentComponents.size()]))
.getComponents();
assertTrue(Iterables.elementsEqual(argumentComponents, returnedComponents));
Question
Is Google Guava Iterables.elementsEqual() the best way, provided I have the library in my build path, to compare those two lists? this is something I have been agonizing about; should I use this helper method which goes over an Iterable<E>.. check size and then iterate running .equals().. or any other of the methods that an Internet search suggests? what's the canonical way to compare lists for unit tests?
Optional insights I would love to get
Is the method test designed reasonably? I am not an expert in JUnit!
Is .toArray() the best way to convert a List<E> to a varargs of E?
Why not simply use List#equals?
assertEquals(argumentComponents, imapPathComponents);
Contract of List#equals:
two lists are defined to be equal if they contain the same elements in the same order.
I prefer using Hamcrest because it gives much better output in case of a failure
Assert.assertThat(listUnderTest,
IsIterableContainingInOrder.contains(expectedList.toArray()));
Instead of reporting
expected true, got false
it will report
expected List containing "1, 2, 3, ..." got list containing "4, 6, 2, ..."
IsIterableContainingInOrder.contain
Hamcrest
According to the Javadoc:
Creates a matcher for Iterables that matches when a single pass over the examined Iterable yields a series of items, each logically equal to the corresponding item in the specified items. For a positive match, the examined iterable must be of the same length as the number of specified items
So the listUnderTest must have the same number of elements and each element must match the expected values in order.
The equals() method on your List implementation should do elementwise comparison, so
assertEquals(argumentComponents, returnedComponents);
is a lot easier.
org.junit.Assert.assertEquals() and org.junit.Assert.assertArrayEquals() do the job.
To avoid next questions: If you want to ignore the order put all elements to set and then compare: Assert.assertEquals(new HashSet<String>(one), new HashSet<String>(two))
If however you just want to ignore duplicates but preserve the order wrap you list with LinkedHashSet.
Yet another tip. The trick Assert.assertEquals(new HashSet<String>(one), new HashSet<String>(two)) works fine until the comparison fails. In this case it shows you error message with to string representations of your sets that can be confusing because the order in set is almost not predictable (at least for complex objects). So, the trick I found is to wrap the collection with sorted set instead of HashSet. You can use TreeSet with custom comparator.
For excellent code-readability, Fest Assertions has nice support for asserting lists
So in this case, something like:
Assertions.assertThat(returnedComponents).containsExactly("One", "Two", "Three");
Or make the expected list to an array, but I prefer the above approach because it's more clear.
Assertions.assertThat(returnedComponents).containsExactly(argumentComponents.toArray());
assertTrue()/assertFalse() : to use only to assert boolean result returned
assertTrue(Iterables.elementsEqual(argumentComponents,
returnedComponents));
You want to use Assert.assertTrue() or Assert.assertFalse() as the method under test returns a boolean value.
As the method returns a specific thing such as a List that should contain some expected elements, asserting with assertTrue() in this way : Assert.assertTrue(myActualList.containsAll(myExpectedList)
is an anti pattern.
It makes the assertion easy to write but as the test fails, it also makes it hard to debug because the test runner will only say to you something like :
expected true but actual is false
Assert.assertEquals(Object, Object) in JUnit4 or Assertions.assertIterableEquals(Iterable, Iterable) in JUnit 5 : to use only as both equals() and toString() are overrided for the classes (and deeply) of the compared objects
It matters because the equality test in the assertion relies on equals() and the test failure message relies on toString() of the compared objects.
As String overrides both equals() and toString(), it is perfectly valid to assert the List<String> with assertEquals(Object,Object).
And about this matter : you have to override equals() in a class because it makes sense in terms of object equality, not only to make assertions easier in a test with JUnit.
To make assertions easier you have other ways (that you can see in the next points of the answer).
Is Guava a way to perform/build unit test assertions ?
Is Google Guava Iterables.elementsEqual() the best way, provided I have the library in my build path, to compare those two lists?
No it is not. Guava is not an library to write unit test assertions.
You don't need it to write most (all I think) of unit tests.
What's the canonical way to compare lists for unit tests?
As a good practice I favor assertion/matcher libraries.
I cannot encourage JUnit to perform specific assertions because this provides really too few and limited features : it performs only an assertion with a deep equals.
Sometimes you want to allow any order in the elements, sometimes you want to allow that any elements of the expected match with the actual, and so for...
So using a unit test assertion/matcher library such as Hamcrest or AssertJ is the correct way.
The actual answer provides a Hamcrest solution. Here is a AssertJ solution.
org.assertj.core.api.ListAssert.containsExactly() is what you need : it verifies that the actual group contains exactly the given values and nothing else, in order as stated :
Verifies that the actual group contains exactly the given values and
nothing else, in order.
Your test could look like :
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
#Test
void ofComponent_AssertJ() throws Exception {
MyObject myObject = MyObject.ofComponents("One", "Two", "Three");
Assertions.assertThat(myObject.getComponents())
.containsExactly("One", "Two", "Three");
}
A AssertJ good point is that declaring a List as expected is needless : it makes the assertion straighter and the code more readable :
Assertions.assertThat(myObject.getComponents())
.containsExactly("One", "Two", "Three");
And if the test fails :
// Fail : Three was not expected
Assertions.assertThat(myObject.getComponents())
.containsExactly("One", "Two");
you get a very clear message such as :
java.lang.AssertionError:
Expecting:
<["One", "Two", "Three"]>
to contain exactly (and in same order):
<["One", "Two"]>
but some elements were not expected:
<["Three"]>
Assertion/matcher libraries are a must because these will really further
Suppose that MyObject doesn't store Strings but Foos instances such as :
public class MyFooObject {
private List<Foo> values;
#SafeVarargs
public static MyFooObject ofComponents(Foo... values) {
// ...
}
public List<Foo> getComponents(){
return new ArrayList<>(values);
}
}
That is a very common need.
With AssertJ the assertion is still simple to write. Better you can assert that the list content are equal even if the class of the elements doesn't override equals()/hashCode() while JUnit ways require that :
import org.assertj.core.api.Assertions;
import static org.assertj.core.groups.Tuple.tuple;
import org.junit.jupiter.api.Test;
#Test
void ofComponent() throws Exception {
MyFooObject myObject = MyFooObject.ofComponents(new Foo(1, "One"), new Foo(2, "Two"), new Foo(3, "Three"));
Assertions.assertThat(myObject.getComponents())
.extracting(Foo::getId, Foo::getName)
.containsExactly(tuple(1, "One"),
tuple(2, "Two"),
tuple(3, "Three"));
}
My answer about whether Iterables.elementsEqual is best choice:
Iterables.elementsEqual is enough to compare 2 Lists.
Iterables.elementsEqual is used in more general scenarios, It accepts more general types: Iterable. That is, you could even compare a List with a Set. (by iterate order, it is important)
Sure ArrayList and LinkedList define equals pretty good, you could call equals directly. While when you use a not well defined List, Iterables.elementsEqual is the best choice. One thing should be noticed: Iterables.elementsEqual does not accept null
To convert List to array: Iterables.toArray is easer.
For unit test, I recommend add empty list to your test case.
Some of the solutions make sense and I agree with them. But for me, I would use assertEquals but I would sort the two lists.
assertEquals(sortedExpectedList, sortedActualList);
It is simple and the output still gives you the diff between the actual and the expected.

How to compare equality between EObject when they contained unordered EList?

I'm still a novice in EMF so maybe my question doesn't really make sense or I assume wrong things.
I'm working on a model-driven project, so I defined an ecore metamodel and generated the associate code. Now I'm currently trying to make unit tests and I need to be able to test equality between objects and more particularly between objects which extend EObject.
I tried to use EcoreUtil.equals() to make my tests but it always returns false as my objects contains references in lists (class EList) that are not ordered the same way. However, I explicitly defined in my metamodel that references are not ordered: I want to use them more like Set than List.
So, I finally decided to implements my own equals methods in my genereated *Impl Class, even if its discouraged in the javadoc, but it there another way, more elegant, to test the structural equality between EMF objects without taking into account the order of lists?
Thanks!
You can implement your own class of utilities where you code your own comparison for unordered lists using the EObject default equals method.
You can base your implementation in the
EqualityHelper.equals(List list1, List list2) using list "contains" instead of going by index as that method does.
I wrote the following utility methods in Xtend:
static def boolean equalsTo(Set<? extends EObject> eObjectSet1, Set<? extends EObject> eObjectSet2) {
contains(eObjectSet1, eObjectSet2) && contains(eObjectSet2, eObjectSet1)
}
static def boolean contains(Set<? extends EObject> eObjectSet1, Set<? extends EObject> eObjectSet2) {
eObjectSet1.forall[eObject1 | eObjectSet2.exists[eObject2 | EcoreUtil.equals(eObject1, eObject2)]]
}
This could probably be solved by using an EMap, with the element of the former list as the key and an arbitrary other type, for example EString, as value.
If an element should be in the set, map it to the empty string.
EMap<SomeClass, EString> someSet;
If the list could contain multiple elements, map it to and integer that gives the number of elements.

Return sorted list in Java

I am coding something like this:
List<Bean> beans = service.findBeans();
Collections.sort(beans, new BeanComparator());
return beans;
It works perfectly. What I am looking for is a shortcut to do this with just one line:
return somelibrary.Collections.sort(service.findBeans(), new BeanComparator());
Or:
return somelibrary.newList(service.findBeans(), new BeanComparator());
Note that it is required a mutable list.
This is one line:
List<Bean> beans = service.findBeans(); Collections.sort(beans, new BeanComparator()); return beans;
But more seriously, Java isn't really the right language for one-liners. Also, just because something is a one-liner doesn't mean that it is any better. For example, I was initially surprised to discover that this:
return condition ? a : b;
Creates a longer bytecode than
if( condition )
return a;
else
return b;
But that's just how the language and compiler are.
If you insist on your one-liner, Guava's Ordering can do it:
return Ordering.from( new BeanComparator() ).sortedCopy( service.findBeans() );
The returned list is modifiable, serializable, and has random access.
Efficiency-wise I think there's a bit of a waste in terms of overhead. And also you're now dependent on a 3rd-party library. You'd be essentially using very powerful tools for a very simple task. It's overkill if this is all you're using it for.
I believe the following function will yield the results you want. Just put it in the class of your choice.
public static <T> List<T> sort(List<T> list, Comparator<? super T> compare) {
Collections.sort(list, compare);
return list;
}
You could use apache CollectionUtils to collate the list with a comparator and an empty list.
CollectionUtils.collate(service.findBeans().iterator(),Collections.EMPTY_LIST.iterator(),new beanComparator())
CollectionUtils really should add a utility method that returns the sorted list...
Classic rebut to use-more-lines is LOGGING. You logging for readability purposes should not take up more than one line. Logging is static noise when you're trying to find out what code is actually doing, yet logging is fairly critical.
So logging should be compact (in one line) and quiet (should not throw exceptions/be nullsafe) and should be performant (if turned off should not introduce excess processing beyond isDebugOn() check.
Second rebut is fluent interfaces such as JOOQ which are becoming much more prevalent.
I guess if you have no duplicates and don't mind hacky code you could use:
return new ArrayList<Bean>(new TreeSet<Bean>(service.findBeans()));
I think the original question posted is valid. Because the "Collections.sort(..)" method has the intended side-effect of sorting the passed-in Collection, if you wanted to maintain your original Collection, you'd have to do the following:
List<Bean> beans = service.findBeans();
List<Bean> sortedBeans = new ArrayList<Bean>(beans);
Collections.sort(sortedBeans, new BeanComparator());
return sortedBeans;
In the case above, it's probably not that big a deal that we sort the Collection returned by a service method. But, what if the Collection we are sorting was a method parameter, and the caller did not want the Collection passed-in sorted?
I usually prefer to have methods without consequences.
Since "Collections.sort(..)" affects the list, I have to write the following code:
public void doSomethingWithBeansInOrder(List<Bean> beans) {
Collection<Bean> sortedBeans = new ArrayList<Bean>(beans);
Collections.sort(sortedBeans, ...comparator...;
for (Bean bean : sortedBeans) {
.. do something
}
}
I find the definition of "sortedBeans" ugly.
If "(Collections.sort(..)" (or something like it), returned a new Collection and did not affect the passed-in Collection, I could write:
public void doSomethingWithBeansInOrder(List<Bean> beans) {
for (Bean bean : Collections.sort(beans, ...comparator...) {
.. do something
}
}
The answer for Guava's Ordering is best, in my opinion.
To start with, Java 8 introduced the sort() metod on the List interface. So, an example of sorting the actual list would be:
List<Integer> integerList = Arrays.asList(3, 2, 1);
integerList.sort(Comparator.naturalOrder());
return integerList;
Here, I used the pre-defined naturalOrder() comparator, which in turn relies on Comparable, but a custom comparator can also be used. This still requires two statements.
However, if the desired behaviour is to create a new sorted list, and leave the orignial as it was before, I guess a stream would be the easiest way to do it:
integerList.stream().sorted(Comparator.naturalOrder()).collect(Collectors.toList());
The same thing as above applies to the comparator here.

Categories