I have a program I have to do in my comp sci class that is about keep track of cookie orders (think girl scout cookies). Here is a method designed to remove a certain variety of cookie and return the number of boxes removed. It keeps going out of bounds. The ArrayList is the "orders" variable it is made up of CookieOrder object(CookieOrder class below)
public int removeVariety(String cookieVar)
{
int counter = 0;
for(int i = orders.size() - 1; i > 0; i--)
{
if(orders.get(i).getVariety().equals(cookieVar))
{
counter += orders.get(i).getNumBoxes();
orders.remove(i);
}
}
return counter;
}
Here's the CookieOrder class
private String variety;
private int boxes;
public CookieOrder (String variety, int numBoxes)
{
this.variety = variety;
boxes = numBoxes;
}
public String getVariety()
{
return variety;
}
public int getNumBoxes()
{
return boxes;
}
Don't use for loop, use an iterator and use the Iterator#remove()
You initialize the for loop variable to start from orders.size() and go down to 0. But inside the loop you remove orders, so orders.size() is decreased but the loop variable i will not notice it.
The simplest solution is to loop from 0 to orders.size() - 1 - it will be evaluated every time, so it will notice the removal.
The problem is you are modifying the list as you are looping through it at the same time. That's not going to work. If you just want to sum up some value, you don't need to remove the item from the list.
Hope this helps
Try this instead note you should really use an Iterator but I think you will not have covered that yet in your course. Specifically note the difference between i > 0 and i >= 0:
public int removeVariety(String cookieVar)
{
int counter = 0;
for (int i = orders.size() - 1; i >= 0; i--)
{
CookieOrder o = orders.get(i);
if (o.getVariety().equals(cookieVar))
{
counter += o.getNumBoxes();
orders.remove(i);
}
}
return counter;
}
What you are doing is basically traversing the list in reverse order, so that the removed elements do not affect the indices of the remaining elements.
Alternative iterator based solution
If your curious here is a solution that uses an iterator:
public int removeVariety(String cookieVar)
{
int counter = 0;
Iterator iter = orders.iterator();
while (iter.hasNext())
{
CookieOrder o = iter.next();
if (o.getVariety().equals(cookieVar))
{
counter += o.getNumBoxes();
iter.remove();
}
}
return counter;
}
If you are using Java 8:
return orders.stream()
.filter(ord -> ord.getVarienty().equals(cookieVar))
.peek(orders::remove)
.mapToInt(ord -> ord.getNumBoxes())
.sum();
or using iterator (more imperative way):
int c = 0;
Iterator<CookieOrder> it = orders.iterator();
while (it.hasNext()) {
CookieOrder current = it.next();
if(current.getVarienty().equals(cookieVar)){
it.remove();
c += current.getNumBoxes();
}
}
return c;
What you were trying to do is to bite your own tail here :)
Do not modify a collection that you are currently processing.
Related
Hey I am searching for a better method to search a String element in an array of LinkedLists.
public static void main(String[] args) {
int m = 1000;
LinkedList<String>[] arrayOfList = new LinkedList[m];
for (int i = 0; i < m; i++) {
arrayOfList[i] = new LinkedList<>();
}
}
This is my search method:
public int search(String word) {
for (int i = 0; i < m; i++) {
for (int j = 0; j < arrayOfList[i].size(); j++) {
if (arrayOfList[i].get(j).equals(word)) {
return i;
}
}
}
return -1;
}
This is how my LinkedLists look like:
Example output: arrayOfList[0] = [house,car,tree.....]
arrayOfList[1] = [computer,book,pen....]
......
until arrayOfList[1000] = [...]
My search method should find the index of my word. Example: search("computer") = 1; search("house") = 0
Ah, a classic!
LinkedList is notoriously bad for random access, i.e. the list.get(j) method.
It's much better at iterating through the list, so it can jump from each item to the next item.
You could use list.iterator(), but the foreach loop does the same thing:
public int search(String word) {
for (int i = 0; i < m; i++) {
for (String listValue: arrayOfList[i]) {
if (listValue.equals(word)) {
return i;
}
}
}
return -1;
}
The other answer notes that you can get much better performance by iterating over each LinkedList rather than using List.get. That's because List.get has to search from the start of the list each time. For example, if the LinkedList has 100 elements, then on average each call to List.get(j) will have to iterate over 50 elements, and you're doing that 100 times. The foreach loop just iterates over the LinkedList elements once.
The foreach strategy runs in O(n) time, that is, the time required to perform the lookup increases proportional to n, the total number of words, because you have to search them all for each word.
If you're going to be doing this a lot, and you can use data structures other than LinkedList, then you should iterate through your array of LinkedList once and build a HashMap where the key is the word and the value is the number of the array in which that word appears. Setting up this HashMap will require O(n) time, but subsequent lookups will only require O(1) time, meaning a constant time regardless of the number of words involved. So if you're going to do more than a single lookup, creating the HashMap will have better performance in big-O terms, although for a very small number of lookups (2 or 3) it may still be faster to scan the arrays.
You can build a HashMap like this:
Map<String, Integer> index = new HashMap<>();
for (int i = 0; i < m; i++) {
for (String word: arrayOfList[i]) {
index.put(word, i);
}
}
Now search just becomes:
public int search(String word) {
return index.getOrDefault(word, -1);
}
Depending on how the strings are constructed in your program and how often you call the search method, testing on your strings' hash code can improve the performance. ex:
public int search(String word) {
int wordHashCode = word.hashCode();
for (int i = 0; i < m; i++) {
for (String listValue: arrayOfList[i]) {
if (listValue.hashCode() == wordHashCode && listValue.equals(word)) {
return i;
}
}
}
return -1;
}
This program takes integers from user input and puts them in a collection. It then prints the positive values first, then the negative values, and doesn't print repeated numbers. It stops asking for input once the user enters 0. Here is the code:
public class Intcoll2
{
private int[] c;
private int[] d;
private int howmany = 0;
public Intcoll2()
{
c = new int[500];
}
public Intcoll2(int i)
{
c = new int[i]
}
public void insert(int i)
{
if (i > 0)
{
int j = 0;
while ((j <= howmany) && (c[j] != i)) j++;
if (j == howmany)
{
if (j == c.length - 1)
{
d = new int[2*c.length];
for (int k = 0; k<c.length; i++){
d[k] = c[k];
}
c = d;
}
c[j] = i; c[j + 1] = 0;
}
howmany++;
}
}
public int get_howmany()
{
int j=0, howmany=0;
while (c[j]!=0) {howmany++; j++;}
return howmany;
}
Now my current print method looks like this:
public void print()
{
int j = 0;
System.out.println();
while (j <= howmany)
{
System.out.println(c[j]); j++;
}
}
But when I try to use that in my client, it only prints out zeros. Any help with what I'm doing wrong would be greatly appreciated.
An answer that you were probably not looking for, but still on the only real answer you should care about.
Your problem is not that somewhere in that code a bug is hiding. The problem is that your code is confusing beyond limits:
Dont use single-character variable names.
The constructor that takes an int ... creates an empty array!
Dont say "collection" when you are using arrays.
Dont give fields and local variables the same name.
Seriously: understanding this mess is mainly complicated and hard because you wrote code that is hard to read.
Now you are asking other people to debug such complicated code that you (the author who created it!) do not understand in the first place.
Instead, you might throw this whole thing away. And slowly write it again; but in a way that isn't at all confusing to the reader.
I took a look at your class and rewrote it in a more legible manner. I didn't test it but I'm confident it works. You can check it out and hopefully understand what's happening. Hope this helps!
public class IntCollection2 {
private int[] collection; // A large allocation, not neccessarily filled up.
private int currentSize; // The number of spots currently filled in the collection.
public IntCollection2() {
collection = new int[500];
currentSize = 0;
}
public IntCollection2(int size) {
collection = new int[size];
currentSize = 0;
}
/**
* Inserts a new element into the internal array. If the current array is filled up,
* a new array double the size of the current one is allocated.
* #param element An int to insert into the collection. Must not be '0'.
* #return True if the element was successfully inserted, false if the element was
* equal to '0' and was ignored.
*/
public boolean insert(int element) {
if (element != 0) {
if (currentSize < collection.length - 1) {
collection[currentSize] = element;
} else {
int[] newCollection = new int[collection.length * 2];
for (int i = 0; i < currentSize; i++) {
newCollection[i] = collection[i];
}
newCollection[currentSize] = element;
collection = newCollection;
}
currentSize++;
return true;
} else {
return false;
}
}
/**
* Not actually necessary because the class automatically updates its currentSize
* every time a new element is inserted.
* #return The current number of filled spots in the internal array.
*/
public int getCurrentSize() {
int size = 0;
for (int i = 0; i < collection.length && collection[i] != 0; i++) {
size++;
}
return size;
}
/**
* Prints out all the elements currently in the collection, one on each line.
*/
public void print() {
System.out.println();
for (int i = 0; i < currentSize; i++) {
System.out.println(collection[i]);
}
}
}
FYI: this class just prints out every element in the collection, in order. You mentioned something about printing positive then negative values, but I leave that to you.
EDIT: I'm guessing you're brand new to programming, so I just want to clarify exactly what a collection is. An array is an ordered list of elements. When you create an array, the computer sets aside a bit of memory to hold exactly the number of elements you told it to. You cannot change the size of an existing array. A collection is basically a wrapper around an array. It makes a bigger array than it needs to hold its elements, and when its array becomes full, it allocates a new, bigger one that can hold more elements.
I have a random set S of integers and the cardinality (n) of this set may vary from 10 to 1000. I need to store all sums of the nCr combinations of size r generated from this set. Usually r range from 3 to 10.
E.g. if S={102,233,344,442,544,613,71289,836,97657,12} and r=4, Then The sums generated will be {0,1,2,3}=102+233+344+442, {0,1,2,4}=102+233+344+544,....so on.
I implemented a findCombi function (below) in Java which gave me all nCr combinations in terms of r sized sets of indices and then I sifted through these sets in another function to generate the sum of corresponding elements.
But the program is giving heapspace error, probably because of exponential nature and I have 100-5000 of such sets, S. Or may be there is a memory leak?
Is there a faster and lesser-memory consuming way to do it?
Note: dsize=n, combiSize=r
List <List<Integer>> findCombi(int dsize,int combiSize) {
if( (combiSize==0) || (dsize==0) ){
return null;
}
long n=dsize;
int r=combiSize;
for(int i=1;i<combiSize;i++) {
n=n*(dsize-i);
r=r*i;
}
int totalcombi=(int) n/r;
List <List<Integer>> combiData=new ArrayList<>(totalcombi);
int pos;
List <Integer> combi=new ArrayList<>(combiSize);
for(int i=0;i<combiSize;i++) {
combi.add(i,i);
}
combiData.add(new ArrayList<>(combi));
pos=combiSize-1;
while(true) {
if(combi.get(pos)<(dsize-combiSize+pos)) {
combi.set(pos,combi.get(pos)+1);
if(pos==(combiSize-1)) {
combiData.add(new ArrayList<>(combi));
}
else {
combi.set(pos+1,combi.get(pos));
pos++;
}
}
else {
pos--;
}
if(pos==-1) {
break;
}
}
return combiData;
}
I needed something like that earlier, so here is some code adapted from the project I made back then. The method allSums builds a list of indices of size r, which is used to represent all the possible combinations. At each step, the current sum is added to the result set, then the next combination is generated. Since the results are put in a set, there is no way a result could appear twice. I included a main method so you can see it work. I hope this is clear, feel free to ask questions.
import java.util.*;
public class Program {
static private Set<Integer> allSums(List<Integer> values, int r) {
HashSet<Integer> res = new HashSet<>();
if ((values.isEmpty()) || r > values.size()) {
return res;
}
// build the list of indices
List<Integer> li = new ArrayList<>();
for (int i = 0; i < r; i++) {
li.add(i);
}
li.add(values.size()); // artificial last index : number of elements in set
while (true) {
// add the current sum to the result
int sum = 0;
for (int i = 0; i < r; i++) {
sum += values.get(li.get(i));
}
res.add(sum);
// move to the next combination
// first, find the last index that can be incremented
int i = r-1;
while ((i >= 0) && (li.get(i) == li.get(i+1)-1)) {
i--;
}
// was such an index found ?
if (i == -1) {
break; // if not, it's over
}
// increment the last index and set all the next indices to their initial value
li.set(i,li.get(i)+1);
for (int j = i+1; j < r; j++) {
li.set(j, li.get(j-1)+1);
}
}
return res;
}
public static void main(String[] args) {
List<Integer> values = new ArrayList<>();
values.add(10);
values.add(100);
values.add(1000);
values.add(10000);
values.add(100000);
Set<Integer> s = allSums(values, 3);
for (int i : s) {
System.out.println(i);
}
}
}
public static int findLargestMark(ArrayList<Result> array)
{
int last = 0;
int largestPOS = 0;
for (int i = 1; i <= array.size(); i++)
{
for (Result s : array)
{
int num = s.getMark();
if (num > last)
{
last = num;
largestPOS = i++;
}
}
}
Does anyone have any idea why this isn't returning the position of the largest value?
I'm sorry but I'm a bit of a newbie to Java.
largestPOS = i++;
This is incrementing i which means it skips the next number. If that next number is the biggest, you'll miss it.
Your code won't compile. You need a return statement.
Your outer loop skips the first element because it starts at 1 instead of 0. Arrays and lists are 0 based.
You only need one loop to accomplish this. I'd remove the inner loop since you're trying to return the index and a foreach loop doesn't give you the index.
If your array is empty, it will set largestPOS to 0. That is not correct. Other algorithms in this situation would return -1 to mean "index not found". See String.indexOf for example.
If you want to find the largest mark, no need to reinvent the wheel. Use Collections.max and provide a custom Comparator :
Result r = Collections.max(array, new Comparator<Result>() {
#Override
public int compare(Result o1, Result o2) {
return Integer.compare(o1.getMark(), o2.getMark());
}
});
Then if you really want to find the position of this object in the list you can use indexOf :
array.indexOf(r);
Note that will return the index of the first occurrence of the specified element in the list.
If you want to get the index of the last occurrence, you can use :
array.lastIndexOf(r);
There are several reasons to this program's failure:
You need to check that your array has at least one item
You need to start the last at the initial mark, not at zero
You need to loop from one, inclusive, to array.size(), exclusive
You do not need a nested loop
You need to add a return statement
Here is how you can fix your code:
public static int findLargestMark(ArrayList<Result> array) {
if (array.size() == 0) return -1; //
int last = array.get(0).getMark();
int largestPOS = 0;
for (int i = 1; i < array.size(); i++) {
int num = array.get(i).getMark();
if (num > last) {
last = num;
largestPOS = i;
}
}
return largestPOS;
}
Because you're iterating through the same array using two nested loops. Keep it simple. Iterate only once through the entire array and find the maximum value and its index.
Try this..
public static int findLargestMark(ArrayList<Result> array)
{
int last = array.get(0).getMark();
int largestPOS = 0;
for (int i = 1; i <= array.size(); i++)
{
Result s = array.get(i);
int num = s.getMark();
if (num > last)
{
last = num;
largestPOS = i;
}
}
return largestPOS;
}
Your code even not compile, java is 0 index based. you should have received a ArrayIndexOfBoundException. However, i would just use Collections.max(array, Comparator):
Result x = Collections.max(array, new Comparator<Result>(){
#Override
public int compare(Result o1, Result o2) {
return Integer.compare(o1.getMark(), o2.getMark());
}
});
And then the index by array.indexOf(x) function, where array is an instance of type ArrayList<Result>
Im a beginner in Java and I had this doubt. Is it possible to use the Enhanced for loop in Java on an ArrayList, but start at the specified point rather than ArrayList[0].
For eg. ArrayList<Integer> calc = new ArrayList<Integer>;
// calc contains {0,1,2,3,4,5,6,7}
Can I use enhanced for loop and start iterating from calc[2] rather than calc[0]?? If possible, how can I do that?
In my particular case, using a enhanced for loop would be better, rather than a normal for loop.
The best way in Java would be like this:
for (Integer i : calc.subList(start, calc.size()) {
...
}
subList is an efficient view of the original list, so it's almost exactly what you need.
UPDATE
OK, motivated by Mikera's comments, I benchmarked it on jmh. This is the benchmarked code:
import org.openjdk.jmh.annotations.GenerateMicroBenchmark;
public class Benchmark1
{
static final List<Integer> list = new ArrayList(asList(1,2,3,4,5,6,7,8,9,10));
static { for (int i = 0; i < 5; i++) list.addAll(list); }
#GenerateMicroBenchmark
public long testIterator() {
long sum = 0;
for (int i : list) sum += i;
return sum;
}
#GenerateMicroBenchmark
public long testIndexed() {
long sum = 0;
for (int i = 0; i < list.size(); i++) sum += list.get(i);
return sum;
}
#GenerateMicroBenchmark
public long testSublistIterator() {
long sum = 0;
for (int i : list.subList(1, list.size())) sum += i;
return sum;
}
#GenerateMicroBenchmark
public long testIndexedSublist() {
long sum = 0;
final List<Integer> l = list.subList(1, list.size());
for (int i = 0; i < l.size(); i++) sum += l.get(i);
return sum;
}
}
And these are the results:
Benchmark ops/msec
-------------------------
Indexed 1860.982
IndexedSublist 1642.059
Iterator 1818.657
SublistIterator 1496.994
Conclusions:
enhanced for on the main list is as fast as indexed iteration, once past the initialization cost;
traversal of the sublist is somewhat slower than of the main list, and iteration is somewhat slower than indexed traversal;
all the differences are negligible for all practical purposes.
You're stuck using a traditional loop here...
for (int i = 2; i < calc.size(); i++) {
Integer x = calc.get(i);
}
Well, unless you're willing to create a temporary subList just for using an enhanced for loop, which is fine, because sublists are views of the original list and don't create a new list object:
for (Integer x : calc.subList(2, calc.size())) {
}
for(Item e : list.subList(1, list.size())) {
// ...
}
You could iterate through the sublist, as below:
for (Integer integerMember : calc.subList(2, calc.size()) {
// operation here
}
It should be noted that the enhanced for loop is less efficient for ArrayList then standard indexing. This is because we have to create an Iterator object and call hasNext and next on it on each step of the loop, causing unnecessary overhead.
For this reason I would recommend indexing in the traditional way rather than using the sublist approach.