Efficient way to iterate over two related ArrayLists? - java

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());
}
}
}

Related

How to display LinkedList<String[]> in Java

there are many posts on how to display the elements of LinkedList<String>, but I can't find something that I understand for LinkedList<String[]>.
The following code shows the output:
LinkedList:[[Ljava.lang.String;#5305068a, [Ljava.lang.String;#1f32e575
whereas I'm looking for the output:
LinkedList:[[Audi SQ5,341], [LandRover Discovery,306]].
and I don't want to overwrite the toString().
Code:
LinkedList<String[]> cars2 = new LinkedList<>();
String[] split_result = new String[2];
split_result = "Audi SQ5,341".split(",");
cars2.add(split_result);
split_result = "LandRover Discovery,306".split(",");
cars2.add(split_result);
// Displaying the size of the list
System.out.println("The size of the linked list is: " + cars2.size());
// Displaying the Strings in the linkedlist ??? (not their addresses)
System.out.println("LinkedList:" + cars2);
cars2.forEach(element -> System.out.println(element));
I would use a Java Stream for that. It has the helpful joining collector:
LinkedList<String[]> cars2 = new LinkedList<>();
String output = cars2
.stream()
.map(Arrays::toString)
.collect(Collectors.joining(", ", "[", "]"));
Your problem is that while default Java collections like LinkedList implement a nice toString to print their memebers, arrays do not. So either you have to iterate over your collection and print the results yourself, as some other answers suggest, or you have to put objects into your LinkedLists that have a nice toString implementation. Those can either be collections themselves List<List<String>>, or you can define a class to contain your data.
class CarInfo {
// these should be encapsulated with getters/setters, I'm being lazy.
public String model;
public String value; // I don't know what this represents, or if it's a string or int
public void toString() { return model + ", " + value; }
}
If you use Lombok, this is very easy with the #Data annotation, which will generate toString and getters/setters for you.
#Data
class CarInfo {
String model;
String value;
}
However, it won't be exactly the same as your desired output. If you want it specifically formatted with the brackets as you described, you'd have to do your own toString anyway.

Java Functional Programming: How to convert a if-else ladder inside for loop to functional style?

The expectation is derive 3 lists itemIsBoth, aItems, bItems from the input list items.
How to convert code like below to functional style? (I understand this code is clear enough in an imperative style, but I want to know does declarative style really fail to deal with such a simple example). Thanks.
for (Item item: items) {
if (item.isA() && item.isB()) {
itemIsBoth.add(item);
} else if (item.isA()) {
aItems.add(item);
} else if (item.isB()){
bItems.add(item)
}
}
The question title is quite broad (convert if-else ladder), but since the actual question asks about a specific scenario, let me offer a sample that can at least illustrate what can be done.
Because the if-else structure creates three distinct lists based on a predicate applied to the item, we can express this behavior more declaratively as a grouping operation. The only extra needed to make this work out of the box would be to collapse the multiple Boolean predicates using a tagging object. For example:
class Item {
enum Category {A, B, AB}
public Category getCategory() {
return /* ... */;
}
}
Then the logic can be expressed simply as:
Map<Item.Category, List<Item>> categorized =
items.stream().collect(Collectors.groupingBy(Item::getCategory));
where each list can be retrieved from the map given its category.
If it's not possible to change class Item, the same effect can be achieved by moving the enum declaration and the categorization method outsize the Item class (the method would become a static method).
Another solution using Vavr and doing only one iteration over a list of items might be achieved using foldLeft:
list.foldLeft(
Tuple.of(List.empty(), List.empty(), List.empty()), //we declare 3 lists for results
(lists, item) -> Match(item).of(
//both predicates pass, add to first list
Case($(allOf(Item::isA, Item::isB)), lists.map1(l -> l.append(item))),
//is a, add to second list
Case($(Item::isA), lists.map2(l -> l.append(item))),
//is b, add to third list
Case($(Item::isB), lists.map3(l -> l.append(item)))
))
);
It will return a tuple containing three lists with results.
Of course, you can. The functional way is to use declarative ways.
Mathematically you are setting an Equivalence relation, then, you can write
Map<String, List<Item>> ys = xs
.stream()
.collect(groupingBy(x -> here your equivalence relation))
A simple example show this
public class Main {
static class Item {
private final boolean a;
private final boolean b;
Item(boolean a, boolean b) {
this.a = a;
this.b = b;
}
public boolean isB() {
return b;
}
public boolean isA() {
return a;
}
}
public static void main(String[] args) {
List<Item> xs = asList(new Item(true, true), new Item(true, true), new Item(false, true));
Map<String, List<Item>> ys = xs.stream().collect(groupingBy(x -> x.isA() + "," + x.isB()));
ys.entrySet().forEach(System.out::println);
}
}
With output
true,true=[com.foo.Main$Item#64616ca2, com.foo.Main$Item#13fee20c]
false,true=[com.foo.Main$Item#4e04a765]
Another way you can get rid of the if-else is to to replace them with Predicate and Consumer:
Map<Predicate<Item>, Consumer<Item>> actions =
Map.of(item.predicateA(), aItems::add, item.predicateB(), bItems::add);
actions.forEach((key, value) -> items.stream().filter(key).forEach(value));
Therefore you need to enhace your Item with the both mehods predicateA() and predicateB() using the logic you have implemented in your isA() and isB()
Btw I would still suggest to use your if-else logic.
Since you've mentioned vavr as a tag, I'm gonna provide a solution using vavr collections.
import static io.vavr.Predicates.allOf;
import static io.vavr.Predicates.not;
...
final Array<Item> itemIsBoth = items.filter(allOf(Item::isA, Item::isB));
final Array<Item> aItems = items.filter(allOf(Item::isA, not(Item::isB)));
final Array<Item> bItems = items.filter(allOf(Item::isB, not(Item::isA)));
The advantage of this solution that it's simple to understand at a glance and it's as functional as you can get with Java. The drawback is that it will iterate over the original collections three times instead of once. That's still an O(n), but with a constant multiplier factor of 3. On non-critical code paths and with small collections it might be worth to trade a few CPU cycles for code clarity.
Of course, this works with all the other vavr collections too, so you can replace Array with List, Vector, Stream, etc.
Not (functional in the sense of) using lambda's or so, but quite functional in the sense of using only functions (as per mathematics) and no local state/variabels anywhere :
/* returns 0, 1, 2 or 3 according to isA/isB */
int getCategory(Item item) {
return item.isA() ? 1 : 0 + 2 * (item.isB() ? 1 : 0)
}
LinkedList<Item>[] lists = new LinkedList<Item> { initializer for 4-element array here };
{
for (Item item: items) {
lists[getCategory(item)].addLast(item);
}
}
The question is somewhat controversial, as it seems (+5/-3 at the time of writing this).
As you mentioned, the imperative solution here is most likely the most simple, appropriate and readable one.
The functional or declarative style does not really "fail". It's rather raising questions about the exact goals, conditions and context, and maybe even philosophical questions about language details (like why there is no standard Pair class in core Java).
You can apply a functional solution here. One simple, technical question is then whether you really want to fill the existing lists, or whether it's OK to create new lists. In both cases, you can use the Collectors#groupingBy method.
The grouping criterion is the same in both cases: Namely, any "representation" of the specific combination of isA and isB of one item. There are different possible solutions for that. In the examples below, I used an Entry<Boolean, Boolean> as the key.
(If you had further conditions, like isC and isD, then you could in fact also use a List<Boolean>).
The example shows how you can either add the item to existing lists (as in your question), or create new lists (which is a tad simpler and cleaner).
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
public class FunctionalIfElse
{
public static void main(String[] args)
{
List<Item> items = new ArrayList<Item>();
items.add(new Item(false, false));
items.add(new Item(false, true));
items.add(new Item(true, false));
items.add(new Item(true, true));
fillExistingLists(items);
createNewLists(items);
}
private static void fillExistingLists(List<Item> items)
{
System.out.println("Filling existing lists:");
List<Item> itemIsBoth = new ArrayList<Item>();
List<Item> aItems = new ArrayList<Item>();
List<Item> bItems = new ArrayList<Item>();
Map<Entry<Boolean, Boolean>, List<Item>> map =
new LinkedHashMap<Entry<Boolean, Boolean>, List<Item>>();
map.put(entryWith(true, true), itemIsBoth);
map.put(entryWith(true, false), aItems);
map.put(entryWith(false, true), bItems);
items.stream().collect(Collectors.groupingBy(
item -> entryWith(item.isA(), item.isB()),
() -> map, Collectors.toList()));
System.out.println("Both");
itemIsBoth.forEach(System.out::println);
System.out.println("A");
aItems.forEach(System.out::println);
System.out.println("B");
bItems.forEach(System.out::println);
}
private static void createNewLists(List<Item> items)
{
System.out.println("Creating new lists:");
Map<Entry<Boolean, Boolean>, List<Item>> map =
items.stream().collect(Collectors.groupingBy(
item -> entryWith(item.isA(), item.isB()),
LinkedHashMap::new, Collectors.toList()));
List<Item> itemIsBoth = map.get(entryWith(true, true));
List<Item> aItems = map.get(entryWith(true, false));
List<Item> bItems = map.get(entryWith(false, true));
System.out.println("Both");
itemIsBoth.forEach(System.out::println);
System.out.println("A");
aItems.forEach(System.out::println);
System.out.println("B");
bItems.forEach(System.out::println);
}
private static <K, V> Entry<K, V> entryWith(K k, V v)
{
return new SimpleEntry<K, V>(k, v);
}
static class Item
{
private boolean a;
private boolean b;
public Item(boolean a, boolean b)
{
this.a = a;
this.b = b;
}
public boolean isA()
{
return a;
}
public boolean isB()
{
return b;
}
#Override
public String toString()
{
return "(" + a + ", " + b + ")";
}
}
}

Group and Reduce list of objects

I have a list of objects with many duplicated and some fields that need to be merged. I want to reduce this down to a list of unique objects using only Java 8 Streams (I know how to do this via old-skool means but this is an experiment.)
This is what I have right now. I don't really like this because the map-building seems extraneous and the values() collection is a view of the backing map, and you need to wrap it in a new ArrayList<>(...) to get a more specific collection. Is there a better approach, perhaps using the more general reduction operations?
#Test
public void reduce() {
Collection<Foo> foos = Stream.of("foo", "bar", "baz")
.flatMap(this::getfoos)
.collect(Collectors.toMap(f -> f.name, f -> f, (l, r) -> {
l.ids.addAll(r.ids);
return l;
})).values();
assertEquals(3, foos.size());
foos.forEach(f -> assertEquals(10, f.ids.size()));
}
private Stream<Foo> getfoos(String n) {
return IntStream.range(0,10).mapToObj(i -> new Foo(n, i));
}
public static class Foo {
private String name;
private List<Integer> ids = new ArrayList<>();
public Foo(String n, int i) {
name = n;
ids.add(i);
}
}
If you break the grouping and reducing steps up, you can get something cleaner:
Stream<Foo> input = Stream.of("foo", "bar", "baz").flatMap(this::getfoos);
Map<String, Optional<Foo>> collect = input.collect(Collectors.groupingBy(f -> f.name, Collectors.reducing(Foo::merge)));
Collection<Optional<Foo>> collected = collect.values();
This assumes a few convenience methods in your Foo class:
public Foo(String n, List<Integer> ids) {
this.name = n;
this.ids.addAll(ids);
}
public static Foo merge(Foo src, Foo dest) {
List<Integer> merged = new ArrayList<>();
merged.addAll(src.ids);
merged.addAll(dest.ids);
return new Foo(src.name, merged);
}
As already pointed out in the comments, a map is a very natural thing to use when you want to identify unique objects. If all you needed to do was find the unique objects, you could use the Stream::distinct method. This method hides the fact that there is a map involved, but apparently it does use a map internally, as hinted by this question that shows you should implement a hashCode method or distinct may not behave correctly.
In the case of the distinct method, where no merging is necessary, it is possible to return some of the results before all of the input has been processed. In your case, unless you can make additional assumptions about the input that haven't been mentioned in the question, you do need to finish processing all of the input before you return any results. Thus this answer does use a map.
It is easy enough to use streams to process the values of the map and turn it back into an ArrayList, though. I show that in this answer, as well as providing a way to avoid the appearance of an Optional<Foo>, which shows up in one of the other answers.
public void reduce() {
ArrayList<Foo> foos = Stream.of("foo", "bar", "baz").flatMap(this::getfoos)
.collect(Collectors.collectingAndThen(Collectors.groupingBy(f -> f.name,
Collectors.reducing(Foo.identity(), Foo::merge)),
map -> map.values().stream().
collect(Collectors.toCollection(ArrayList::new))));
assertEquals(3, foos.size());
foos.forEach(f -> assertEquals(10, f.ids.size()));
}
private Stream<Foo> getfoos(String n) {
return IntStream.range(0, 10).mapToObj(i -> new Foo(n, i));
}
public static class Foo {
private String name;
private List<Integer> ids = new ArrayList<>();
private static final Foo BASE_FOO = new Foo("", 0);
public static Foo identity() {
return BASE_FOO;
}
// use only if side effects to the argument objects are okay
public static Foo merge(Foo fooOne, Foo fooTwo) {
if (fooOne == BASE_FOO) {
return fooTwo;
} else if (fooTwo == BASE_FOO) {
return fooOne;
}
fooOne.ids.addAll(fooTwo.ids);
return fooOne;
}
public Foo(String n, int i) {
name = n;
ids.add(i);
}
}
If the input elements are supplied in the random order, then having intermediate map is probably the best solution. However if you know in advance that all the foos with the same name are adjacent (this condition is actually met in your test), the algorithm can be greatly simplified: you just need to compare the current element with the previous one and merge them if the name is the same.
Unfortunately there's no Stream API method which would allow you do to such thing easily and effectively. One possible solution is to write custom collector like this:
public static List<Foo> withCollector(Stream<Foo> stream) {
return stream.collect(Collector.<Foo, List<Foo>>of(ArrayList::new,
(list, t) -> {
Foo f;
if(list.isEmpty() || !(f = list.get(list.size()-1)).name.equals(t.name))
list.add(t);
else
f.ids.addAll(t.ids);
},
(l1, l2) -> {
if(l1.isEmpty())
return l2;
if(l2.isEmpty())
return l1;
if(l1.get(l1.size()-1).name.equals(l2.get(0).name)) {
l1.get(l1.size()-1).ids.addAll(l2.get(0).ids);
l1.addAll(l2.subList(1, l2.size()));
} else {
l1.addAll(l2);
}
return l1;
}));
}
My tests show that this collector is always faster than collecting to map (up to 2x depending on average number of duplicate names), both in sequential and parallel mode.
Another approach is to use my StreamEx library which provides a bunch of "partial reduction" methods including collapse:
public static List<Foo> withStreamEx(Stream<Foo> stream) {
return StreamEx.of(stream)
.collapse((l, r) -> l.name.equals(r.name), (l, r) -> {
l.ids.addAll(r.ids);
return l;
}).toList();
}
This method accepts two arguments: a BiPredicate which is applied for two adjacent elements and should return true if elements should be merged and the BinaryOperator which performs merging. This solution is a little bit slower in sequential mode than the custom collector (in parallel the results are very similar), but it's still significantly faster than toMap solution and it's simpler and somewhat more flexible as collapse is an intermediate operation, so you can collect in another way.
Again both these solutions work only if foos with the same name are known to be adjacent. It's a bad idea to sort the input stream by foo name, then using these solutions, because the sorting will drastically reduce the performance making it slower than toMap solution.
As already pointed out by others, an intermediate Map is unavoidable, as that’s the way of finding the objects to merge. Further, you should not modify source data during reduction.
Nevertheless, you can achieve both without creating multiple Foo instances:
List<Foo> foos = Stream.of("foo", "bar", "baz")
.flatMap(n->IntStream.range(0,10).mapToObj(i -> new Foo(n, i)))
.collect(collectingAndThen(groupingBy(f -> f.name),
m->m.entrySet().stream().map(e->new Foo(e.getKey(),
e.getValue().stream().flatMap(f->f.ids.stream()).collect(toList())))
.collect(toList())));
This assumes that you add a constructor
public Foo(String n, List<Integer> l) {
name = n;
ids=l;
}
to your Foo class, as it should have if Foo is really supposed to be capable of holding a list of IDs. As a side note, having a type which serves as single item as well as a container for merged results seems unnatural to me. This is exactly why to code turns out to be so complicated.
If the source items had a single id, using something like groupingBy(f -> f.name, mapping(f -> id, toList()), followed by mapping the entries of (String, List<Integer>) to the merged items was sufficient.
Since this is not the case and Java 8 lacks the flatMapping collector, the flatmapping step is moved to the second step, making it look much more complicated.
But in both cases, the second step is not obsolete as it is where the result items are actually created and converting the map to the desired list type comes for free.

Java, searching within a list of objects?

I'm a bit lost on the way to make this happen the fastest. I have a large list of objects that have basic variable attributes (with getters / setters) and I need to do a search in this list to find the objects within the list that match a given parameter
I have found how to do a regular list search but I need to, for example search for the value of the result of doing a call getName() for each object in the list and get objects that have a result that matches my input.
Something like below where the third argument is the result of the method call and the second is what I am trying to find.
int index = Collections.binarySearch(myList, "value", getName());
Any advice is appreciated
If you just as a one-off operation need to find the object(s) whose getName() is a particular value, then there's probably not much magic possible: cycle through the list, call getName() on each object, and for those that match, add them to your list of results.
If getName() is an expensive operation and there's some other way of a-priori working out if a given object definitely won't return a matching value, then obviously you can build in this 'filtering' as you cycle through.
If you frequently need to fetch objects for a given getName(), then keep an index (e.g. in a HashMap) of [result of getName()->object -> list of matches]. You'll need to decide how and if you need to keep this "index" in synch with the actual list.
See also the other proposition to use binarySearch() but to keep the list maintained. This way, inserts are more expensive than with a map and unsorted list, but if inserts are infrequent compared to lookups, then it has the advantage of only needing to maintain one structure.
Take a look at the binarySearch that takes a comparator:
public static int binarySearch(List list,
T key,
Comparator c)
So you would do something like:
class FooComparator
implements Comparator<Foo>
{
public int compare(T a, T b)
{
return (a.getName().compareTo(b.getName());
}
}
int index = Collections.binarySearch(myList, "value", new FooComparator());
You will need to first sort the list of course (Collections.sort takes a Comaprator as well...).
I know anonymous inner classes are not fashion anymore, but while Java 8 arrives, you can create something like this:
1.- Create a search method that iterates the collection and pass an object that tells you if your object is to be returned or not.
2.- Invoke that method and create an anonymous inner class with the criteria
3.- Get the new list in separate variable.
Something like this:
result = search( aList, new Matcher(){ public boolean matches( Some some ) {
if( some.name().equals("a")) {
return true;
}
}});
Here's a working demo:
import java.util.*;
class LinearSearchDemo {
public static void main( String ... args ) {
List<Person> list = Arrays.asList(
Person.create("Oscar", 0x20),
Person.create("Reyes", 0x30),
Person.create("Java", 0x10)
);
List<Person> result = searchIn( list,
new Matcher<Person>() {
public boolean matches( Person p ) {
return p.getName().equals("Java");
}});
System.out.println( result );
result = searchIn( list,
new Matcher<Person>() {
public boolean matches( Person p ) {
return p.getAge() > 16;
}});
System.out.println( result );
}
public static <T> List<T> searchIn( List<T> list , Matcher<T> m ) {
List<T> r = new ArrayList<T>();
for( T t : list ) {
if( m.matches( t ) ) {
r.add( t );
}
}
return r;
}
}
class Person {
String name;
int age;
String getName(){
return name;
}
int getAge() {
return age;
}
static Person create( String name, int age ) {
Person p = new Person();
p.name = name;
p.age = age;
return p;
}
public String toString() {
return String.format("Person(%s,%s)", name, age );
}
}
interface Matcher<T> {
public boolean matches( T t );
}
Output:
[Person(Java,16)]
[Person(Oscar,32), Person(Reyes,48)]
To do this in a more scalable way, without simply iterating/filtering objects, see this answer to a similar question: How do you query object collections in Java (Criteria/SQL-like)?
If the objects are immutable (or you at least know their names won't change) you could create an index using a HashMap.
You would have to fill the Map and keep it updated.
Map map = new HashMap();
map.put(myObject.getName(), myObject);
... repeat for each object ...
Then you can use map.get("Some name"); to do lookup using your index.
One library I'm familiar with is Guava -- you can compose its Predicate to pull out items from an Iterable. There's no need for the collection to be pre-sorted. (This means, in turn, that it's O(N), but it's convenient.)

How can I sort a List alphabetically?

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;
}
}

Categories

Resources