I have a base class: "Working Items". Witch is extended by 3 subclasses: "Bug", "Story", "Feedback". I receive a command from the console (String), to list all types of Working Items, or one specific type (Bug, Story, Feedback).
I have a collection of type WorkingItems, which contains all of the 3 above mention subclasses. How can I validate while looping the collection if the current object is Bug, Story or Feedback (I can't use instanceof)?
I don't want to divide the collection of Working Items into 3 smaller collections of Bugs, Stories, Feedbacks.
private String listAllWorkingItems(StringBuilder result) {
WIMRepository.getWorkItemList().forEach(workItem -> result.append(workItem.toString()));
return result.toString();
}
private String listAllStories(StringBuilder result) {
WIMRepository.getWorkItemList(); //TODO
return result.toString();
}
private String listAllBugs(StringBuilder result) {
WIMRepository.getWorkItemList() //TODO
return result.toString();
}
private String listAllFeedbacks(StringBuilder result) {
WIMRepository.getWorkItemList() //TODO
return result.toString();
}
The result should be like this:
Command: show all
Result: "printing working items collection"
Command: show bugs
Result: "printing only the bugs from working items collection"
Correct me if I am wrong, but I don't see any point in having 3 subclasses.
enum WorkingItemType {
BUG, STORY, FEEDBACK;
}
class WorkingItem {
private WorkingItemType type;
public WorkingItemType getType() {
return type;
}
}
You didn't show why you've chosen that way, and I am assuming that two classes (WorkingItem and WorkingItemType) would be enough, which would make the method listAllBugs as simple as
WIMRepository.getWorkItemList().stream()
.filter(i -> WorkingItemType.BUG.equals(i.getType()))
.forEach(i -> result.append(i.toString()));
Moreover, one general method would be enough.
WIMRepository.getWorkItemList().stream()
.filter(i -> WorkingItemType.valueOf(input.toUpperCase()).equals(i.getType()))
.forEach(i -> result.append(i.toString()));
instanceof is rarely a good choice, so it's nice that the requirements prohibit it.
Related
I would like to check if two lists (let's say, ArrayLists) have exactly the same instance classes, based in an expected List.
To do so, I have built the next method, but I was wondering whether there is another fancy way using certain library, like assertJ.
private void assertConcreteTypes(List actual, List expected) {
for (int i = 0; i < actual.size(); i++){
assertThat(actual.get(i)).isExactlyInstanceOf(expected.get(i).getClass());
}
}
Any suggestion would be more than welcome. Thanks!
You can create a custom Assertj asserter and leverage it for asserting types.
class TypeAssert extends AbstractAssert<TypeAssert, List<?>> {
public TypeAssert(List<?> actual) {
super(actual, TypeAssert.class);
}
public TypeAssert hasElementsOfExactlyTheSameTypeAs(List<?> expected) {
isNotNull();
for (int i = 0; i < actual.size(); i++) {
if (!actual.get(i).getClass().equals(expected.get(i).getClass())) {
failWithMessage("Expected [%s]th element to be of type: %s but was of type: %s",
i, expected.get(i).getClass(), actual.get(i).getClass());
}
}
return this;
}
}
You'll need a static method that will expose the object of our Custom Exporter.
class Assertions {
// static factory method which exposes custom asserted
static TypeAssert assertThat(List<?> actual) {
return new TypeAssert(actual);
}
}
And then you can use the above method for asserting based on type.
List<Object> actual = List.of(new Employee());
List<Object> expected = List.of(new StringBuilder());
Assertions.assertThat(actual).hasElementsOfExactlyTheSameTypeAs(expected);
If you are asserting based on a type only at a very few places then I think the approach you have mentioned is much cleaner and readable. But if you need such assertion at several places then may be creating a custom assertion is a good choice.
You need to take into account lists of different sizes as well as null elements in the lists.
This seems quite readable and caters to these edge cases:
private void assertConcreteTypes(List actual, List expected) {
assertEquals(classes(expected), classes(actual));
}
private List<Class<?>> classes(List<Object> list) {
return list.stream().map(v -> v == null ? null : v.getClass()).collect(Collectors.toList());
}
Hey if anyone has an idea I would be really thankfull.
I'm in a Java stream and i would like to sort my list that i'll be returning.
I need to sort the list via TradPrefis ( MyObject::getTradPrefix ).
But this would be way too easy. Because i want to sort following the number at the end of TradPrefix exampleTradPrefix_[NUMBER TO SORT]
Exemple : hello_1
test_2
...
still_there_22
Here is a piece of code so you can imagine easier.
public LinkedHashSet<WsQuestion> get(String quizId, String companyId) {
LinkedHashSet<QuizQuestionWithQuestion> toReturn = quizQuestionRepository.findAllQuizQuestionWithQuestionByQuizId(quizId);
return (toReturn.stream()
.map(this::createWsQuestion)
.sorted(comparing(WsQuestion::getTradPrefix.toString().length()))
.collect(Collectors.toCollection(LinkedHashSet::new)));
}
One method would simply be to split getTradPrefix().toString() by _ and parse the rightmost value as an int, and use it to sort the Stream:
public LinkedHashSet<WsQuestion> get(String quizId, String companyId) {
LinkedHashSet<QuizQuestionWithQuestion> toReturn = quizQuestionRepository.findAllQuizQuestionWithQuestionByQuizId(quizId);
return toReturn.stream()
.map(this::createWsQuestion)
.sorted(Comparator.comparingInt(question -> {
String[] args = question.getTradPrefix().toString().split("_");
return Integer.parseInt(args[args.length - 1]);
}))
.collect(Collectors.toCollection(LinkedHashSet::new));
}
If I where you I would simply put a method on the WsQuestion class, let's call it sort order:
public int getSortOrder() {
return Integer.valueOf(tradPrefix.substring(tradPrefix.lastIndexOf("_") + 1));
}
The Integer parse is needed since comparing strings would give "11" < "2" (thanks Holger for pointing this out). The lastIndexOf() makes sure that any number of underscores are allowed in tradPrefix, as long as there is at least one.
Then simply create a comparotor by using Comparator.comparingInt()
public LinkedHashSet<WsQuestion> get(String quizId, String companyId) {
LinkedHashSet<QuizQuestionWithQuestion> toReturn = quizQuestionRepository.findAllQuizQuestionWithQuestionByQuizId(quizId);
return (toReturn.stream()
.map(this::createWsQuestion)
.sorted(comparingInt(WsQuestion::getSortOrder))
.collect(Collectors.toCollection(LinkedHashSet::new)));
}
You can make a small Comparator like this:
private static final Comparator<String> questionComparator = Comparator.comparingInt(s -> {
String[] pieces = s.split("_");
return Integer.parseInt(pieces[pieces.length-1]);
});
Then use it in your sorted().
Having a separate Comparator will make your code more readable too, since you will be separating concerns.
return toReturn.stream()
.map(this::createWsQuestion)
.sorted(questionComparator)
.collect(Collectors.toCollection(LinkedHashSet::new));
Let say I have 2 classes:
public class Person
{
private String name;
private int age;
private Contact contact;
//getter & setter
}
public class Contact
{
private String phone;
private String email;
//getter & setter
}
With the classes above, I want to create 2 instances of Person class, with different field value. Then I want to compare some fields of 2 objects with their getter function, but I don't want to compare all fields.
For example, I want to compare the field name and phone, then I will store this 2 getter method to a list like something below:
List<WhatShouldBeTheDataType> funcList = new ArrayList<>();
funcList.add(MyClass::getName);
funcList.add(MyClass::getContact::getPhone) //I know this won't work, what should be the solution?
then loop through the funcList, pass the 2 objects I want to compare into the function, if the value not same, write something into the database. This can be easily done with ordinary if...else... way, but is it possible to do in Java 8 way?
Below is what I want to achieve in if...else... way:
if(person1.getName() != person2.getName())
{
//message format basically is: "fieldName + value of object 1 + value of object 2"
log.append("Name is different: " + person1.getName() + ", " + person2.getName());
}
if(person1.getContact.getPhone() != person2.getContact().getPhone())
{
log.append("Phone is different: " + person1.getContact.getPhone() + ", " + person2.getContact.getPhone());
}
//other if to compare other fields
It looks like Person and MyClass refer to the same thing in your question.
You need a Function<Person,String>, since your functions accept a Person instance and return a String:
List<Function<Person,String>> funcList = new ArrayList<>();
funcList.add(Person::getName);
funcList.add(p -> p.getContact().getPhone());
For the second function, you can't use a method reference, but you can use a lambda expression instead.
Given an instance of Person, you can apply your functions as follows:
Person instance = ...;
for (Function<Person,String> func : funcList) {
String value = func.apply(instance);
}
to complete Eran's code:
boolean isEqual(Person person1, Person person2){
for (Function<Person,String> function:functionList) {
if (!function.apply(person1).equals(function.apply(person2))) return false;
}
return true;
}
then use the returned boolean to check and update your database.
Although you can use a list of functions (as suggested in Eran's answer), using comparators directly is probably more appropriate for your use case.
You can alternatively use a chain of comparators, and then use the result of compare:
Comparator<Person> comparators = Comparator.comparing((Person p) -> p.getName())
.thenComparing((Person p) -> p.getContact().getPhone());
Person p1 = null, p2 = null;
if(0 != comparators.compare(person1, person2)) {
//p1 and p2 are different
}
Even simpler (and more natural, in my opinion), is overriding equals in Person, and checking if(!person1.equals(person2))
Edit (after update of the question):
Here's a version built on a function list, dynamically generating the log content by adding a field name list:
List<Function<Person, String>> functions =
Arrays.asList(Person::getName, p -> p.getContact().getPhone());
List<String> fieldNames = Arrays.asList("Name", "Phone");
IntStream.range(0, functions.size())
.filter(i -> functions.get(i).apply(person1)
.compareTo(functions.get(i).apply(person2)) != 0)
.mapToObj(i -> String.format("%s is different: %s, %s",
fieldNames.get(i),
functions.get(i).apply(person1),
functions.get(i).apply(person2)))
.forEach(log::append);
This rather takes advantage of the fact that String is already comparable, and avoids creating comparators altogether.
Modifying a local variable in forEach gives a compile error:
Normal
int ordinal = 0;
for (Example s : list) {
s.setOrdinal(ordinal);
ordinal++;
}
With Lambda
int ordinal = 0;
list.forEach(s -> {
s.setOrdinal(ordinal);
ordinal++;
});
Any idea how to resolve this?
Use a wrapper
Any kind of wrapper is good.
With Java 10+, use this construct as it's very easy to setup:
var wrapper = new Object(){ int ordinal = 0; };
list.forEach(s -> {
s.setOrdinal(wrapper.ordinal++);
});
With Java 8+, use either an AtomicInteger:
AtomicInteger ordinal = new AtomicInteger(0);
list.forEach(s -> {
s.setOrdinal(ordinal.getAndIncrement());
});
... or an array:
int[] ordinal = { 0 };
list.forEach(s -> {
s.setOrdinal(ordinal[0]++);
});
Note: be very careful if you use a parallel stream. You might not end up with the expected result. Other solutions like Stuart's might be more adapted for those cases.
For types other than int
Of course, this is still valid for types other than int.
For instance, with Java 10+:
var wrapper = new Object(){ String value = ""; };
list.forEach(s->{
wrapper.value += "blah";
});
Or if you're stuck with Java 8 or 9, use the same kind of construct as we did above, but with an AtomicReference...
AtomicReference<String> value = new AtomicReference<>("");
list.forEach(s -> {
value.set(value.get() + s);
});
... or an array:
String[] value = { "" };
list.forEach(s-> {
value[0] += s;
});
This is fairly close to an XY problem. That is, the question being asked is essentially how to mutate a captured local variable from a lambda. But the actual task at hand is how to number the elements of a list.
In my experience, upward of 80% of the time there is a question of how to mutate a captured local from within a lambda, there's a better way to proceed. Usually this involves reduction, but in this case the technique of running a stream over the list indexes applies well:
IntStream.range(0, list.size())
.forEach(i -> list.get(i).setOrdinal(i));
If you only need to pass the value from the outside into the lambda, and not get it out, you can do it with a regular anonymous class instead of a lambda:
list.forEach(new Consumer<Example>() {
int ordinal = 0;
public void accept(Example s) {
s.setOrdinal(ordinal);
ordinal++;
}
});
As the used variables from outside the lamda have to be (implicitly) final, you have to use something like AtomicInteger or write your own data structure.
See
https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#accessing-local-variables.
An alternative to AtomicInteger is to use an array (or any other object able to store a value):
final int ordinal[] = new int[] { 0 };
list.forEach ( s -> s.setOrdinal ( ordinal[ 0 ]++ ) );
But see the Stuart's answer: there might be a better way to deal with your case.
Yes, you can modify local variables from inside lambdas (in the way shown by the other answers), but you should not do it. Lambdas have been made for functional style of programming and this means: No side effects. What you want to do is considered bad style. It is also dangerous in case of parallel streams.
You should either find a solution without side effects or use a traditional for loop.
If you are on Java 10, you can use var for that:
var ordinal = new Object() { int value; };
list.forEach(s -> {
s.setOrdinal(ordinal.value);
ordinal.value++;
});
You can wrap it up to workaround the compiler but please remember that side effects in lambdas are discouraged.
To quote the javadoc
Side-effects in behavioral parameters to stream operations are, in general, discouraged, as they can often lead to unwitting violations of the statelessness requirement
A small number of stream operations, such as forEach() and peek(), can operate only via side-effects; these should be used with care
I had a slightly different problem. Instead of incrementing a local variable in the forEach, I needed to assign an object to the local variable.
I solved this by defining a private inner domain class that wraps both the list I want to iterate over (countryList) and the output I hope to get from that list (foundCountry). Then using Java 8 "forEach", I iterate over the list field, and when the object I want is found, I assign that object to the output field. So this assigns a value to a field of the local variable, not changing the local variable itself. I believe that since the local variable itself is not changed, the compiler doesn't complain. I can then use the value that I captured in the output field, outside of the list.
Domain Object:
public class Country {
private int id;
private String countryName;
public Country(int id, String countryName){
this.id = id;
this.countryName = countryName;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCountryName() {
return countryName;
}
public void setCountryName(String countryName) {
this.countryName = countryName;
}
}
Wrapper object:
private class CountryFound{
private final List<Country> countryList;
private Country foundCountry;
public CountryFound(List<Country> countryList, Country foundCountry){
this.countryList = countryList;
this.foundCountry = foundCountry;
}
public List<Country> getCountryList() {
return countryList;
}
public void setCountryList(List<Country> countryList) {
this.countryList = countryList;
}
public Country getFoundCountry() {
return foundCountry;
}
public void setFoundCountry(Country foundCountry) {
this.foundCountry = foundCountry;
}
}
Iterate operation:
int id = 5;
CountryFound countryFound = new CountryFound(countryList, null);
countryFound.getCountryList().forEach(c -> {
if(c.getId() == id){
countryFound.setFoundCountry(c);
}
});
System.out.println("Country found: " + countryFound.getFoundCountry().getCountryName());
You could remove the wrapper class method "setCountryList()" and make the field "countryList" final, but I did not get compilation errors leaving these details as-is.
To have a more general solution, you can write a generic Wrapper class:
public static class Wrapper<T> {
public T obj;
public Wrapper(T obj) { this.obj = obj; }
}
...
Wrapper<Integer> w = new Wrapper<>(0);
this.forEach(s -> {
s.setOrdinal(w.obj);
w.obj++;
});
(this is a variant of the solution given by Almir Campos).
In the specific case this is not a good solution, as Integer is worse than int for your purpose, anyway this solution is more general I think.
I am looking to implement a sort feature for my address book application.
I want to sort an ArrayList<Contact> contactArray. Contact is a class which contains four fields: name, home number, mobile number and address. I want to sort on name.
How can I write a custom sort function to do this?
Here's a tutorial about ordering objects:
The Java Tutorials - Collections - Object Ordering
Although I will give some examples, I would recommend to read it anyway.
There are various way to sort an ArrayList. If you want to define a natural (default) ordering, then you need to let Contact implement Comparable. Assuming that you want to sort by default on name, then do (nullchecks omitted for simplicity):
public class Contact implements Comparable<Contact> {
private String name;
private String phone;
private Address address;
#Override
public int compareTo(Contact other) {
return name.compareTo(other.name);
}
// Add/generate getters/setters and other boilerplate.
}
so that you can just do
List<Contact> contacts = new ArrayList<Contact>();
// Fill it.
Collections.sort(contacts);
If you want to define an external controllable ordering (which overrides the natural ordering), then you need to create a Comparator:
List<Contact> contacts = new ArrayList<Contact>();
// Fill it.
// Now sort by address instead of name (default).
Collections.sort(contacts, new Comparator<Contact>() {
public int compare(Contact one, Contact other) {
return one.getAddress().compareTo(other.getAddress());
}
});
You can even define the Comparators in the Contact itself so that you can reuse them instead of recreating them everytime:
public class Contact {
private String name;
private String phone;
private Address address;
// ...
public static Comparator<Contact> COMPARE_BY_PHONE = new Comparator<Contact>() {
public int compare(Contact one, Contact other) {
return one.phone.compareTo(other.phone);
}
};
public static Comparator<Contact> COMPARE_BY_ADDRESS = new Comparator<Contact>() {
public int compare(Contact one, Contact other) {
return one.address.compareTo(other.address);
}
};
}
which can be used as follows:
List<Contact> contacts = new ArrayList<Contact>();
// Fill it.
// Sort by address.
Collections.sort(contacts, Contact.COMPARE_BY_ADDRESS);
// Sort later by phone.
Collections.sort(contacts, Contact.COMPARE_BY_PHONE);
And to cream the top off, you could consider to use a generic javabean comparator:
public class BeanComparator implements Comparator<Object> {
private String getter;
public BeanComparator(String field) {
this.getter = "get" + field.substring(0, 1).toUpperCase() + field.substring(1);
}
public int compare(Object o1, Object o2) {
try {
if (o1 != null && o2 != null) {
o1 = o1.getClass().getMethod(getter, new Class[0]).invoke(o1, new Object[0]);
o2 = o2.getClass().getMethod(getter, new Class[0]).invoke(o2, new Object[0]);
}
} catch (Exception e) {
// If this exception occurs, then it is usually a fault of the developer.
throw new RuntimeException("Cannot compare " + o1 + " with " + o2 + " on " + getter, e);
}
return (o1 == null) ? -1 : ((o2 == null) ? 1 : ((Comparable<Object>) o1).compareTo(o2));
}
}
which you can use as follows:
// Sort on "phone" field of the Contact bean.
Collections.sort(contacts, new BeanComparator("phone"));
(as you see in the code, possibly null fields are already covered to avoid NPE's during sort)
In addition to what was already posted by BalusC it may be worth pointing that since Java 8 we can shorten our code and write it like:
Collection.sort(yourList, Comparator.comparing(YourClass::getSomeComparableField));
or since List now have sort method also like
yourList.sort(Comparator.comparing(YourClass::getSomeComparableField));
Explanation:
Since Java 8, functional interfaces (interfaces with only one abstract method - they can have more default or static methods) can be easily implemented using:
lambdas arguments -> body
or method references source::method.
Since Comparator<T> has only one abstract method int compare(T o1, T o2) it is functional interface.
So instead of (example from #BalusC answer)
Collections.sort(contacts, new Comparator<Contact>() {
public int compare(Contact one, Contact other) {
return one.getAddress().compareTo(other.getAddress());
}
});
we can reduce this code to:
Collections.sort(contacts, (Contact one, Contact other) -> {
return one.getAddress().compareTo(other.getAddress());
});
We can simplify this (or any) lambda by skipping
argument types (Java will infer them based on method signature)
or {return ... }
So instead of
(Contact one, Contact other) -> {
return one.getAddress().compareTo(other.getAddress();
}
we can write
(one, other) -> one.getAddress().compareTo(other.getAddress())
Also now Comparator has static methods like comparing(FunctionToComparableValue) or comparing(FunctionToValue, ValueComparator) which we could use to easily create Comparators which should compare some specific values from objects.
In other words we can rewrite above code as
Collections.sort(contacts, Comparator.comparing(Contact::getAddress));
//assuming that Address implements Comparable (provides default order).
This page tells you all you need to know about sorting collections, such as ArrayList.
Basically you need to
make your Contact class implement the Comparable interface by
creating a method public int compareTo(Contact anotherContact) within it.
Once you do this, you can just call Collections.sort(myContactList);,
where myContactList is ArrayList<Contact> (or any other collection of Contact).
There's another way as well, involving creating a Comparator class, and you can read about that from the linked page as well.
Example:
public class Contact implements Comparable<Contact> {
....
//return -1 for less than, 0 for equals, and 1 for more than
public compareTo(Contact anotherContact) {
int result = 0;
result = getName().compareTo(anotherContact.getName());
if (result != 0)
{
return result;
}
result = getNunmber().compareTo(anotherContact.getNumber());
if (result != 0)
{
return result;
}
...
}
}
BalusC and bguiz have already given very complete answers on how to use Java's built-in Comparators.
I just want to add that google-collections has an Ordering class which is more "powerful" than the standard Comparators.
It might be worth checking out. You can do cool things such as compounding Orderings, reversing them, ordering depending on a function's result for your objects...
Here is a blog post that mentions some of its benefits.
You need make your Contact classes implement Comparable, and then implement the compareTo(Contact) method. That way, the Collections.sort will be able to sort them for you. Per the page I linked to, compareTo 'returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.'
For example, if you wanted to sort by name (A to Z), your class would look like this:
public class Contact implements Comparable<Contact> {
private String name;
// all the other attributes and methods
public compareTo(Contact other) {
return this.name.compareTo(other.name);
}
}
By using lambdaj you can sort a collection of your contacts (for example by their name) as it follows
sort(contacts, on(Contact.class).getName());
or by their address:
sort(contacts, on(Contacts.class).getAddress());
and so on. More in general, it offers a DSL to access and manipulate your collections in many ways, like filtering or grouping your contacts based on some conditions, aggregate some of their property values, etc.
Ok, I know this was answered a long time ago... but, here's some new info:
Say the Contact class in question already has a defined natural ordering via implementing Comparable, but you want to override that ordering, say by name. Here's the modern way to do it:
List<Contact> contacts = ...;
contacts.sort(Comparator.comparing(Contact::getName).reversed().thenComparing(Comparator.naturalOrder());
This way it will sort by name first (in reverse order), and then for name collisions it will fall back to the 'natural' ordering implemented by the Contact class itself.
The Collections.sort is a good sort implementation. If you don't have The comparable implemented for Contact, you will need to pass in a Comparator implementation
Of note:
The sorting algorithm is a modified mergesort (in which the merge is omitted if the highest element in the low sublist is less than the lowest element in the high sublist). This algorithm offers guaranteed n log(n) performance. The specified list must be modifiable, but need not be resizable. This implementation dumps the specified list into an array, sorts the array, and iterates over the list resetting each element from the corresponding position in the array. This avoids the n2 log(n) performance that would result from attempting to sort a linked list in place.
The merge sort is probably better than most search algorithm you can do.
I did it by the following way.
number and name are two arraylist. I have to sort name .If any change happen to name arralist order then the number arraylist also change its order.
public void sortval(){
String tempname="",tempnum="";
if (name.size()>1) // check if the number of orders is larger than 1
{
for (int x=0; x<name.size(); x++) // bubble sort outer loop
{
for (int i=0; i < name.size()-x-1; i++) {
if (name.get(i).compareTo(name.get(i+1)) > 0)
{
tempname = name.get(i);
tempnum=number.get(i);
name.set(i,name.get(i+1) );
name.set(i+1, tempname);
number.set(i,number.get(i+1) );
number.set(i+1, tempnum);
}
}
}
}
}
use this method:
private ArrayList<myClass> sortList(ArrayList<myClass> list) {
if (list != null && list.size() > 1) {
Collections.sort(list, new Comparator<myClass>() {
public int compare(myClass o1, myClass o2) {
if (o1.getsortnumber() == o2.getsortnumber()) return 0;
return o1.getsortnumber() < o2.getsortnumber() ? 1 : -1;
}
});
}
return list;
}
`
and use: mySortedlist = sortList(myList);
No need to implement comparator in your class.
If you want inverse order swap 1 and -1
With java 8 feature
List<Contact> contact = contactArray.stream().sorted((c1, c2) -> ((c1.getName().compareTo(c2.getName())))).collect(Collectors.toList());
You shoud use the Arrays.sort function. The containing classes should implement Comparable.