Java 8: Replace traditional for loop [closed] - java

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
What's the best & concise way to change the code given below in Java 8:
static int[] breakingRecords(int[] scores) {
int lowest = 0, highest = 0, countlow = 0, counthigh = 0;
for (int i = 0; i < scores.length; i++) {
if (i == 0) {
lowest = scores[0];
highest = scores[0];
} else {
if (scores[i] < lowest) {
lowest = scores[i];
countlow++;
} else if (scores[i] > highest) {
highest = scores[i];
counthigh++;
}
}
}
int rc[] = {counthigh, countlow};
return rc;
}
Note: I know that I can use:
IntStream.range(0, 10).forEach(
i -> {
if (i == 0) {
...
}
But to do so, I need to declare all the variables as AtomicInteger which makes the code verbose & hard to read.
There must be a better way than using AtomicIntegers. Is there?

Let's be much more simpler. You are getting the maximum and minimum values in a Stream.
IntStream provides method for that :
OptionalInt max()
OptionalInt min()
Here is a quick way of how to use it :
int[] array = {5, 6, 10, 2, 5};
OptionalInt max = Arrays.stream(array).max();
OptionalInt min = Arrays.stream(array).min();
System.out.println("min : " + min.getAsInt());
System.out.println("max : " + max.getAsInt());
Note that you get OptionalInt, so you should check for empty values, but this only occurs if the IntStream itself is empty. So you can check that before you even read the array.
EDIT: this solution was proposed before the question include the return showing that the important part is the countHigh and countLow part.

Assuming that "the best way" actually refers to the most performant way ... you are probably already there (or very close to it)!
You see, using IntStream and lambdas might help with readability, but that will not magically increase performance. To the contrary. These constructs come with a certain amount of overhead!
In other words: if your ultimate goal is to write the code that solves the underlying problem in the most efficient way, then "old school" is (most likely) your best option.
Streams play nicely for readability (when used wisely), and they can help with efficient filtering and such, and of course, when using parallelStream() you might be able to get results quicker (by using more threads). But for straight forward simple computations on some array of ints, none of these advantages apply!

You could achieve that by using a custom comparator, which accepts a predicate when to increment the counter and an access method to be able to get said counter:
public class IncrementingComparator implements Comparator<Integer>{
private final IntPredicate doIncrement;
private int count;
public IncrementingComparator(IntPredicate doIncrement){
this.doIncrement = doIncrement;
}
#Override
public int compare(Integer o1, Integer o2){
final int compareResult = o1.compareTo(o2);
if(doIncrement.test(compareResult)){
count++;
}
return compareResult;
}
public int count(){
return count;
}
}
You then could create 2 instances of above class:
final IncrementingComparator maxCmp = new IncrementingComparator(i -> i > 0);
final IncrementingComparator minCmp = new IncrementingComparator(i -> i < 0);
Which then can be used inside the min() and max() operations of Stream:
final Optional<Integer> max = Arrays.stream(array)
.boxed()
.max(maxCmp);
final Optional<Integer> min = Arrays.stream(array)
.boxed()
.min(minCmp);
After the min and max are found, you could then extract the count values from each comparator:
final int maxCount = maxCmp.count();
final int minCount = minCmp.count();
Note: Because the Comparators have an internal state, they can only be used once. (Or one could add a reset() method to set the count variable back to 0.

Related

The code showing Time Limit Exceed erroe, could some one explain, where is problem in this code [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed last year.
Improve this question
LeetCode 485
Given a binary array nums, return the maximum number of consecutive 1's in the array.
Example 1:
Input: nums = [1,1,0,1,1,1]
Output: 3
Explanation: The first two digits or the last three digits are consecutive 1s. The maximum number of consecutive 1s is 3.
---------Solution:-------
public int findMaxConsecutiveOnes(int[] nums) {
int maxConsSize = Integer.MIN_VALUE;
int i = -1, j=-1, k=0;
while(k<nums.length){
while(k<nums.length && nums[k] == 1){
k++;
i++;
}
if(nums[k] == 0){
maxConsSize = Math.max(maxConsSize,i-j);
j = i;
}
}
maxConsSize = Math.max(maxConsSize,i-j);
return maxConsSize;
}
Warning: This is not direct answer (for this "do my homework" question)
You should use (or learn to use) debugger in your IDE (trust me, IDE, e.g. Eclipse will help you a lot in your beginnings).
The easiest (I'm not saying smartest) way, how to know what the program is doing (when you need to know, like in this case) is to add some print statements, e.g. add System.out.println("k=" + k) into your program (in a while loop).
You might want to watch this youtube video.
You have an infinity loop. Try run this:
public class Test {
public static void main(String[] args) {
int maxConsSize = Integer.MIN_VALUE;
int[] nums = {1,1,0,1,1,1};
int i = -1, j=-1, k=0;
System.out.println(nums.length);
while(k<nums.length){
while(k<nums.length && nums[k] == 1){
k++;
i++;
System.out.println("k = " + k);
}
if(nums[k] == 0){
maxConsSize = Math.max(maxConsSize,i-j);
j = i;
}
}
maxConsSize = Math.max(maxConsSize,i-j);
System.out.println(maxConsSize);
}
}
Output:
6
k = 1
k = 2
After reading the first 0 you are in infinite loop. You have made this task very complicated :)
It's probably not the best solution, but it should be faster
public int findMaxConsecutiveOnes(int[] nums) {
int maxCons = 0;
int currentCons = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == 0) {
if (currentCons > maxCons) {
maxCons = currentCons;
}
currentCons = 0;
} else {
currentCons++;
}
}
if (currentCons > maxCons) {
maxCons = currentCons;
}
return maxCons;
}
}
There are two basic forms of loops:
for-each, for-i or sometimes called ranged for
Use that for a countable number of iterations.
For example having an array or collection to loop through.
while and do-while (like until-loops in other programming languages)
Use that for something that has a dynamic exit-condition. Bears the risk for infinite-loops!
Your issue: infinite loop
You used the second form of a while for a typical use-case of the first. When iterating over an array, you would be better to use any kind of for loop.
The second bears always the risk of infinite-loops, without having a proper exit-condition, or when the exit-condition is not fulfilled (logical bug). The first is risk-free in that regard.
Recommendation to solve
Would recommend to start with a for-i here:
// called for-i because the first iterator-variable is usually i
for(int i=0; i < nums.length, i++) {
// do something with num[i]
System.out.println(num[i]):
}
because:
it is safer, no risk of infinite-loop
the iterations can be recognized from the first line (better readability)
no counting, etc. inside the loop-body
Even simpler and idiomatic pattern is actually to use a for each:
for(int n : nums) {
// do something with n
System.out.println(n):
}
because:
it is safer, no risk of infinite-loop
the iterations can be recognized from the first line (better readability)
no index required, suitable for arrays or lists
no counting at all
See also:
Java For Loop, For-Each Loop, While, Do-While Loop (ULTIMATE GUIDE), an in-depth tutorial covering all about loops in Java, including concepts, terminology, examples, risks

Recursive function that finds the minimum value in an ArrayList of Integers in Java

This is a problem that I have been thinking about as part of self-learning java. The problem consists of writing a recursive function that finds the minimum value in an ArrayList of Integers. Below you will find my attempt. I believe that it is working as intended, but I wonder if there are better ways to get this done. Any comments are appreciated.
public static int findMin(ArrayList<Integer> numbers){
// Base Case
if(numbers.size()==1){
return numbers.get(0).intValue();
}
ArrayList<Integer> numbers_short = new ArrayList<Integer>(numbers);
numbers.remove(numbers.size()-1);
return Math.min(numbers_short.get(numbers_short.size()-1).intValue(), findMin(numbers));
}
Your example is not so good in the way that you should not use recursivity in this case.
But anyway, you can avoid to copy your array each time, by using a method with a start and end parameters to analyze only a part of your initial array.
Something like that:
public static int findMin(ArrayList<Integer> numbers) {
return findMin(numbers, 0, numbers.size() - 1);
}
public static int findMin(ArrayList<Integer> numbers, int start, int end) {
if (end == start)
return numbers.get(start);
int middle = start + (end - start) / 2;
return Math.min(findMin(numbers, start, middle), findMin(numbers, middle + 1, end));
}
And add a check in case the array is empty if needed.
The reason why I'm using the "middle" method is that each time it divides the array by 2, meaning at the end it limits the risk of stack overflow because it will divide by 2 the maximum number of recursivity compare to recurse on every element.
You may want to avoid creating a new ArrayList object every time you call the method. This may seem non consequential for smaller inputs, but for really large inputs it may lead to StackOverflowError.
A cleaner solution with similar time complexity can be:
public static int findMin(ArrayList<Integer> numbers) {
return findMin(numbers, numbers.size());
}
public static int findMin(ArrayList<Integer> numbers, int len) {
// Base Case
if (len == 1) {
return numbers.get(0);
}
return Math.min(numbers.get(len-1), findMin(numbers, len-1));
}
Reference to garbage collector and recursion: Garbage Collection in Java with Recursive Function
Keeping the original method signature and formal parameters - the code can be simplified by eliminating the need to create a new temporary array each time.
public static int findMin(ArrayList<Integer> numbers){
if (numbers.size() == 1) return numbers.get(0);
return Math.min(numbers.remove(0), findMinimum(numbers));
}
numbers.remove(0) will simultaneously remove the first element and return the value of the removed element, eliminating the need to create a temporary array.

Why using stack container is slower?

I 'm trying to solve problem 739, Daily Temperatures on LeetCode.
https://leetcode.com/problems/daily-temperatures/
My code used Stack container provided by the JAVA. It takes 60 ms to run. This is my code:
class Solution {
public int[] dailyTemperatures(int[] T) {
int[] ret = new int[T.length];
Stack<Integer> stack = new Stack<Integer>();
for(int i=0; i < T.length; i++){
while(!stack.isEmpty() && T[i] > T[stack.peek()]){
int index = stack.pop();
ret[index] = i - index;
}
stack.push(i);
}
return ret;
}
}
Here is a code that only takes 6ms to run:
class Solution {
public int[] dailyTemperatures(int[] T) {
int[] temperatures = T;
if(temperatures == null) return null;
int[] result = new int[temperatures.length];
int[] stack = new int[temperatures.length];
int top = 0;
stack[top] = -1;
for(int i = 0; i < temperatures.length; i++) {
while(stack[top] != -1 && temperatures[i] > temperatures[stack[top]]) {
int index = stack[top--];
result[index] = i - index;
}
stack[++top] = i;
}
return result;
}
}
Why building the stack using an array is faster than using the stack container?
Java's Stack is a very old class, introduced back in JDK 1.0. It extends Vector, and all it's data manipulation methods are synchronized, creating a very sizeable performance overhead. While it isn't officially deprecated, it's outdated, and you really shouldn't be using it in this day and age. The modern ArrayDeque provides the same functionality without the synchronization overhead.
Tested in the leetcode environment:
the first Stack[Integer] solution takes 80ms to run, and changing Stack[Integer] to ArrayDeque[Integer] takes 31ms. Which is a great improvement, which can prove that Stack is much slower than the morden ArrayDeque.
Note that only the pop method and peek are synchronized, while the push is not.
the second array[] solution takes 10ms in my run. and chaning to Integer[]
takes 19ms. So I think autoboxing is also a factor.
So there are not a single reason for this.
Only profiling will show you exactly which effects are the source of the slower running time.
My best guest would be the creation of Boxing-Integer-Intances for the int values and the much more complex implementation of
stack.pop() / stack.peek() / stack.push() in contrast to elementary array-accesses.
You could try to change your array version to use Integer instead and look how the performance changes.

Java - Return random index of specific character in string

So given a string such as: 0100101, I want to return a random single index of one of the positions of a 1 (1, 5, 6).
So far I'm using:
protected int getRandomBirthIndex(String s) {
ArrayList<Integer> birthIndicies = new ArrayList<Integer>();
for (int i = 0; i < s.length(); i++) {
if ((s.charAt(i) == '1')) {
birthIndicies.add(i);
}
}
return birthIndicies.get(Randomizer.nextInt(birthIndicies.size()));
}
However, it's causing a bottle-neck on my code (45% of CPU time is in this method), as the strings are over 4000 characters long. Can anyone think of a more efficient way to do this?
If you're interested in a single index of one of the positions with 1, and assuming there is at least one 1 in your input, you can just do this:
String input = "0100101";
final int n=input.length();
Random generator = new Random();
char c=0;
int i=0;
do{
i = generator.nextInt(n);
c=input.charAt(i);
}while(c!='1');
System.out.println(i);
This solution is fast and does not consume much memory, for example when 1 and 0 are distributed uniformly. As highlighted by #paxdiablo it can perform poorly in some cases, for example when 1 are scarce.
You could use String.indexOf(int) to find each 1 (instead of iterating every character). I would also prefer to program to the List interface and to use the diamond operator <>. Something like,
private static Random rand = new Random();
protected int getRandomBirthIndex(String s) {
List<Integer> birthIndicies = new ArrayList<>();
int index = s.indexOf('1');
while (index > -1) {
birthIndicies.add(index);
index = s.indexOf('1', index + 1);
}
return birthIndicies.get(rand.nextInt(birthIndicies.size()));
}
Finally, if you need to do this many times, save the List as a field and re-use it (instead of calculating the indices every time). For example with memoization,
private static Random rand = new Random();
private static Map<String, List<Integer>> memo = new HashMap<>();
protected int getRandomBirthIndex(String s) {
List<Integer> birthIndicies;
if (!memo.containsKey(s)) {
birthIndicies = new ArrayList<>();
int index = s.indexOf('1');
while (index > -1) {
birthIndicies.add(index);
index = s.indexOf('1', index + 1);
}
memo.put(s, birthIndicies);
} else {
birthIndicies = memo.get(s);
}
return birthIndicies.get(rand.nextInt(birthIndicies.size()));
}
Well, one way would be to remove the creation of the list each time, by caching the list based on the string itself, assuming the strings are used more often than they're changed. If they're not, then caching methods won't help.
The caching method involves, rather than having just a string, have an object consisting of:
current string;
cached string; and
list based on the cached string.
You can provide a function to the clients to create such an object from a given string and it would set the string and the cached string to whatever was passed in, then calculate the list. Another function would be used to change the current string to something else.
The getRandomBirthIndex() function then receives this structure (rather than the string) and follows the rule set:
if the current and cached strings are different, set the cached string to be the same as the current string, then recalculate the list based on that.
in any case, return a random element from the list.
That way, if the list changes rarely, you avoid the expensive recalculation where it's not necessary.
In pseudo-code, something like this should suffice:
# Constructs fastie from string.
# Sets cached string to something other than
# that passed in (lazy list creation).
def fastie.constructor(string s):
me.current = s
me.cached = s + "!"
# Changes current string in fastie. No list update in
# case you change it again before needing an element.
def fastie.changeString(string s):
me.current = s
# Get a random index, will recalculate list first but
# only if necessary. Empty list returns index of -1.
def fastie.getRandomBirthIndex()
me.recalcListFromCached()
if me.list.size() == 0:
return -1
return me.list[random(me.list.size())]
# Recalculates the list from the current string.
# Done on an as-needed basis.
def fastie.recalcListFromCached():
if me.current != me.cached:
me.cached = me.current
me.list = empty
for idx = 0 to me.cached.length() - 1 inclusive:
if me.cached[idx] == '1':
me.list.append(idx)
You also have the option of speeding up the actual searching for the 1 character by, for example, useing indexOf() to locate them using the underlying Java libraries rather than checking each character individually in your own code (again, pseudo-code):
def fastie.recalcListFromCached():
if me.current != me.cached:
me.cached = me.current
me.list = empty
idx = me.cached.indexOf('1')
while idx != -1:
me.list.append(idx)
idx = me.cached.indexOf('1', idx + 1)
This method can be used even if you don't cache the values. It's likely to be faster using Java's probably-optimised string search code than doing it yourself.
However, you should keep in mind that your supposed problem of spending 45% of time in that code may not be an issue at all. It's not so much the proportion of time spent there as it is the absolute amount of time.
By that, I mean it probably makes no difference what percentage of the time being spent in that function if it finishes in 0.001 seconds (and you're not wanting to process thousands of strings per second). You should only really become concerned if the effects become noticeable to the user of your software somehow. Otherwise, optimisation is pretty much wasted effort.
You can even try this with best case complexity O(1) and in worst case it might go to O(n) or purely worst case can be infinity as it purely depends on Randomizer function that you are using.
private static Random rand = new Random();
protected int getRandomBirthIndex(String s) {
List<Integer> birthIndicies = new ArrayList<>();
int index = s.indexOf('1');
while (index > -1) {
birthIndicies.add(index);
index = s.indexOf('1', index + 1);
}
return birthIndicies.get(rand.nextInt(birthIndicies.size()));
}
If your Strings are very long and you're sure it contains a lot of 1s (or the String you're looking for), its probably faster to randomly "poke around" in the String until you find what you are looking for. So you save the time iterating the String:
String s = "0100101";
int index = ThreadLocalRandom.current().nextInt(s.length());
while(s.charAt(index) != '1') {
System.out.println("got not a 1, trying again");
index = ThreadLocalRandom.current().nextInt(s.length());
}
System.out.println("found: " + index + " - " + s.charAt(index));
I'm not sure about the statistics, but it rare cases might happen that this Solution take much longer that the iterating solution. On case is a long String with only a very few occurrences of the search string.
If the Source-String doesn't contain the search String at all, this code will run forever!
One possibility is to use a short-circuited Fisher-Yates style shuffle. Create an array of the indices and start shuffling it. As soon as the next shuffled element points to a one, return that index. If you find you've iterated through indices without finding a one, then this string contains only zeros so return -1.
If the length of the strings is always the same, the array indices can be static as shown below, and doesn't need reinitializing on new invocations. If not, you'll have to move the declaration of indices into the method and initialize it each time with the correct index set. The code below was written for strings of length 7, such as your example of 0100101.
// delete this and uncomment below if string lengths vary
private static int[] indices = { 0, 1, 2, 3, 4, 5, 6 };
protected int getRandomBirthIndex(String s) {
int tmp;
/*
* int[] indices = new int[s.length()];
* for (int i = 0; i < s.length(); ++i) indices[i] = i;
*/
for (int i = 0; i < s.length(); i++) {
int j = randomizer.nextInt(indices.length - i) + i;
if (j != i) { // swap to shuffle
tmp = indices[i];
indices[i] = indices[j];
indices[j] = tmp;
}
if ((s.charAt(indices[i]) == '1')) {
return indices[i];
}
}
return -1;
}
This approach terminates quickly if 1's are dense, guarantees termination after s.length() iterations even if there aren't any 1's, and the locations returned are uniform across the set of 1's.

Java fastest way to get matching range

I have a set of integer ranges, which represent lower and upper bounds of classes. For example:
0..500 xsmall
500..1000 small
1000..1500 medium
1500..2500 large
In my case there can be over 500 classes. These classes do not overlap, but they can differ in size.
I can implement finding the matching range as a simple linear search through a list, for example
class Range
{
int lower;
int upper;
String category;
boolean contains(int val)
{
return lower <= val && val < upper;
}
}
public String getMatchingCategory(int val)
{
for (Range r : listOfRanges)
{
if (r.contains(val))
{
return r.category;
}
}
return null;
}
However, this seems slow; as I need on average N/2 look-ups. If the classes were equally sized, I could use division. Is there a standard technique to find the correct range faster?
What you are looking for is a SortedMap and its methods tailMap and firstKey. Check out the documentation for full details.
The advantage of this approach over plain arrays is in the ease of maintaining your ranges: you can insert/remove new boundaries at any point with almost no runtime cost; with arrays it means copying both parallel arrays in full.
Update
I've written code for both variants and benchmarked it:
#State(Scope.Thread)
#OutputTimeUnit(TimeUnit.MICROSECONDS)
public class BinarySearch
{
static final int ARRAY_SIZE = 128, INCREMENT = 1000;
static final int[] arrayK = new int[ARRAY_SIZE];
static final String[] arrayV = new String[ARRAY_SIZE];
static final SortedMap<Integer,String> map = new TreeMap<>();
static {
for (int i = 0, j = 0; i < arrayK.length; i++) {
arrayK[i] = j; arrayV[i] = String.valueOf(j);
map.put(j, String.valueOf(j));
j += INCREMENT;
}
}
final Random rnd = new Random();
int rndInt;
#Setup(Level.Invocation) public void nextInt() {
rndInt = rnd.nextInt((ARRAY_SIZE-1)*INCREMENT);
}
#GenerateMicroBenchmark
public String array() {
final int i = Arrays.binarySearch(arrayK, rndInt);
return arrayV[i >= 0? i : -(i+1)];
}
#GenerateMicroBenchmark
public String sortedMap() {
return map.tailMap(rndInt).values().iterator().next();
}
}
Benchmark results:
Benchmark Mode Thr Cnt Sec Mean Mean error Units
array thrpt 1 5 5 10.948 0.033 ops/usec
sortedMap thrpt 1 5 5 5.752 0.070 ops/usec
Interpretation: array search is only twice as fast and this factor is quite stable across array sizes. In the presented code the array size is 1024 and the factor is 1.9. I've also tested with array size 128, where the factor is 2.05.
Here, Arrays.binarySearch is your friend. Simply put all the boundaries in and handle the possible cases. Assuming you ranges leave no holes between them, you only need to put the upper bounds in.
For you example
0..500 xsmall
500..1000 small
1000..1500 medium
1500..2500 large
you'd use
int[] boundaries = {500, 1000, 1500, 2500};
and look up the input. Handle the two cases (found/not found) and you're done. Forget about ranges, they're nice but they don't fit you problem.
Update
I also wrote a benchmark and no matter how I try I'd lose my bet as the ratio is about 3 rather than 5. The strange things like S001024 in my results stand for the size 1024.

Categories