I have a List<String> object that contains country names. How can I sort this list alphabetically?
Assuming that those are Strings, use the convenient static method sort:
Collections.sort(listOfCountryNames)
Solution with Collections.sort
If you are forced to use that List, or if your program has a structure like
Create List
Add some country names
sort them once
never change that list again
then Thilos answer will be the best way to do it. If you combine it with the advice from Tom Hawtin - tackline, you get:
java.util.Collections.sort(listOfCountryNames, Collator.getInstance());
Solution with a TreeSet
If you are free to decide, and if your application might get more complex, then you might change your code to use a TreeSet instead. This kind of collection sorts your entries just when they are inserted. No need to call sort().
Collection<String> countryNames =
new TreeSet<String>(Collator.getInstance());
countryNames.add("UK");
countryNames.add("Germany");
countryNames.add("Australia");
// Tada... sorted.
Side note on why I prefer the TreeSet
This has some subtle, but important advantages:
It's simply shorter. Only one line shorter, though.
Never worry about is this list really sorted right now becaude a TreeSet is always sorted, no matter what you do.
You cannot have duplicate entries. Depending on your situation this may be a pro or a con. If you need duplicates, stick to your List.
An experienced programmer looks at TreeSet<String> countyNames and instantly knows: this is a sorted collection of Strings without duplicates, and I can be sure that this is true at every moment. So much information in a short declaration.
Real performance win in some cases. If you use a List, and insert values very often, and the list may be read between those insertions, then you have to sort the list after every insertion. The set does the same, but does it much faster.
Using the right collection for the right task is a key to write short and bug free code. It's not as demonstrative in this case, because you just save one line. But I've stopped counting how often I see someone using a List when they want to ensure there are no duplictes, and then build that functionality themselves. Or even worse, using two Lists when you really need a Map.
Don't get me wrong: Using Collections.sort is not an error or a flaw. But there are many cases when the TreeSet is much cleaner.
You can create a new sorted copy using Java 8 Stream or Guava:
// Java 8 version
List<String> sortedNames = names.stream().sorted().collect(Collectors.toList());
// Guava version
List<String> sortedNames = Ordering.natural().sortedCopy(names);
Another option is to sort in-place via Collections API:
Collections.sort(names);
Better late than never! Here is how we can do it(for learning purpose only)-
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
class SoftDrink {
String name;
String color;
int volume;
SoftDrink (String name, String color, int volume) {
this.name = name;
this.color = color;
this.volume = volume;
}
}
public class ListItemComparision {
public static void main (String...arg) {
List<SoftDrink> softDrinkList = new ArrayList<SoftDrink>() ;
softDrinkList .add(new SoftDrink("Faygo", "ColorOne", 4));
softDrinkList .add(new SoftDrink("Fanta", "ColorTwo", 3));
softDrinkList .add(new SoftDrink("Frooti", "ColorThree", 2));
softDrinkList .add(new SoftDrink("Freshie", "ColorFour", 1));
Collections.sort(softDrinkList, new Comparator() {
#Override
public int compare(Object softDrinkOne, Object softDrinkTwo) {
//use instanceof to verify the references are indeed of the type in question
return ((SoftDrink)softDrinkOne).name
.compareTo(((SoftDrink)softDrinkTwo).name);
}
});
for (SoftDrink sd : softDrinkList) {
System.out.println(sd.name + " - " + sd.color + " - " + sd.volume);
}
Collections.sort(softDrinkList, new Comparator() {
#Override
public int compare(Object softDrinkOne, Object softDrinkTwo) {
//comparision for primitive int uses compareTo of the wrapper Integer
return(new Integer(((SoftDrink)softDrinkOne).volume))
.compareTo(((SoftDrink)softDrinkTwo).volume);
}
});
for (SoftDrink sd : softDrinkList) {
System.out.println(sd.volume + " - " + sd.color + " - " + sd.name);
}
}
}
In one line, using Java 8:
list.sort(Comparator.naturalOrder());
Unless you are sorting strings in an accent-free English only, you probably want to use a Collator. It will correctly sort diacritical marks, can ignore case and other language-specific stuff:
Collections.sort(countries, Collator.getInstance(new Locale(languageCode)));
You can set the collator strength, see the javadoc.
Here is an example for Slovak where Š should go after S, but in UTF Š is somewhere after Z:
List<String> countries = Arrays.asList("Slovensko", "Švédsko", "Turecko");
Collections.sort(countries);
System.out.println(countries); // outputs [Slovensko, Turecko, Švédsko]
Collections.sort(countries, Collator.getInstance(new Locale("sk")));
System.out.println(countries); // outputs [Slovensko, Švédsko, Turecko]
Use the two argument for of Collections.sort. You will want a suitable Comparator that treats case appropriate (i.e. does lexical, not UTF16 ordering), such as that obtainable through java.text.Collator.getInstance.
Here is what you are looking for
listOfCountryNames.sort(String::compareToIgnoreCase)
more simply you can use method reference.
list.sort(String::compareTo);
By using Collections.sort(), we can sort a list.
public class EmployeeList {
public static void main(String[] args) {
// TODO Auto-generated method stub
List<String> empNames= new ArrayList<String>();
empNames.add("sudheer");
empNames.add("kumar");
empNames.add("surendra");
empNames.add("kb");
if(!empNames.isEmpty()){
for(String emp:empNames){
System.out.println(emp);
}
Collections.sort(empNames);
System.out.println(empNames);
}
}
}
output:
sudheer
kumar
surendra
kb
[kb, kumar, sudheer, surendra]
You can use the following line
Collections.sort(listOfCountryNames, String.CASE_INSENSITIVE_ORDER)
It is similar to the suggestion of Thilo, but will not make a difference between upper and lowercase characters.
descending alphabet:
List<String> list;
...
Collections.sort(list);
Collections.reverse(list);
Java 8 ,
countries.sort((country1, country2) -> country1.compareTo(country2));
If String's compareTo is not suitable for your need, you can provide any other comparator.
Same in JAVA 8 :-
//Assecnding order
listOfCountryNames.stream().sorted().forEach((x) -> System.out.println(x));
//Decending order
listOfCountryNames.stream().sorted((o1, o2) -> o2.compareTo(o1)).forEach((x) -> System.out.println(x));
//Here is sorted List alphabetically with syncronized
package com.mnas.technology.automation.utility;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import org.apache.log4j.Logger;
/**
*
* #author manoj.kumar
*/
public class SynchronizedArrayList {
static Logger log = Logger.getLogger(SynchronizedArrayList.class.getName());
#SuppressWarnings("unchecked")
public static void main(String[] args) {
List<Employee> synchronizedList = Collections.synchronizedList(new ArrayList<Employee>());
synchronizedList.add(new Employee("Aditya"));
synchronizedList.add(new Employee("Siddharth"));
synchronizedList.add(new Employee("Manoj"));
Collections.sort(synchronizedList, new Comparator() {
public int compare(Object synchronizedListOne, Object synchronizedListTwo) {
//use instanceof to verify the references are indeed of the type in question
return ((Employee)synchronizedListOne).name
.compareTo(((Employee)synchronizedListTwo).name);
}
});
/*for( Employee sd : synchronizedList) {
log.info("Sorted Synchronized Array List..."+sd.name);
}*/
// when iterating over a synchronized list, we need to synchronize access to the synchronized list
synchronized (synchronizedList) {
Iterator<Employee> iterator = synchronizedList.iterator();
while (iterator.hasNext()) {
log.info("Sorted Synchronized Array List Items: " + iterator.next().name);
}
}
}
}
class Employee {
String name;
Employee (String name) {
this.name = name;
}
}
Related
I am looking for a clean and simple way to iterate over two ArrayLists that are related directly in that each index of one maps to the index of another (in relationship).
Currently, I'm doing it via a simple for loop and I tried looking into lambdas and for-each loops but don't see a way to apply it to two lists at the same time that are of the same size.
firstList: ["Blue", "Red", "Green"]
secondList: ["Sky", "Roses", "Grass"]
for(int i = 0; i < firstList.size(); i++){
System.out.println(firstList.get(i) + " " + secondList.get(i));
}
Result:
Blue Sky
Red Roses
Green Grass
Is there a way to effectively iterate over both lists simultaneously using a lambda or a for-each loop to avoid using a for loop?
What you have it is already pretty efficient (assuming that you are using ArrayList), concise and readable. However, this type of scenarios:
I am looking for a clean and simple way to iterate over two ArrayLists
that are related directly in that each index of one maps to the index
of another (in relationship).
typically require a class that defines the relationship, in your case:
public class MyC {
private final String color;
private final String object;
public MyC(String color, String object) {
this.color = color;
this.object = object;
}
public String getColor(){
return color;
}
public String getObject(){
return object;
}
#Override
public String toString() {
return "MyC{" +
"color='" + color + '\'' +
", object='" + object + '\'' +
'}';
}
}
then the two lists would become one:
List<MyC> list = List.of(new MyC("Blue", "Sky"), new MyC("Red", "Roses"), new MyC("Green", "Grass") );
and then you can use:
list.forEach(System.out::println);
The Answer by dreamcrash is correct: While your looping of a pair of arrays works, you should instead take advantage of Java as a OOP language by defining your own class.
Record
Defining such a class is even simpler in Java 16 with the new records feature. Write your class as a record when it’s main purpose is communicating data, transparently and immutably.
A record is very brief by default. The compiler implicitly creates the constructor, getters, equals & hashCode, and toString. You need only declare the type and name of each member field.
record ColorNoun ( String color , String noun ) {}
Use a record like a conventional class. Instantiate with new.
ColorNoun blueSky = new ColorNoun ( "Blue" , "Sky" ) ;
Note that a record can be declared locally within a method, or declared separately like a conventional class.
You can use the "range"-statement to iterate the index with a lambda expression.
https://www.baeldung.com/java-stream-indices
Like this:
List<String> firstList = Arrays.asList("Blue","Red","Green");
List<String> secondList = Arrays.asList("Sky","Roses","Grass");
IntStream
.range(0, firstList.size())
.forEach(index ->
System.out.println(String.format("%s %s", firstList.get(index), secondList.get(index)))
);
It's basically the same approach - just with lambdas.
The only way to get rid of the index-based access is by using a different data-structure or use an alternating iteration technique as shown in the other answers.
If your intention is to just avoid using a for loop, then you could try the below:
Iterator<String> iterator = secondList.iterator();
firstList.forEach(s -> System.out.println(s + " " + iterator.next()));
Or even add to a StringBuilder and then display the result in the end.
All the other answers are correct and the preferred solution should always be the one shown by #Basil Bourque , but as others pointed out in the comments, iterating a non-array-based list using indexes is not very efficient. However, iterating using an iterator should (where each implementation can provide an efficient implementation) is.
Here is an example how you can iterate 2 lists using their iterator:
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.function.BiConsumer;
public class IterateTwoListsExample {
public static void main(String[] args) {
List<String> firstList = Arrays.asList("Blue","Red","Green");
List<String> secondList = Arrays.asList("Sky","Roses","Grass");
forEach(firstList, secondList, (first, second) -> System.out.printf("%s %s%n", first, second));
}
public static <T0, T1> void forEach(List<? extends T0> first, List<? extends T1> second, BiConsumer<? super T0, ? super T1> consumer) {
final Iterator<? extends T0> firstIt = first.iterator();
final Iterator<? extends T1> secondIt = second.iterator();
while (firstIt.hasNext() && secondIt.hasNext()) {
consumer.accept(firstIt.next(), secondIt.next());
}
}
}
how to implement Java Based Auto suggestion. suppose I have different types of data like firstName, rollNumber, address.
My first requirement is like if user enter first character on text box, then result should be sorted on natural order based on firstName and 10 results should be display.
after space if use enter second character and if it is numbere then RollNumber else lastName should be sorted on natural order as ascending.
or if user type third character then Address should be display on ascending order. there should be no database, you don't have to implement Solr or other api. how to implement on pure Java.
here I did not implement the text-box,but I Just took an example to demonstrate
import java.util.*;
import java.lang.*;
import java.io.*;
// A class to represent a student.
class Student {
int rollno;
String name;
String address;
// Constructor
public Student(int rollno, String name, String address) {
this.rollno = rollno;
this.name = name;
this.address = address;
}
// Used to print student details in main()
public String toString(){
return this.rollno + " " + this.name +
" " + this.address;
}
}
class Sortbyroll implements Comparator<Student> {
// Used for sorting in ascending order of rollno
public int compare(Student a, Student b) {
return a.rollno - b.rollno;
}
}
class Sortbyname implements Comparator<Student> {
// Used for sorting in ascending order of name
public int compare(Student a, Student b) {
return a.name.compareTo(b.name);
}
}
// Driver class
class Main {
public static void main (String[] args) {
ArrayList<Student> ar = new ArrayList<Student>();
//here I have thousand student are inserted into
//simple collection.
ar.add(new Student(111, "bbbb", "london"));
ar.add(new Student(131, "aaaa", "nyc"));
ar.add(new Student(121, "cccc", "jaipur"));
System.out.println("Unsorted");
for (int i=0; i<ar.size(); i++) {
System.out.println(ar.get(i));
}
//collection sorted by rollno
Collections.sort(ar, new Sortbyroll());
System.out.println("\nSorted by rollno");
for (int i=0; i<ar.size(); i++) {
System.out.println(ar.get(i));
}
//sort by Name
Collections.sort(ar, new Sortbyname());
System.out.println("\nSorted by name");
for (int i=0; i<ar.size(); i++) {
System.out.println(ar.get(i));
}
}
}
First of all your question is incomplete and misleading. It does not describes the requirement properly. But overall what I assume
You want Google like (?) suggester in your text box
It does not tell any specific things. What about your front end ? How about your data ?
Any way I think you just wanted to have a console like application where you will give partial String as input and your method will guess the Rest of String as an assumption from your dummy data. Am I right ?
If that is the thing you were looking for then I just sketched a demo code below
static List<String> query(String queryStr, List<Student> list) {
List<String> suggestion = new ArrayList<>();
list.forEach(std -> {
if (isMatched(queryStr, String.valueOf(std.getRoll()))) {
suggestion.add(String.valueOf(std.getRoll()));
}
if (isMatched(queryStr, std.getName())) {
suggestion.add(std.getName());
}
if (isMatched(queryStr, std.getAddress())) {
suggestion.add(std.getAddress());
}
});
return suggestion;
}
private static boolean isMatched(String query, String text) {
return text.toLowerCase().contains(query.toLowerCase());
}
And what does this code do ? It actually takes the Partial String that the user input so far and your List<Student> as parameters. Then it iterates over the list and matches for all field for partial match. If any field matches the query it add that value in the suggestion list. In the main you can do like this :
public static void main(String[] args) {
List<Student> list = new ArrayList<>();
list.add(new Student(101, "Abc ghi", "USA"));
list.add(new Student(102, "DEF", "UST"));
list.add(new Student(103, "Ghi ab", "DSjkD"));
list.add(new Student(104, "jKL ut", "USN"));
list.add(new Student(105, "MNP", "TSA101"));
list.add(new Student(106, "UTC ABC", "ESA"));
List<String> sugg = query("01", list);
sugg.forEach(System.out::println);
}
and you will find the console printed like :
101
TSA101
Does it make sense ? it might not be your whole confusing requirements. But I think you got the idea. You can exploit this to address your own requirements. You could further imply your sorting logic or any kind of filters to it. It should not be that tough thing.
But you should be concerned that with large number of collection or complex associated objects this would not suffice. Real world application does not work this straight forward. You might need lot of other things to consider like memory, i/o and execution time.
Good Luck!
Do refer https://github.com/nikcomestotalk/autosuggest/
This implementation is in java based on Patricia trie and Edit distance algorithm.
Some salient features of this application is
Auto correction of keywords
Bucket support for sorting and personalization support.
Filtering support.
Limit support.
Build in http server.
Blazing fast search.
And you all are welcome for feedback
Solr/Lucene/Elastic will not give freedom to choose algorithm and personalization support.
You can use a Trie data structure for autosuggestion implementation and the time complexity would be O(word_length) for insert and search.
Apache commons provides implementation "org.apache.commons.collections4.Trie"
example:
Trie<String, String> trie = new PatriciaTrie<>();
trie.put("abcd", "abcd");
trie.put("abc", "abc");
trie.put("abef", "abef");
SortedMap<String, String> map = trie.prefixMap("ab");
map.forEach((k, v) -> {
System.out.println(k + " " + v);
});
I have two Lists and trying to form a String with element from each List, and in between when I do " ", the sorted order is maintained. But once I put "|" in the middle, which I would want to, the order of the elements in the Set gets switched around.
How can I add "|" and still maintain the sorted order in the Set students?
Here is the code:
Set<String> students = new HashSet<>();
Set<String> fn = new HashSet<>();
Set<String> nums = new HashSet<>();
List<String> firstNames = new ArrayList<>(fn);
Collections.sort(firstNames);
List<String> favNumbers = new ArrayList<>(nums);
Collections.sort(favNumbers);
for(int i=0; i<firstNames.size(); i++) {
students.add(firstNames.get(i) + "|" + favNumbers.get(i));
}
System.out.println(students);
With ... + " " + ..., the order is [Joshua 4, Lyon 7], but if "|" is added in place of " ", the order becomes [Lyon|7, Joshua|4] when I want and should be[Joshua|4, Lyon|7].
A HashSet does not provide any ordering guarantees about its contents, using whatever ordering the underlying HashMap generates, which is in turn based on the hashCode() of the elements.
When you change the contents of a string, you get a different hash code--simple as that. The order in a HashMap is undefined and could change if you inserted additional elements triggering a rehash.
If you want a set with a guaranteed order, you can use a SortedSet implementation (such as TreeSet), but you'd need to write a proper class and implement suitable Comparators. Alternately, you could use LinkedHashSet, which maintains elements in insertion order at the expense of additional overhead.
You should be using object oriented design, as Java is an object oriented language. Instead of trying to represent the various features of a student as independent collection, create a Student POJO which contains these features. Then, create custom comparators to sort by either name or favorite number.
public class Student {
private String firstName;
private String lastName;
private int favNumber;
// getters and setters
public static Comparator<Student> NameComparator
= new Comparator<Student>() {
public int compare(Student s1, Student s2) {
String f1 = s1.getFirstName();
String f2 = s2.getFirstName();
String l1 = s1.getLastName();
String l2 = s2.getLastName();
if (l1.equalsIgnoreCase(l2) {
return f1.toUpperCase().compareTo(f2);
}
else {
return l1.toUpperCase().compareTo(l2);
}
}
};
public static Comparator<Student> FavComparator
= new Comparator<Student>() {
public int compare(Student s1, Student s2) {
return s1.getFavNumber() < s2.getFavNumber();
}
};
}
Now if you have a list of students, List<Student> list, you can sort via:
Collections.sort(list, Student.NameComparator);
Or, to sort by favorite numbers, use:
Collections.sort(list, Student.FavComparator);
From the documentation for HashSet:
It makes no guarantees as to the iteration order of the set; in particular, it does not guarantee that the order will remain constant over time.
So you cannot rely on HashSet to preserve any kind of order.
It looks to me like you just need to preserve the order of insertion, in which case you're better off not using a Set and rather a List, e.g.,
List<String> students = new ArrayList<>();
I am told to have a list of words sorted by length, and those that have the same length are sorted by alphabetical order. This is what I have for the method that does that so far.
public static void doIt(BufferedReader r, PrintWriter w) throws IOException {
TreeMap<String, Integer> s = new TreeMap<String, Integer>();
ArrayList<Integer> count = new ArrayList<Integer>();
String line;
int length;
while ((line = r.readLine()) != null) {
length = line.length();
s.put(line, length);
if (!count.contains(length)){
count.add(length);
}
}
Collections.sort(count);
System.out.println(count);
}
My mindset was to use a TreeMap to keep the String, and the length of the word as the key. I also have an ArrayList that keeps track of all the word's lengths without any duplicates, it's then sorted.
I was hoping to somehow call on the TreeMap for the key value of 5, which would list all the words with 5 letters in it.
I was wondering if I'm on the right track? I've been playing around for over an hour and can't seem to figure out what I should do after this. Am I approaching this from the right angle?
you want to use a string comparator that compares by length 1st. like so:
public class LengthFirstComparator implements Comparator<String> {
#Override
public int compare(String o1, String o2) {
if (o1.length()!=o2.length()) {
return o1.length()-o2.length(); //overflow impossible since lengths are non-negative
}
return o1.compareTo(o2);
}
}
then you could simply sort your Strings by calling Collections.sort(yourStringList, new LengthFirstComparator());
The easiest way would be to write a Comparator<String>. The Comparator<String> would receive two words, and compare them. If the first was shorter than the second, it should return -1. If the second was shorter than the first, it would return 1. If they are the same length, it should call the default String compareTo method. You can then simply sort your list using this custom Comparator.
You can do that using simple List. Try following code.
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
*
* #author Masudul Haque
*/
public class LengthSort {
public static void main(String[] args) {
List<String> list=new ArrayList<>();
list.add("cowa");
list.add("cow");
list.add("aow");
Collections.sort(list, new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
if(o1.length()>o2.length()){
return 1;
}else{
return o1.compareTo(o2);
}
}
});
System.out.println(list);
}
}
By far the easiest and best way is to write a custom comparator as other answers say.
But to do it a similar way you were attempting would be to make the length the key and rather then having a single string as the value have a list of all the words of that length. So a map of the form
Map<Integer,List<String>>
You could then call the key of any length and return a sorted list of words like this
Collections.sort(yourMap.get(theLength))
BUT far more complicated then just using a comparator
You can use Java 8's lamba utilities to make concise functions that prevent the clutter of using comparator classes, like so:
Collections.sort(words, (string1, string2) -> Integer.compare(string1.length(), string2.length());
-Example taken from Effective Java by Joshua Bloch
If you have a sentence like - Apple and grape are not vegetables and told to sort based on the length and if two or more words are equal then if it has to be sorted alphabetically then the code is as below:
public class ExampleDemo {
public static void main(String[] args) {
String s = "Apple and grape are not vegetables";
ExampleDemo e = new ExampleDemo();
e.display(s);
}
public void display(String str) {
String[] st = str.split(" ");
List<String> list = new ArrayList<>();
for(String word: st){
list.add(word);
}
System.out.println("Before sorting: " + list);
Comparator<String> comparator = (s1,s2) -> Integer.compare(s1.length(), s2.length());
Collections.sort(list, comparator);
System.out.println("After sorting: " + list);
}
}
Output:
Before sorting: [Apple, and, grape, are, not, vegetables]
After sorting: [and, are, not, Apple, grape, vegetables]
How can I make an equality assertion between lists in a JUnit test case? Equality should be between the content of the list.
For example:
List<String> numbers = Arrays.asList("one", "two", "three");
List<String> numbers2 = Arrays.asList("one", "two", "three");
List<String> numbers3 = Arrays.asList("one", "two", "four");
// numbers should be equal to numbers2
//numbers should not be equal to numbers3
For junit4! This question deserves a new answer written for junit5.
I realise this answer is written a couple years after the question, probably this feature wasn't around then. But now, it's easy to just do this:
#Test
public void test_array_pass()
{
List<String> actual = Arrays.asList("fee", "fi", "foe");
List<String> expected = Arrays.asList("fee", "fi", "foe");
assertThat(actual, is(expected));
assertThat(actual, is(not(expected)));
}
If you have a recent version of Junit installed with hamcrest, just add these imports:
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
http://junit.org/junit4/javadoc/latest/org/junit/Assert.html#assertThat(T, org.hamcrest.Matcher)
http://junit.org/junit4/javadoc/latest/org/hamcrest/CoreMatchers.html
http://junit.org/junit4/javadoc/latest/org/hamcrest/core/Is.html
For JUnit 5
you can use assertIterableEquals :
List<String> numbers = Arrays.asList("one", "two", "three");
List<String> numbers2 = Arrays.asList("one", "two", "three");
Assertions.assertIterableEquals(numbers, numbers2);
or assertArrayEquals and converting lists to arrays :
List<String> numbers = Arrays.asList("one", "two", "three");
List<String> numbers2 = Arrays.asList("one", "two", "three");
Assertions.assertArrayEquals(numbers.toArray(), numbers2.toArray());
Don't transform to string and compare. This is not good for perfomance.
In the junit, inside Corematchers, there's a matcher for this => hasItems
List<Integer> yourList = Arrays.asList(1,2,3,4)
assertThat(yourList, CoreMatchers.hasItems(1,2,3,4,5));
This is the better way that I know of to check elements in a list.
assertEquals(Object, Object) from JUnit4/JUnit 5 or assertThat(actual, is(expected)); from Hamcrest proposed in the other answers will work 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.
For built-in classes such as String, Integer and so for ... no problem as these override both equals() and toString(). So it is perfectly valid to assert List<String> or List<Integer> 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.
As a good practice I favor assertion/matcher libraries.
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 in the javadoc.
Suppose a Foo class where you add elements and where you can get that.
A unit test of Foo that asserts that the two lists have the same content could look like :
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
#Test
void add() throws Exception {
Foo foo = new Foo();
foo.add("One", "Two", "Three");
Assertions.assertThat(foo.getElements())
.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(foo.getElements())
.containsExactly("One", "Two", "Three");
But Assertion/matcher libraries are a must because these will really further.
Suppose now that Foo doesn't store Strings but Bars instances.
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 way requires that :
import org.assertj.core.api.Assertions;
import static org.assertj.core.groups.Tuple.tuple;
import org.junit.jupiter.api.Test;
#Test
void add() throws Exception {
Foo foo = new Foo();
foo.add(new Bar(1, "One"), new Bar(2, "Two"), new Bar(3, "Three"));
Assertions.assertThat(foo.getElements())
.extracting(Bar::getId, Bar::getName)
.containsExactly(tuple(1, "One"),
tuple(2, "Two"),
tuple(3, "Three"));
}
This is a legacy answer, suitable for JUnit 4.3 and below. The modern version of JUnit includes a built-in readable failure messages in the assertThat method. Prefer other answers on this question, if possible.
List<E> a = resultFromTest();
List<E> expected = Arrays.asList(new E(), new E(), ...);
assertTrue("Expected 'a' and 'expected' to be equal."+
"\n 'a' = "+a+
"\n 'expected' = "+expected,
expected.equals(a));
For the record, as #Paul mentioned in his comment to this answer, two Lists are equal:
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. (Two elements e1 and e2 are equal if (e1==null ? e2==null : e1.equals(e2)).) In other words, two lists are defined to be equal if they contain the same elements in the same order. This definition ensures that the equals method works properly across different implementations of the List interface.
See the JavaDocs of the List interface.
If you don't care about the order of the elements, I recommend ListAssert.assertEquals in junit-addons.
Link: http://junit-addons.sourceforge.net/
For lazy Maven users:
<dependency>
<groupId>junit-addons</groupId>
<artifactId>junit-addons</artifactId>
<version>1.4</version>
<scope>test</scope>
</dependency>
You can use assertEquals in junit.
import org.junit.Assert;
import org.junit.Test;
#Test
public void test_array_pass()
{
List<String> actual = Arrays.asList("fee", "fi", "foe");
List<String> expected = Arrays.asList("fee", "fi", "foe");
Assert.assertEquals(actual,expected);
}
If the order of elements is different then it will return error.
If you are asserting a model object list then you should
override the equals method in the specific model.
#Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj != null && obj instanceof ModelName) {
ModelName other = (ModelName) obj;
return this.getItem().equals(other.getItem()) ;
}
return false;
}
if you don't want to build up an array list , you can try this also
#Test
public void test_array_pass()
{
List<String> list = Arrays.asList("fee", "fi", "foe");
Strint listToString = list.toString();
Assert.assertTrue(listToString.contains("[fee, fi, foe]")); // passes
}
List<Integer> figureTypes = new ArrayList<Integer>(
Arrays.asList(
1,
2
));
List<Integer> figureTypes2 = new ArrayList<Integer>(
Arrays.asList(
1,
2));
assertTrue(figureTypes .equals(figureTypes2 ));
I know there are already many options to solve this issue, but I would rather do the following to assert two lists in any oder:
assertTrue(result.containsAll(expected) && expected.containsAll(result))
You mentioned that you're interested in the equality of the contents of the list (and didn't mention order). So containsExactlyInAnyOrder from AssertJ is a good fit. It comes packaged with spring-boot-starter-test, for example.
From the AssertJ docs ListAssert#containsExactlyInAnyOrder:
Verifies that the actual group contains exactly the given values and nothing else, in any order.
Example:
// an Iterable is used in the example but it would also work with an array
Iterable<Ring> elvesRings = newArrayList(vilya, nenya, narya, vilya);
// assertion will pass
assertThat(elvesRings).containsExactlyInAnyOrder(vilya, vilya, nenya, narya);
// assertion will fail as vilya is contained twice in elvesRings.
assertThat(elvesRings).containsExactlyInAnyOrder(nenya, vilya, narya);
assertEquals(expected, result); works for me.
Since this function gets two objects, you can pass anything to it.
public static void assertEquals(Object expected, Object actual) {
AssertEquals.assertEquals(expected, actual);
}
If there are no duplicates, following code should do the job
Assertions.assertTrue(firstList.size() == secondList.size()
&& firstList.containsAll(secondList)
&& secondList.containsAll(firstList));
Note: In case of duplicates, assertion will pass if number of elements is the same in both lists (even if different elements are duplicated in each list.
I don't this the all the above answers are giving the exact solution for comparing two lists of Objects.
Most of above approaches can be helpful in following limit of comparisons only
- Size comparison
- Reference comparison
But if we have same sized lists of objects and different data on the objects level then this comparison approaches won't help.
I think the following approach will work perfectly with overriding equals and hashcode method on the user-defined object.
I used Xstream lib for override equals and hashcode but we can override equals and hashcode by out won logics/comparison too.
Here is the example for your reference
import com.thoughtworks.xstream.XStream;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
class TestClass {
private String name;
private String id;
public void setName(String value) {
this.name = value;
}
public String getName() {
return this.name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
/**
* #see java.lang.Object#equals(java.lang.Object)
*/
#Override
public boolean equals(Object o) {
XStream xstream = new XStream();
String oxml = xstream.toXML(o);
String myxml = xstream.toXML(this);
return myxml.equals(oxml);
}
/**
* #see java.lang.Object#hashCode()
*/
#Override
public int hashCode() {
XStream xstream = new XStream();
String myxml = xstream.toXML(this);
return myxml.hashCode();
}
}
public class XstreamCompareTest {
public static void main(String[] args) throws ParseException {
checkObjectEquals();
}
private static void checkObjectEquals() {
List<TestClass> testList1 = new ArrayList<TestClass>();
TestClass tObj1 = new TestClass();
tObj1.setId("test3");
tObj1.setName("testname3");
testList1.add(tObj1);
TestClass tObj2 = new TestClass();
tObj2.setId("test2");
tObj2.setName("testname2");
testList1.add(tObj2);
testList1.sort((TestClass t1, TestClass t2) -> t1.getId().compareTo(t2.getId()));
List<TestClass> testList2 = new ArrayList<TestClass>();
TestClass tObj3 = new TestClass();
tObj3.setId("test3");
tObj3.setName("testname3");
testList2.add(tObj3);
TestClass tObj4 = new TestClass();
tObj4.setId("test2");
tObj4.setName("testname2");
testList2.add(tObj4);
testList2.sort((TestClass t1, TestClass t2) -> t1.getId().compareTo(t2.getId()));
if (isNotMatch(testList1, testList2)) {
System.out.println("The list are not matched");
} else {
System.out.println("The list are matched");
}
}
private static boolean isNotMatch(List<TestClass> clist1, List<TestClass> clist2) {
return clist1.size() != clist2.size() || !clist1.equals(clist2);
}
}
The most important thing is that you can ignore the fields by Annotation (#XStreamOmitField) if you don't want to include any fields on the equal check of Objects. There are many Annotations like this to configure so have a look deep about the annotations of this lib.
I am sure this answer will save your time to identify the correct approach for comparing two lists of objects :). Please comment if you see any issues on this.