From the input string provided:
{ "200,400,7,1", "100,0,1,1", "200,200,3,1", "0,400,11,1",
"407,308,5,1","100,600,9,1" } ,
I am adding the same in a TreeSet and want it to be sorted with the 3rd element order, so the expected output will be:
(100,0,1,1) (200,200,3,1) (407,308,5,1) (200,400,7,1) (100,600,9,1) (0,400,11,1)
But my actual output is:
(100,0,1,1)(0,400,11,1)(200,200,3,1)(407,308,5,1)(200,400,7,1)(100,600,9,1)
But since the string comparison of 11 is less than 9 but in terms of integer , 11>9 . My expected output is getting differed. Suggest me some idea to resolve the same.
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetComparator {
public static void main(String[] args) {
Comparator<String> comparator = new Comparator<String>() {
#Override
public int compare(String a, String b) {
String aStr = a;
String bStr = b;
String[] splitA = aStr.split(",");
String[] splitB = bStr.split(",");
return splitA[2].compareTo(splitB[2]);
}
};
String[] arr = { "200,400,7,1", "100,0,1,1", "200,200,3,1",
"0,400,11,1", "407,308,5,1", "100,600,9,1" };
TreeSet<String> ts = new TreeSet<String>(comparator);
for (String str : arr) {
ts.add(str);
}
for (String element : ts)
System.out.print(element + " ");
}
}
You're sorting in a lexicographical order ("123" comes before "20"), what you need to do is convert them to integers, and then compare them:
Not:
return splitA[2].compareTo(splitB[2]);
but:
return Integer.valueOf(splitA[2]).compareTo(Integer.valueOf(splitB[2]));
However, a somewhat cleaner way would be to create a custom object holding these 4 different values and then create a Comparator that compares the 3rd value of such an object:
The following:
public class Main {
public static void main (String[] args) {
Comparator<CustomObject> sortOn3rdValue = new Comparator<CustomObject>() {
#Override
public int compare(CustomObject o1, CustomObject o2) {
return o1.v3 < o2.v3 ? -1 : o1.v3 > o2.v3 ? 1 : 0;
}
};
Set<CustomObject> objects = new TreeSet<CustomObject>(sortOn3rdValue);
String[] arr = { "200,400,7,1", "100,0,1,1", "200,200,3,1", "0,400,11,1", "407,308,5,1", "100,600,9,1" };
for(String a : arr) {
objects.add(new CustomObject(a.split(",")));
}
for(CustomObject co : objects) {
System.out.println(co);
}
}
}
class CustomObject {
final int v1, v2, v3, v4;
CustomObject(String[] strValues) {
// assume strValues.lenght == 4
v1 = Integer.valueOf(strValues[0]);
v2 = Integer.valueOf(strValues[1]);
v3 = Integer.valueOf(strValues[2]);
v4 = Integer.valueOf(strValues[3]);
}
#Override
public String toString() {
return String.format("(%d,%d,%d,%d)", v1, v2, v3, v4);
}
}
would print:
(100,0,1,1)
(200,200,3,1)
(407,308,5,1)
(200,400,7,1)
(100,600,9,1)
(0,400,11,1)
Related
Hey guys I'm trying to make a scoreboard for my game and therefor I need to sort it. My input is DATE;LEVEL;SCORE and I want to sort it by the highest score, if it's equal by the highest level and if it's equal by the date.
My ArrayList:
ArrayList<String> test = new ArrayList<>();
test.add("16.06.2018;1;10");
test.add("16.06.2018;1;2");
test.add("16.06.2018;1;5");
test.add("16.06.2018;1;1");
test.add("16.06.2018;1;3");
test.add("16.06.2018;2;3");
test.add("15.06.2018;1;3");
test.add("17.06.2018;1;3");
should be sorted
[16.06.2018;1;10, 16.06.2018;1;5, 16.06.2018;2;3, 15.06.2018;1;3, 16.06.2018;1;3, 17.06.2018;1;3, 16.06.2018;1;2, 16.06.2018;1;1];
but I'm getting
[16.06.2018;1;5, 16.06.2018;2;3, 15.06.2018;1;3, 16.06.2018;1;3, 17.06.2018;1;3, 16.06.2018;1;2, 16.06.2018;1;10, 16.06.2018;1;1]
My code:
Collections.sort(test, new Comparator<String>() {
#Override
public int compare(String A, String B) {
String[] tmp = A.split(";");
String[] tmp2 = B.split(";");
if (tmp[2].equals(tmp2[2])) {
if (tmp[1].equals(tmp2[1])) {
return compareDate(tmp[0], tmp2[0]);
} else {
return tmp2[1].compareTo(tmp[1]);
}
} else {
return tmp2[2].compareTo(tmp[2]);
}
}
//compares 2 dates
private int compareDate(String A, String B) {
String[] tmp = A.split("\\.");
String[] tmp2 = B.split("\\.");
if (tmp[2].equals(tmp2[2])) {
if (tmp[1].equals(tmp2[1])) {
return tmp[0].compareTo(tmp2[0]);
} else {
return tmp[1].compareTo(tmp2[1]);
}
} else {
return tmp[2].compareTo(tmp2[2]);
}
}
});
You're using a string-based lexical comparison which treats "5" as being greater than "10" (because the character '5' comes after '1' in the Unicode table).
Instead you should use a numerical comparison. Convert the strings to integers and compare them with Integer.compare or similar:
Instead of this:
return tmp2[2].compareTo(tmp[2]);
You can do this:
return Integer.compare(
Integer.parseInt(tmp2[2]),
Integer.parseInt(tmp[2])
);
If you are using Java 8, I would like to create an Object from that String so you can compare it easily :
test.stream()
.map(c -> {
String[] tmp = c.split(";");
MyObject obj = new MyObject(
LocalDate.parse(tmp[0], DateTimeFormatter.ofPattern("dd.MM.yyyy")),
Integer.valueOf(tmp[1]), Integer.valueOf(tmp[2])
);
return obj;
}).sorted(
Comparator.comparing(MyObject::getDate)
.thenComparing(MyObject::getLevel)
.thenComparing(MyObject::getScore));
With this Object :
class MyObject {
private LocalDate date;
private Integer level;
private Integer score;
public MyObject(LocalDate date, Integer level1, Integer score) {
this.date = date;
this.level = level;
this.score= score;
}
public MyObject() {
}
//getter setter
}
Or without an Object :
test.stream().map(c -> c.split(";")).sorted(
Comparator.comparing(a -> LocalDate.parse(((String[]) a)[0], DateTimeFormatter.ofPattern("dd.MM.yyyy")))
.thenComparing(a -> Integer.valueOf(((String[]) a)[1]))
.thenComparing(a -> Integer.valueOf(((String[]) a)[2])));
Note : You can put them in the order you want so you will get the expected result
I like the approach from #YCF_L and how #jspcal gets right to the point. I would usually break it up into reusable components like this.
public static void sort(List<String> data) {
Collections.sort(data, new DataComparator());
}
private static class DataComparator implements Comparator<String>
{
#Override
public int compare(String str1, String str2) {
DataObject data1 = DataObject.valueOf(str1);
DataObject data2 = DataObject.valueOf(str2);
return data1.compareTo(data2);
}
}
private static class DataObject implements Comparable<DataObject>
{
private static final Map<String,DataObject> valuesCache = new HashMap<String,DataObject>();
private LocalDate date;
private int value1;
private int value2;
/**
* Parse the "date;value1;value2" String into an Object.
* #param value the string
* #throws ParseException if the date is invalid
*/
public DataObject(String value) {
String[] values = value.split(";");
this.date = LocalDate.parse(values[0], DateTimeFormatter.ofPattern("dd.MM.yyyy"));
this.value1 = Integer.valueOf(values[1]);
this.value2 = Integer.valueOf(values[2]);
}
/**
* Parse the String into an object.
* #param str the string
* #return the data object
*/
public static DataObject valueOf(String str) {
DataObject data = valuesCache.get(str);
if (data == null) {
data = new DataObject(str);
valuesCache.put(str, data);
}
return data;
}
/**
* Compare this DataObject to the other DataObject.
*/
#Override
public int compareTo(DataObject other) {
int cmp = 0;
if (this != other) {
// first compare the value2 integers
// sort descending (higher first) by multiplying by -1
cmp = -1 * Integer.compare(this.value2, other.value2);
// if those values matched, then compare value1 integers
// also sort descending
if (cmp == 0) {
cmp = -1 * Integer.compare(this.value1, other.value1);
}
// if those values matched, then compare dates ascending
if (cmp == 0) {
cmp = this.date.compareTo(other.date);
}
}
return cmp;
}
#Override
public String toString() {
return String.format("%s;%d;%d", date, value1, value2);
}
}
I am trying to solve a exorcise that is supposed to learn me about the Comparable<T> interface. It tells me to find the shortest and longest string in a string array.
I think I am supposed to make my own compareTo() -method because the String.compareTo() method sorts alphabetically. But I can't get what my method should look like.
This is my code so far:
class ComparableTest implements Comparable<String> {
public static void main(String[] args) {
String arr[] = {"hei", "hvordan", "gaar", "det", "med", "deg", "a"};
String tempSto = arr[0]; //long string
String tempLit = arr[0]; //short string
for(String e : arr) {
if(e.compareTo(tempSto) > 0) {
tempSto = e;
}
if(e.compareTo(tempLit) < 0) {
tempLit = e;
}
}
System.out.println("Longest string is: " + tempSto);
System.out.println("Shortest string is: " + tempLit);
}
}
As you mentioned, you should implement your own Comparator, based on the String's length, not its alphabetical contents. E.g.:
public class StringLengthComparator extends Comparator<String> {
#Override
public int compare (String s1, String s2) {
return Integer.compare(s1.length(), s2.length();
}
}
Once you've done that, you can use it to find the shortest string in the array, or just reuse Collections#min(Collection, Comparator) to do the heavy lifting for you:
String shortest =
Collections.min(Arrays.asList(arr), new StringLengthComparator());
If your class implements the Comparable interface, i guess you have to override the compareTo method, and compare the length of the two String compared.
You can use a comparable to sort a list according to your own needs. This means in your case, that the highest and lowest number of characters in a string is relevant for the comparing. So, you subtract those value from each other in the comparing function. If you have for example house and dog, you have the two values 5 and 3. By subtracting the values from each other, you get the difference on how close those values are. So, just return the difference of the length of the two parameters in your ordering function. The first and last element in the list will then be the largest and smallest word (depending on which value you subtract from which).
In Java 8:
import java.util.Arrays;
public class Example {
public static void main(String[] args) {
String arr[] = {"hei", "hvordan", "gaar", "det", "med", "deg", "a"};
String minLengthStr = Arrays.stream(arr)
.min((str1, str2) -> Integer.compare(str1.length(), str2.length()))
.get();
String maxLengthStr = Arrays.stream(arr)
.max((str1, str2) -> Integer.compare(str1.length(), str2.length()))
.get();
System.out.println("Longest string is: " + maxLengthStr);
System.out.println("Shortest string is: " + minLengthStr);
}
}
Output:
Longest string is: hvordan
Shortest string is: a
Explanation:
The min method in the Stream class takes a Comparator as a parameter. As Comparator is a functional interface we can use a lambda expression as a short hand for implementing it.
So instead of writing:
Comparator<String> lengthComparator = new Comparator<String>() {
#Override
public int compare(String str1, String str2) {
return Integer.compare(str1.length(), str2.length());
}
};
You can write:
Comparator<String> lengthComparator = (str1, str2) -> Integer.compare(str1.length(), str2.length());
You can use this without streams too:
public class Example {
public static void main(String[] args) {
String arr[] = {"hei", "hvordan", "gaar", "det", "med", "deg", "a"};
String minLengthStr = arr[0], maxLengthStr = arr[0];
Comparator<String> lengthComparator = (str1, str2) -> Integer.compare(str1.length(), str2.length());
for(String str : arr) {
if(lengthComparator.compare(str, minLengthStr) == -1) {
minLengthStr = str;
} else if(lengthComparator.compare(str, maxLengthStr) == 1) {
maxLengthStr = str;
}
}
System.out.println("Longest string is: " + maxLengthStr);
System.out.println("Shortest string is: " + minLengthStr);
}
}
I hope,this will help
public class Test implements Comparator<String>{
public static void main(String[] args) {
// TODO Auto-generated method stub
//String arr[] = {"hei", "hvordan", "gaar", "det", "med", "deg", "a"};
Set<String> set = new TreeSet<String>(new Test());
set.add("hei");
set.add("hvordan");
set.add("gaar");
set.add("med");
set.add("deg");
set.add("a");
System.out.println(set);
}
#Override
public int compare(String o1, String o2) {
// TODO Auto-generated method stub
if(o1.length()>o2.length())
return 1;
else if(o1.length()<o2.length())
return -1;
else
return 0;
}
If you want to play with String, Comparable and compareTo here is an example.
ideone.com
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Test {
String arr[] = {"hei", "hvordan", "gaar", "det", "med", "deg", "a"};
/**
* Use composition.
*/
class MyComparableString implements Comparable<MyComparableString> {
String myString;
MyComparableString(String s) {
myString = s;
}
#Override
public int compareTo(MyComparableString other) {
// Compare the lengths of the strings in this and other.
Integer l1 = myString.length();
Integer l2 = other.myString.length();
return l1.compareTo(l2);
}
// String representation.
public String toString() {
return myString;
}
}
void go() {
// Convert the String array into a List (Collection) of MyComparableString.
List<MyComparableString> l = new ArrayList<>();
for (String s: arr) {
l.add(new MyComparableString(s));
}
// Print longest and shortest.
System.out.println("Shortest: " + Collections.min(l));
System.out.println("Longest: " + Collections.max(l));
}
public static void main(String[] args) {
new Test().go();
}
}
If I have an ArrayList as the following:
["a 100", "b 32", "t 54", "u 1"] (numbers and letter are separated by space in each cell of the array list).
How can I sort it by numbers keeping each number with its corresponding letter?.
This looks like you are trying to implement object oriented programming using strings. Luckily, Java has already done this.
So, do something like this instead:
public class MyClass implements Comparable<MyClass> {
private final String aString; //could be char perhaps..
private final Integer anInteger;
public MyClass(final String aString, final Integer anInteger) {
this.aString = aString;
this.anInteger = anInteger;
}
public String getAString() { return aString; }
public Integer getAnInteger() { return anInteger; }
public String toString() { return anInteger + " " + aString }
//comparison by number
public int compareTo(final MyClass other) {
return anInteger.compareTo(other.anInteger);
}
}
Then, you use it like this:
final List<MyClass> myClasses = new ArrayList<>();
myClasses.add(new MyClass("a", 100));
myClasses.add(new MyClass("b", 32));
myClasses.add(new MyClass("t", 54));
myClasses.add(new MyClass("u", 1));
Collections.sort(myClasses);
You can simply use swapping method just like in regular arrays. The only difference is that we use set(index, "value") method to update a specific string at specified index.
public static void sort (ArrayList<String> arr){
int N = arr.size();
int E = N-1;
String temp;
boolean flag = true;
while(flag){
flag=false;
for(int a = 0 ; a < E ; a++){
if(Integer.parseInt(arr.get(a).substring(arr.get(a).indexOf(" ")+1)) >
Integer.parseInt(arr.get(a+1).substring(arr.get(a+1).indexOf(" ")+1))) {
temp=arr.get(a);
arr.set(a, arr.get(a+1));
arr.set(a+1, temp);
flag=true;
}
}
E--;
}}
The sorting algorithm is bubble sort. I have used it due to simplicity. You can use any other sorting algorithm if you want.
Then, you can call the sort() function in main method:
public static void main(String[] args) {
ArrayList<String> arr = new ArrayList<String>();
arr.add("a 98");
arr.add("a 23");
arr.add("c 11");
sort(arr);
}
Use a custom comparator to sort the list.
List<String> yourList = Arrays.asList("a 100", "b 32", "t 54", "u 1");
yourList.sort((entry1, entry2) -> {
int number1 = Integer.parseInt(entry1.split(" ")[1]);
int number2 = Integer.parseInt(entry2.split(" ")[1]);
return Integer.compare(number1, number2);
});
Regards
import static java.lang.Integer.*;
Just import static Integer methods and you'll get the most compact Comparator<String> for your purpose.
(a, b) -> compare(valueOf(a.split(" ")[1]), valueOf(b.split(" ")[1]));
Assuming the elements in the list are the same pattern:
then you can do
public static void main(String[] args) {
// ["a 100", "b 32", "t 54", "u 1"]
List<String> myList = new ArrayList<>();
myList.add("a 100");
myList.add("b 32");
myList.add("t 54");
myList.add("u 1");
System.out.println("List unsorted" + myList);
Collections.sort(myList, new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
try {
int a1 = Integer.parseInt(o1.substring(2));
int a2 = Integer.parseInt(o2.substring(2));
return Integer.compare(a1,a2);
} catch (NumberFormatException ex) {
return 0;
}
}
});
System.out.println("List sorted" + myList);
}
In Java 8, Comparator has a handful of static and default methods that make it easy to create custom comparators. For instance, you could create one that splits each string and converts the second word to an integer.
list.sort(Comparator.comparingInt(
s -> Integer.parseInt(s.split(" ")[1])
));
Say I have an array of strings:
String[] array = {
"2183417234 somerandomtexthere",
"1234123656 somemorerandomtexthere",
"1093241066 andevenmore",
"1243981234 you get what i mean",
//etc
};
How would I sort this array using the long (it's a long) at the start of the string, so it'll end up looking like this:
String[] array = {
"1093241066 andevenmore",
"1234123656 somemorerandomtexthere",
"1243981234 you get what i mean",
"2183417234 somerandomtexthere",
//etc
};
I've tried everyting from making it an arraylist and using Collections#sort to creating my own comparator, to using a sorted map / tree map and I just can't figure it out.
Thanks.
Use this function:
static long comparedValue(String s) {
return Long.valueOf(s.substring(0, s.indexOf(' ')));
}
and then define a Comparator in terms of it:
public int compare(String left, String right) {
return comparedValue(left) - comparedValue(right);
}
Using Google Guava:
List<String> unsorted = Arrays.asList(array);
Function<String, Long> longFunction = new Function<String, Long>() {
#Override public Long apply(String input) {
return Long.valueOf(input.split(" ")[0]);
}
};
List<String> sorted = Ordering.natural().onResultOf(longFunction).immutableSortedCopy(unsorted);
Or if you don't wanna use a List (you should always prefer collections to arrays):
Arrays.sort(array, Ordering.natural().onResultOf(longFunction));
The input that you have shown works perfectly fine. But that's because all of them have the same number of digits.
public static void main(String[] args) {
String[] array = { "2183417234 somerandomtexthere",
"1234123656 somemorerandomtexthere", "1093241066 andevenmore",
"1243981234 you get what i mean", "999 little shorter"
// etc
};
List<String> list = Arrays.asList(array);
Collections.sort(list);
System.out.println(list);
}
Problems start to occur, when you use some shorter numbers - as 999 shown above...
output will be:
[1093241066 andevenmore, 1234123656 somemorerandomtexthere, 1243981234 you get what i mean, 2183417234 somerandomtexthere, 999 little shorter]
So, to make it working allways - you need your custom comparator, that will be able to split given Strings, and then take the number part out of them, and compare them. Using #Marko Topolik solution:
static long comparedValue(String s) {
return Long.valueOf(s.substring(0, s.indexOf(' ')));
}
public int compare(String left, String right) {
long result = comparedValue(left) - comparedValue(right);
boolean numberPartAreEqual = result == 0;
if (numberPartAreEqual) {
result = left.compareTo(right);
}
return (int) result;
}
A custom comparator should work fine:
public class LongPrefixComparator implements Comparator<String> {
#Override
public int compare(String s1, String s2) {
final long pref1 = getPrefixValue(s1);
final long pref2 = getPrefixValue(s2);
return s1 == s2 ? 0 : s1 < s2 ? -1 : 1;
}
private static long getPrefixValue(String stg) {
int len = stg.indexOf(' ');
if (len > 0) {
try {
return Long.parseLong(stg.substring(0, len));
catch (NumberFormatException ignored) {}
}
return 0L;
}
}
In Python, the enumerate function allows you to iterate over a sequence of (index, value) pairs. For example:
>>> numbers = ["zero", "one", "two"]
>>> for i, s in enumerate(numbers):
... print i, s
...
0 zero
1 one
2 two
Is there any way of doing this in Java?
For collections that implement the List interface, you can call the listIterator() method to get a ListIterator. The iterator has (amongst others) two methods - nextIndex(), to get the index; and next(), to get the value (like other iterators).
So a Java equivalent of the Python above might be:
import java.util.ListIterator;
import java.util.List;
List<String> numbers = Arrays.asList("zero", "one", "two");
ListIterator<String> it = numbers.listIterator();
while (it.hasNext()) {
System.out.println(it.nextIndex() + " " + it.next());
}
which, like the Python, outputs:
0 zero
1 one
2 two
I find this to be the most similar to the python approach.
Usage
public static void main(String [] args) {
List<String> strings = Arrays.asList("zero", "one", "two");
for(EnumeratedItem<String> stringItem : ListUtils.enumerate(strings)) {
System.out.println(stringItem.index + " " + stringItem.item);
}
System.out.println();
for(EnumeratedItem<String> stringItem : ListUtils.enumerate(strings, 3)) {
System.out.println(stringItem.index + " " + stringItem.item);
}
}
Output
0 zero
1 one
2 two
3 zero
4 one
5 two
Features
Works on any iterable
Does not create an in-memory list copy (suitable for large lists)
Supports native for each syntax
Accepts a start parameter which can be added to the index
Implementation
import java.util.Iterator;
public class ListUtils {
public static class EnumeratedItem<T> {
public T item;
public int index;
private EnumeratedItem(T item, int index) {
this.item = item;
this.index = index;
}
}
private static class ListEnumerator<T> implements Iterable<EnumeratedItem<T>> {
private Iterable<T> target;
private int start;
public ListEnumerator(Iterable<T> target, int start) {
this.target = target;
this.start = start;
}
#Override
public Iterator<EnumeratedItem<T>> iterator() {
final Iterator<T> targetIterator = target.iterator();
return new Iterator<EnumeratedItem<T>>() {
int index = start;
#Override
public boolean hasNext() {
return targetIterator.hasNext();
}
#Override
public EnumeratedItem<T> next() {
EnumeratedItem<T> nextIndexedItem = new EnumeratedItem<T>(targetIterator.next(), index);
index++;
return nextIndexedItem;
}
};
}
}
public static <T> Iterable<EnumeratedItem<T>> enumerate(Iterable<T> iterable, int start) {
return new ListEnumerator<T>(iterable, start);
}
public static <T> Iterable<EnumeratedItem<T>> enumerate(Iterable<T> iterable) {
return enumerate(iterable, 0);
}
}
Strictly speaking, no, as the enumerate() function in Python returns a list of tuples, and tuples do not exist in Java.
If however, all you're interested in is printing out an index and a value, then you can follow the suggestion from Richard Fearn & use nextIndex() and next() on an iterator.
Note as well that enumerate() can be defined using the more general zip() function (using Python syntax):
mylist = list("abcd")
zip(range(len(mylist)), mylist)
gives [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')]
If you define your own Tuple class (see Using Pairs or 2-tuples in Java as a starting point), then you could certainly easily write your own zip() function in Java to make use of it (using the Tuple class defined in the link):
public static <X,Y> List<Tuple<X,Y>> zip(List<X> list_a, List<Y> list_b) {
Iterator<X> xiter = list_a.iterator();
Iterator<Y> yiter = list_b.iterator();
List<Tuple<X,Y>> result = new LinkedList<Tuple<X,Y>>();
while (xiter.hasNext() && yiter.hasNext()) {
result.add(new Tuple<X,Y>(xiter.next(), yiter.next()));
}
return result;
}
And once you have zip(), implementing enumerate() is trivial.
Edit: slow day at work, so to finish it off:
public static <X> List<Tuple<Integer,X>> enumerate (List<X> list_in) {
List<Integer> nums = new ArrayList<Integer>(list_in.size());
for (int x = 0; x < list_in.size(); x++) {
nums.add(Integer.valueOf(x));
}
return zip (nums, list_in);
}
Edit 2: as pointed out in the comments to this question, this is not entirely equivalent. While it produces the same values as Python's enumerate, it doesn't do so in the same generative fashion that Python's enumerate does. Thus for large collections this approach could be quite prohibitive.
Simple and straightforward
public static <T> void enumerate(Iterable<T> iterable, java.util.function.ObjIntConsumer<T> consumer) {
int i = 0;
for(T object : iterable) {
consumer.accept(object, i);
i++;
}
}
Sample usage:
void testEnumerate() {
List<String> strings = Arrays.asList("foo", "bar", "baz");
enumerate(strings, (str, i) -> {
System.out.println(String.format("Index:%d String:%s", i, str));
});
}
According to the Python docs (here), this is the closest you can get with Java, and it's no more verbose:
String[] numbers = {"zero", "one", "two"}
for (int i = 0; i < numbers.length; i++) // Note that length is a property of an array, not a function (hence the lack of () )
System.out.println(i + " " + numbers[i]);
}
If you need to use the List class...
List<String> numbers = Arrays.asList("zero", "one", "two");
for (int i = 0; i < numbers.size(); i++) {
System.out.println(i + " " + numbers.get(i));
}
*NOTE: if you need to modify the list as you're traversing it, you'll need to use the Iterator object, as it has the ability to modify the list without raising a ConcurrentModificationException.
Now with Java 8s Stream API together with the small ProtonPack library providing StreamUtils it can be achieved easily.
The first example uses the same for-each notation as in the question:
Stream<String> numbers = Arrays.stream("zero one two".split(" "));
List<Indexed<String>> indexedNumbers = StreamUtils.zipWithIndex(numbers)
.collect(Collectors.toList());
for (Indexed<String> indexed : indexedNumbers) {
System.out.println(indexed.getIndex() + " " + indexed.getValue());
}
Above although does not provide the lazy evaluation as in Python.
For that you must use the forEach() Stream API method:
Stream<String> numbers = Arrays.stream("zero one two".split(" "));
StreamUtils.zipWithIndex(numbers)
.forEach(n -> System.out.println(n.getIndex() + " " + n.getValue()));
The lazy evaluation can be verified with the following infinite stream:
Stream<Integer> infStream = Stream.iterate(0, i -> i++);
StreamUtils.zipWithIndex(infStream)
.limit(196)
.forEach(n -> System.out.println(n.getIndex() + " " + n.getValue()));
No. Maybe there are some libraries for supporting such a functionality. But if you resort to the standard libraries it is your job to count.
List<String> list = { "foo", "bar", "foobar"};
int i = 0;
for (String str : list){
System.out.println(i++ + str );
}
I think this should be the java functionality that resemble the python "enumerate" most, though it is quite complicated and inefficent. Basically, just map the list's indices to its elements, using ListIterator or Collector:
List<String> list = new LinkedList<>(Arrays.asList("one", "two", "three", "four"));
Map<Integer, String> enumeration = new Map<>();
ListIterator iter = list.listIterator();
while(iter.hasNext){
map.put(iter.nextIndex(), iter.next());
}
or using lambda expression:
Set<Integer, String> enumeration = IntStream.range(0, list.size()).boxed.collect(Collectors.toMap(index -> index, index -> list.get(index)));
then you can use it with an enhanced for loop:
for (Map.Entry<Integer, String> entry : enumeration.entrySet){
System.out.println(entry.getKey() + "\t" + entry.getValue());
}
By combining generics with anonymous interfaces, you can essentially create a factory method for handing enumeration. The Enumerator callback hides the messiness of the iterator underneath.
import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;
public class ListUtils2 {
public static interface Enumerator<T> {
void execute(int index, T value);
};
public static final <T> void enumerate(final List<T> list,
final Enumerator<T> enumerator) {
for (ListIterator<T> it = list.listIterator(); it.hasNext();) {
enumerator.execute(it.nextIndex(), it.next());
}
}
public static final void enumerate(final String[] arr,
final Enumerator<String> enumerator) {
enumerate(Arrays.asList(arr), enumerator);
}
public static void main(String[] args) {
String[] names = { "John", "Paul", "George", "Ringo" };
enumerate(names, new Enumerator<String>() {
#Override
public void execute(int index, String value) {
System.out.printf("[%d] %s%n", index, value);
}
});
}
}
Result
[0] John
[1] Paul
[2] George
[3] Ringo
Extended Thoughts
Map, Reduce, Filter
I have taken this a step further and created map, reduce, and filter functions based on this concept.
Both Google's Guava and Apache common-collections dependencies include similar functionality. You can check them out as you wish.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;
public class ListUtils {
// =========================================================================
// Enumerate
// =========================================================================
public static abstract interface Enumerator<T> {
void execute(int index, T value, List<T> list);
};
public static final <T> void enumerate(final List<T> list,
final Enumerator<T> enumerator) {
for (ListIterator<T> it = list.listIterator(); it.hasNext();) {
enumerator.execute(it.nextIndex(), it.next(), list);
}
}
// =========================================================================
// Map
// =========================================================================
public static interface Transformer<T, U> {
U execute(int index, T value, List<T> list);
};
public static final <T, U> List<U> transform(final List<T> list,
final Transformer<T, U> transformer) {
List<U> result = new ArrayList<U>();
for (ListIterator<T> it = list.listIterator(); it.hasNext();) {
result.add(transformer.execute(it.nextIndex(), it.next(), list));
}
return result;
}
// =========================================================================
// Reduce
// =========================================================================
public static interface Reducer<T, U> {
U execute(int index, T value, U result, List<T> list);
};
public static final <T, U> U reduce(final List<T> list,
final Reducer<T, U> enumerator, U result) {
for (ListIterator<T> it = list.listIterator(); it.hasNext();) {
result = enumerator.execute(it.nextIndex(), it.next(), result, list);
}
return result;
}
// =========================================================================
// Filter
// =========================================================================
public static interface Predicate<T> {
boolean execute(int index, T value, List<T> list);
};
public static final <T> List<T> filter(final List<T> list,
final Predicate<T> predicate) {
List<T> result = new ArrayList<T>();
for (ListIterator<T> it = list.listIterator(); it.hasNext();) {
int index = it.nextIndex();
T value = it.next();
if (predicate.execute(index, value, list)) {
result.add(value);
}
}
return result;
}
// =========================================================================
// Predefined Methods
// =========================================================================
// Enumerate
public static <T> String printTuples(List<T> list) {
StringBuffer buff = new StringBuffer();
enumerate(list, new Enumerator<T>() {
#Override
public void execute(int index, T value, List<T> list) {
buff.append('(').append(index).append(", ")
.append(value).append(')');
if (index < list.size() - 1) {
buff.append(", ");
}
}
});
return buff.toString();
}
// Map
public static List<String> intToHex(List<Integer> list) {
return transform(list, new Transformer<Integer, String>() {
#Override
public String execute(int index, Integer value, List<Integer> list) {
return String.format("0x%02X", value);
}
});
}
// Reduce
public static Integer sum(List<Integer> list) {
return reduce(list, new Reducer<Integer, Integer>() {
#Override
public Integer execute(int index, Integer value, Integer result,
List<Integer> list) {
return result + value;
}
}, 0);
}
// Filter
public static List<Integer> evenNumbers(List<Integer> list) {
return filter(list, new Predicate<Integer>() {
#Override
public boolean execute(int index, Integer value, List<Integer> list) {
return value % 2 == 0;
}
});
}
// =========================================================================
// Driver
// =========================================================================
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(8, 6, 7, 5, 3, 0, 9);
// Enumerate
System.out.printf("%-10s: %s%n", "Enumerate", printTuples(numbers));
// Map
System.out.printf("%-10s: %s%n", "Map", intToHex(numbers));
// Reduce
System.out.printf("%-10s: %d%n", "Reduce", sum(numbers));
// Filter
System.out.printf("%-10s: %s%n", "Filter", evenNumbers(numbers));
}
}
Pretty much the same syntax using Java8 Streams
ArrayList<String> numbers = new ArrayList<String>();
numbers.add("one");
numbers.add("two");
numbers.add("three");
numbers.stream().forEach(num ->
{
System.out.println(numbers.indexOf(num) + " " + num);
});