In my program, I am trying to print sorted int array using stream. But I am getting false output while using normal stream. And correct details are getting printed while using int stream.
Please refer below core snippet for more details.
package com.test.sort.bubblesort;
import java.util.Arrays;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class BubbleSortWithRecursion {
public static void bubbleSort(int[] arr, int n) {
if (n < 2) {
return;
}
int prevValue;
int nextValue;
for (int index = 0; index < n-1; index++) {
prevValue = arr[index];
nextValue = arr[index+1];
if (prevValue > nextValue) {
arr[index] = nextValue;
arr[index+1] = prevValue;
}
}
bubbleSort(arr, n-1);
}
public static void main(String[] args) {
int arr[] = new int[] {10,1,56,8,78,0,12};
bubbleSort(arr, arr.length);
**//False Output** : [I#776ec8df
String output = Arrays.asList(arr)
.stream()
.map(x -> String.valueOf(x))
.collect(Collectors.joining(","));
System.out.println(output);
//Correct Output : 0,1,8,10,12,56,78
String output2 = IntStream
.of(arr)
.boxed()
.map(x -> Integer.toString(x))
.collect(Collectors.joining(","));
System.out.println(output2);
}
}
And I am getting following output on console :
[I#776ec8df
0,1,8,10,12,56,78
The fist line of output was generated using normal java stream which is not correct.
Why am I getting false content using normal JAVA stream ? Am I missing something here ?
You can solve your issue like so :
String output = Arrays.stream(arr)
.boxed()
.map(String::valueOf)
.collect(Collectors.joining(",")); // 0,1,8,10,12,56,78
Explain what happen :
when you use Arrays.asList() which look :
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
it took varargs of type T, in your case you use it for int[] Object, so Arrays.asList() will return List of int[] and not a stream of ints, so instead you have to use Arrays.stream which look like this :
public static IntStream stream(int[] array) {
return stream(array, 0, array.length);
}
to get the correct data.
Arrays.asList(arr) returns a List<int[]> whose only element is arr. Therefore streaming that List and then mapping that single element to String.valueOf(x) and collecting with Collectors.joining(",") will result in a String whose value is that single array's toString(), which is the output you see.
String output = Arrays.asList(arr) // List<int[]>
.stream() // Stream<int[]>
.map(x -> String.valueOf(x)) // Stream<String> having a single element - "[I#776ec8df"
.collect(Collectors.joining(",")); // "[I#776ec8df"
When you create an IntStream from the int array, you get a stream of the individual elements (the int values), so you can box them, convert then to Strings and join them to get the desired output.
You can make your first snippet work if you change:
int arr[] = new int[] {10,1,56,8,78,0,12};
to:
Integer arr[] = new Integer[] {10,1,56,8,78,0,12};
since this way Arrays.asList(arr) will produce a List<Integer> containing all the elements of the input array.
You can do it even a bit shorter:
String output = Arrays.stream(arr)
.mapToObj(String::valueOf)
.collect(Collectors.joining(","));
The result is 0,1,8,10,12,56,78
First you create an IntStream, then you get a Stream<String> which is collected to the final String.
Related
I am trying to implement DES code in java. I have bit stream of 64bits that I required to change it to ArrayList of Integer type.
I am able to convert it to an array as shown below.
public class strtoArr {
public static void main(final String[] args)
{
final String string = "0100010111010001000011110111110100010110110011001010001101010010";
final char[] ch=string.toCharArray();
for (final char chh: ch ) {
System.out.print(chh);
}
}
}
I want an Arraylist of integer type so that I can access each element index wise.
Why not just :
String string = "0100010111010001000011110111110100010110110011001010001101010010";
List<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < string.length(); i++) {
list.add(Integer.parseInt(String.valueOf(string.charAt(i))));
}
Here we create a list of type Integer and then iterate over each character of the string and parse it to an int.
Approach using stream API.
Arrays.stream(str.split(“”))
.map(Integer::valueOf)
.collect(toList());
or Pattern API:
Pattern.compile(“”)
.splitAsStream(str)
.map(Integer::valueOf)
.collect(toList());
I have an array list which contains objects of this class:
public class SearchCriteria {
private String key;
private String operation;
private Object value;
}
How to count length of all Strings in all objects in this ArrayList?
It can be done in foreach, but I suppose it also can be done in lambda, but I dont know how do it sneaky and modern.
now my solution is:
Integer sum=0;
for (SearchCriteria s: builder.getParams()
) {
sum+=s.getKey().length();
sum+=s.getOperation().length();
sum+=s.getValue().toString().length();
}
You can use flatMapToInt to convert a Stream<SearchCriteria > to an IntStream containing the lengths of all the properties of the elements of the original Stream:
int sum = builder.getParams()
.stream()
.flatMapToInt(sc -> IntStream.of(sc.getKey().length(),sc.getOperation().length(),sc.getValue().toString().length()))
.sum();
I don't know why this is useful to you, but you can try this:
List<SearchCriteria> criterion = builder.getParams();
int sum = criterion.stream().map(s -> s.getKey() + s.getOperation() + s.getValue())
.mapToInt(String::length).sum();
You can use:
List<SearchCriteria> list = ...
int sum = list.stream()
.flatMapToInt(crit -> Arrays.stream(new int[] {
crit.getKey().length(),
crit.getOperation().length(),
crit.getValue().toString().length()}))
.sum();
This makes an int stream of all length values and simply sums them.
You cannot sum in a straight way different things of the streamed element.
So as alternative, I would extract the computation logic in a SearchCriteria method such as :
public int computeAllLength(){
return key.length() + operation.length() + value.toString().length();
}
And I would use it in this way :
int sum = builder.getParams().stream()
.mapToInt(SearchCriteria::computeAllLength)
.sum();
Trying to understand how to use some java 8 features and was playing around with multidimensional array of objects, if I wanted to find the first instance of a value in a multidimensional array of objects.
Objects[][] someArray= .....
Arrays.stream(someArray)
.map(someArrayFirst -> Arrays.stream(someArrayFirst))
.map(unsure what to do here)
.filter(a -> a.equals("some value"))
.findFirst();
edit, thanks for the input. Just to help others out here is what I have now.
Arrays.stream(someArray)
.flatMap(someArrayFirst -> Arrays.stream(someArrayFirst))
.filter(MyCustomClass.class::isInstance)
.map(MyCustomClass.class::cast)
.filter(v -> v.value().equalsIgnoreCase("SomeString"))
.findFirst();
You are on the right track. You need to turn the two dimensions into a single stream and then take the first element that satisfies your condition:
String[][] array;
Optional<String> result = Arrays.stream(array)
.flatMap(Arrays::stream)
.filter("some value"::equals).findFirst();
The first stream produces a Stream<String[]>. The flat map turns each of the array elements into a Stream<String>. Then it's just filtering for the value you want and getting the first element that satisfies your condition.
static String[][] arr = new String[][]{{"x","y"},{"z","v"},{"b","z"}};
static String searchStr = "x";
static String searchObj = null;
public static void main(String... args) {
Arrays.stream(arr)
.forEach((subarr)->{
Optional<String> opt = Arrays.stream(subarr)
.filter((obj)->obj.equals(searchStr))
.findFirst();
if (opt.isPresent())
searchObj = opt.get();
});
System.out.println(searchObj);
}
or
static public String mapFlatMethod(String[][] arr, String searchStr) {
return Arrays.stream(arr).flatMap(row -> Stream.of(row))
.filter((obj)->obj.equals(searchStr))
.findFirst().get();
}
I got an array of elements like :
ArrayList<String> t = new ArrayList();
t.add("/folder1/sub-folder1");
t.add("/folder2/sub-folder2");
t.add("/folder1/sub-folder1/data");
I need to get output as /folder1/sub-folder1 which is mostly repeated path.
In python this can be achieved using the below function:
def getRepeatedPath(self, L):
""" Returns the highest repeated path/string in a provided list """
try:
pkgname = max(g(sorted(L)), key=lambda(x, v): (len(list(v)), -L.index(x)))[0]
return pkgname.replace("/", ".")
except:
return "UNKNOWN"
I am trying to work on equivalent lambda function in Java. I got struck and need some help in the lambda implementation.
public String mostRepeatedSubString(ArrayList<String> pathArray) {
Collections.sort(pathArray);
String mostRepeatedString = null;
Map<String,Integer> x = pathArray.stream.map(s->s.split("/")).collect(Collectors.toMap());
return mostRepeatedString;
}
Lots of tweaking, but I finally got it!
public static void main(String[] args) {
ArrayList<String> t = new ArrayList<String>();
t.add("folder1/sub-folder1");
t.add("folder2/sub-folder2");
t.add("folder1/sub-folder1/data");
System.out.println(mostRepeatedSubString(t));
}
public static String mostRepeatedSubString(List<String> pathArray) {
return pathArray
.stream()
// Split to lists of strings
.map(s -> Arrays.asList(s.split("/")))
// Group by first folder
.collect(Collectors.groupingBy(lst -> lst.get(0)))
// Find the key with the largest list value
.entrySet()
.stream()
.max((e1, e2) -> e1.getValue().size() - e2.getValue().size())
// Extract that largest list
.map(Entry::getValue)
.orElse(Arrays.asList())
// Intersect the lists in that list to find maximal matching
.stream()
.reduce(YourClassName::commonPrefix)
// Change back to a string
.map(lst -> String.join("/", lst))
.orElse("");
}
private static List<String> commonPrefix(List<String> lst1, List<String> lst2) {
int maxIndex = 0;
while(maxIndex < Math.min(lst1.size(), lst2.size())&& lst1.get(maxIndex).equals(lst2.get(maxIndex))) {
maxIndex++;
}
return lst1.subList(0, maxIndex);
}
Note that I had to remove the initial / from the paths, otherwise that character would have been used in the split, resulting in the first string in every path list being the empty string, which would always be the most common prefix. Shouldn't be too hard to do this in pre-processing though.
I try to parse a textfile which has lines which look like the following:
#KEY,_,0,1,2,_,4,5,6, ...
The #KEY is just an identifier in the beginning while the following numbers are my data which I want to store in an ArrayList<Integer>.
I have a metadata class which contains the arraylist in which I want to insert there integers:
class MetaD {
public List<Integer> key1, key2, key3 = new ArrayList<Integer>();
}
I parse the textfile line by line; when the line starts with #KEY, I want to add the elements to the key1 list. If there is an _, it should be replaced with an empty value:
if(line.startsWith("#KEY")){
metaObject.key1 = Arrays.asList(line.replace("#KEY,", "").replace("_", "").trim().split("\\s*,\\s*"));
}
I found out that this does not work with ArrayList<Integer>. key1 has to be of the type ArrayList<String> or ArrayList<Object> to make it work.
Is there a way to convert Integers in the same way?
If not, my idea would be the following:
Convert everything to an ArrayList<String>
Iterate every item of this new ArrayList and convert it with Integer.parseInt() into an Integer.
Adding this new Integer to my ArrayList<Integer>
Would there be a more efficient or better way to archive my needs?
Edit:
Since Tunaki wrote in the comments, that my idea will probably be the only possible way I tried to do the following:
if(line.startsWith("#KEY")){
List<String> channelTemp = Arrays.asList(line.replace("#KEY,", "").replace("_", "1").split("\\s*,\\s*"));
channelTemp.forEach(item -> metaObject.channel.add(Integer.parseInt(item)));
System.out.println("done");
}
Unfortunately, this throws a NullPointerException in the third line here and I don't have a clue why. I replaced _ with 1 for testing purposes to avoid a NumberFormatException. When I print out every object in the lambda function instead of adding them to my ArrayList<Integer>, I can see that all items have an Integer value. So why do I get an exception here?
Since you're almost there I'll give you a hand.
String line = "#KEY,_,0,1,2 , _,4,5,6,";
List<Integer> collect = Arrays.stream(line.replaceAll("#KEY|_", "").split(","))
.map(String::trim)
.filter(s -> !s.isEmpty())
.map(Integer::valueOf).collect(Collectors.toList());
System.out.println(collect);
EDIT
To obtain the null you can alter the mapping process like:
List<Integer> collect = Arrays.stream(line.split(","))
.skip(line.startsWith("#KEY") ? 1 : 0)
.map(String::trim)
.filter(s -> !s.isEmpty())
.map(s -> "_".equals(s) ? null : Integer.valueOf(s)).collect(Collectors.toList());
You're trying to put in list of Integer a String:
metaObject.key1 = Arrays.asList(line.replace("#KEY,", "").replace("_", "").trim().split("\\s*,\\s*"));
Here line.replace(...) and trim() return a String, and split(...) returns a String[].
Therefore Arrays.asList(...) returns a List<String> here, that's not compatible with your definition of key1 (List<Integer>).
Yes, you can convert it to List<Integer> by call Integer.valueOf(...) or Integer.parseInt(...).
But I would recommend to
Use a new instance of List instead of Arrays.asList(...) because the latest one will produce an unmodifiable collection. Sometines it's not what you want :)
Use something less specific than your own text format. What about JSON? There are a lot of libraries to simplify parsing/storing of the data.
Firstly, you should split your string with ",", then you try if your each String is an integer or not with an isIntegerMethod. If it is an integer, you can add it into the list.
public static void main(String[] args) throws IOException {
String str = "#KEY,_,0,1,2,_,4,5,9";
String [] strArr = str.split(",");
List<Integer> intList = new ArrayList<Integer>();
for (String string : strArr) {
if (isInteger(string, 10)) {
intList.add(Integer.valueOf(string));
} else {
System.out.println(string + " is not an integer");
}
}
System.out.println(intList.toString());
}
public static boolean isInteger(String s, int radix) {
if(s.isEmpty()) return false;
for(int i = 0; i < s.length(); i++) {
if(i == 0 && s.charAt(i) == '-') {
if(s.length() == 1) return false;
else continue;
}
if(Character.digit(s.charAt(i),radix) < 0) return false;
}
return true;
}