How can I get the Observation method to increase a single element in the numberofObs array by 1, every time I use it?
I get this error: Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
public class Database {
private ArrayList <String> databaseOfBirds = new ArrayList<String>();
private int[] numberofObs = new int[databaseOfBirds.size()];
public void add(String name, String latinName) {
databaseOfBirds.add(name + "(" + latinName + ")");
}
public String Observation(String birdname){
for(int i = 0; i < databaseOfBirds.size(); i++) {
if(databaseOfBirds.get(i).contains(birdname)) {
numberofObs[i]++;
return "";
}
}
return "Is not a bird!";
}
}
When you create your numberofObs array it's length is zero, since at that point databaseOfBirds is empty. Unless you're growing the array in some unlisted code you'll get a NullPointerException the first time you find a bird.
You could fix this by making numberofObs a List:
private List<Integer> numberofObs = new ArrayList<>();
Then in your add method add the line:
numberofObs.add(0);
Finally, change
numberofObs[i]++;
to
numberofObs.set(i, 1 + numberofObs.get(i));
Implementation is wrong in 2 places:
numberOfObs assignment will trigger ArrayIndexOutOfBoundsException since you are not resizing it each time a new bird is added to database
you are not increasing anything on the matching bird counter
I am understanding you want to have a separate counter for each bird matched by Observation. Here you are (there are several approaches in terms of data structures, taking the one easiest to understand):
public class Database {
private List<String> databaseOfBirds = new ArrayList<>();
private List<Integer> numberofObs = new ArrayLidt<>();
public void add(String name, String latinName) {
databaseOfBirds.add(name + "(" + latinName + ")");
numberofObs.add(0);
}
public String Observation(String birdname){
for(int i = 0; i < databaseOfBirds.size(); i++) {
if (databaseOfBirds.get(i).contains(birdname)) {
numberofObs.set(i, numberofObs.get(i)+1);
System.out.println(numberofObs.get(i));
return "";
}
}
return "Is not a bird!";
}
}
I suggest using a Map instead of 2 Lists.
private Map<String, Long> birds = new HashMap<>();
Easier to manage. Unless memory is scarce, you probably don't want to use int[].
I have a String arraylist called 'hand' that takes random elements from 3 different String arrays, PEOPLE, WEAPONS AND ROOMS. Is there any way I can determine if the arraylist has all but one element from each category? So if 'hand' contains 8 of the 9 Strings in the String array ROOMS, it will return the string that it doesn't have for that array? This method should only apply if 'hand' is missing EXACTLY 1 element from the specified array. If it's missing more than one element from a specified array, it shouldn't do anything.
import java.util.ArrayList;
import java.util.List;
public class Main {
public List<String> hand = new ArrayList<String>();
public final static String[] PEOPLE = new String[] {
"Miss. Scarlet",
"Mrs. Peacock",
"Colonel Mustard",
"Professor Plum",
"Mrs. White",
"Mr. Green"
};
public final static String[] WEAPONS = new String[] {
"Wrench",
"Candlestick",
"Pipe",
"Rope",
"Revolver",
"Knife"
};
public final static String[] ROOMS = new String[] {
"Library",
"Kitchen",
"Study",
"Conservatory",
"Ballroom",
"Lounge",
"Hall",
"Billiard Room",
"Dining Room"
};
public Main() {
hand.add("Library");
hand.add("Lounge");
hand.add("Wrench");
hand.add("Miss. Scarlet");
hand.add("Mrs. Peacock");
hand.add("Colonel Mustard");
hand.add("Professor Plum");
hand.add("Mrs. White");
}
public static void main(String[] args) {
Main main = new Main();
}
}
I guess this is what you're looking for: use removeAll() method on a List.
So, convert your arrays to List with Arrays.asList(..).
Than removeAll hands collection from each of your former arrays. If the size of the remaining List is 1 - this is what you are looking for.
List<String> peoples = new ArrayList<>(Arrays.asList(PEOPLE));
peoples.removeAll(hands);
if (peoples.size() == 1)
{
// here your hands List contained all items from PEOPLE, except 1
}
Declare a method that takes two parameters : the constant list and your hand and that will return a String : the missing String element in your hand if it is the last one missing or else null.
Call this method three times by passing at each time your hand and one of the three constant lists.
That's all.
In code it could give :
public String findLastMissingElement(String[] constants, List<String> hand){
String missingElement = null;
for (String constant : constants){
if (!hand.contains(constant) && missingElement==null){
missingElement = constant;
}
else if (!hand.contains(constant)){
return null;
}
}
return missingElement;
}
And you could call it in this way :
String missingPeople = findLastMissingElement(PEOPLE, hand);
String missingWeapon = findLastMissingElement(WEAPONS, hand);
String missingRoom = findLastMissingElement(ROOMS, hand);
I am comparing three arrays of Strings using the two classes below. Without using any hash maps or changing the structure of my code too much (I can't change the signature of findMatchingElements()), is there a way to minimize the number of comparisons that my method makes, in order to construct the new array of shared elements?
In TestRun.java I tested my code on three arrays with 8 elements each, which resulted in 46 comparisons made. I want to achieve a lower number of comparisons. Is there a way?
I tried using the remove() method to remove a string from the collection once it was successfully compared to a matching element from the query collection. That prevented some redundant comparisons, but it did not result in a significant reduction.
import java.util.*;
public class CommonElements {
int originalCollectionCount = 0;
Object[] originalCollections;
int listCount = 1;
int matchCount;
int comparisonCount = 0;
public Comparable[] findMatchingItems(Object[] collections)
{
String[] queryArray = (String[])collections[0];
String[] secondaryArray = (String[])collections[1];
ArrayList<String> queryList = new ArrayList(Arrays.asList(queryArray));
ArrayList<String> secondaryList = new ArrayList(Arrays.asList(secondaryArray));
ArrayList<String> commonList = new ArrayList();
int i = 0;
if(listCount == 1){
originalCollectionCount = collections.length;
originalCollections = collections;
}
listCount ++;
for(String x:queryList)
{
for(String y:secondaryList)
{
comparisonCount++;
if(x.compareTo(y) == 0)
{
commonList.add(x); //add mutually shared item to commonList
secondaryList.remove(y); //remove mutually shared item from consideration
if(originalCollectionCount == listCount) //if every list has been examined
{
System.out.println(commonList.get(i));
}
i++;
break;
}
}
}
String[] commonListResult = new String[commonList.size()];
commonList.toArray(commonListResult);
if(originalCollectionCount > listCount){
findMatchingItems(new Object[] {commonListResult,originalCollections[listCount]});}
if (collections.length == 0) {
return new Comparable[0];
} else if (collections.length == 1) {
return (Comparable[]) collections[0];
}
return commonListResult;
}
public int getComparisons(){
return comparisonCount;}
}
public class TestRun {
private final static String[] COLLECTION_5_1 = {"Pittsburgh", "New York", "Chicago", "Cleveland", "Miami", "Dallas", "Atlanta", "Detroit"};
private final static String[] COLLECTION_5_2 = {"Dallas", "Atlanta", "Cleveland", "Chicago", "Washington", "Houston", "Baltimore", "Denver"};
private final static String[] COLLECTION_5_3 = {"Chicago", "Kansas City", "Cleveland", "Jacksonville", "Atlanta", "Tampa Bay", "Dallas", "Seattle"};
public static void main(String[] args) {
new TestRun();
}
public TestRun() {
CommonElements commonElements = new CommonElements();
Object[] input = new Object[3];
input[0] = COLLECTION_5_1;
input[1] = COLLECTION_5_2;
input[2] = COLLECTION_5_3;
System.out.println("Matching items:");
commonElements.findMatchingItems(input);
System.out.println(commonElements.comparisonCount + " comparisons made.");
}
}
You could run a single advanced for loop as below provide the length for both array are same if not run throug it accordingly.
for(String str:arrayStr1){
if(arrayStr2.contains(str)){
newArray.add(str);
}
}
List<String> list_5_1 = new ArrayList<>(Arrays.asList(COLLECTION_5_1));
//[Pittsburgh, New York, Chicago, Cleveland, Miami, Dallas, Atlanta, Detroit]
List<String> list_5_2 = new ArrayList<>(Arrays.asList(COLLECTION_5_2));
//[Dallas, Atlanta, Cleveland, Chicago, Washington, Houston, Baltimore, Denver]
list_5_1.retainAll(list_5_2);
//[Chicago, Cleveland, Dallas, Atlanta]
We have to pass list returned from Arrays.asList, as Arrays.asList method returns only immutable list.
am comparing three arrays of Strings using the two classes below. Without using any hash maps or changing the structure of my code too much (I can't change the signature of findMatchingElements()), is there a way to minimize the number of comparisons that my method makes, in order to construct the new array of shared elements?
Sure. Your nested loops have complexity of O(m*n). When you create a temporary HashMap, you can reduce it to O(m+n) and gain a lot for big inputs. From practical POV, somewhere around length of 10 it should get faster than your solution.
I'm giving no code as it's too straightforward.
Hey guys I'm trying to get the concept of recursion down by making a program that generates String of an ArrayList recursively. My basic algorithm is:
public static ArrayList<String> generateListOfAll1sStrings(int maxBits)
terminal condition: if maxBits is 1, return the simplest case: a list containing just "1"
otherwise:
recursively call generateListOfAll1sStrings() for the next-smallest bit-length, saving the list that is returned
find the longest string in that list and create a new string with "1" appended to it (making the next-longest string)
return a new list that contains all the elements of the shorter list along with the new string just added.
The code I have so far is:
package bincomb.model;
import java.util.ArrayList;
public class BinaryCombinationGenerator {
public static ArrayList<String> generateListOfAll1sStrings(int maxBits) {
String string = null;
ArrayList<String> listofJust1 = new ArrayList<String>();
ArrayList<String> otherArray = new ArrayList<String>();
int i = 1;
if (maxBits == 1) {
listofJust1.add("1");
return listofJust1;
}
if (maxBits > 1) {
for (String string2 : listofJust1) {
String comp = "";
if (!(comp.equals(string2))) {
comp = string2;
}
string = comp;
}
listofJust1.add(i, (string + "1"));
i++;
listofJust1 = BinaryCombinationGenerator.generateListOfAll1sStrings((maxBits-1));
System.out.println(listofJust1);
return listofJust1;
}
return listofJust1;
}
public static void main(String[] args) {
generateListOfAll1sStrings(10);
}
}
However, currently, I'm returning an IndexOutOfBoundsException. I think my for loop is causing the problem, but I'm not certain how to go about fixing it.
You're getting an java.lang.IndexOutOfBoundsException at this line listofJust1.add(i, (string + "1"));.
This is because the method list.add(index, objects) tries to add the object at index "1" but your array has 0 elements.
Either change it to listofJust1.add(i-1, (string + "1")); or simply listofJust1.add((string + "1"));
#Edit: here:
listofJust1.add(i, (string + "1"));
You want to add the string for the current (N) level of recursion but below you substitute this array with:
listofJust1 = BinaryCombinationGenerator.generateListOfAll1sStrings((maxBits-1));
Which basically says "get the result for (maxBits-1) and replace with it listofJust1" therefore you are losing what you added before.
Instead you should first get the list for level N-1 and then add the string for the current level:
listofJust1 = BinaryCombinationGenerator.generateListOfAll1sStrings((maxBits-1));
listofJust1.add(stringForThisLevel);
Also you need to rething how you are computing "string" at level N, doesn't seem right.
Is there a way in Java's for-each loop
for(String s : stringArray) {
doSomethingWith(s);
}
to find out how often the loop has already been processed?
Aside from using the old and well-known for(int i=0; i < boundary; i++) - loop, is the construct
int i = 0;
for(String s : stringArray) {
doSomethingWith(s);
i++;
}
the only way to have such a counter available in a for-each loop?
No, but you can provide your own counter.
The reason for this is that the for-each loop internally does not have a counter; it is based on the Iterable interface, i.e. it uses an Iterator to loop through the "collection" - which may not be a collection at all, and may in fact be something not at all based on indexes (such as a linked list).
There is another way.
Given that you write your own Index class and a static method that returns an Iterable over instances of this class you can
for (Index<String> each: With.index(stringArray)) {
each.value;
each.index;
...
}
Where the implementation of With.index is something like
class With {
public static <T> Iterable<Index<T>> index(final T[] array) {
return new Iterable<Index<T>>() {
public Iterator<Index<T>> iterator() {
return new Iterator<Index<T>>() {
index = 0;
public boolean hasNext() { return index < array.size }
public Index<T> next() { return new Index(array[index], index++); }
...
}
}
}
}
}
The easiest solution is to just run your own counter thus:
int i = 0;
for (String s : stringArray) {
doSomethingWith(s, i);
i++;
}
The reason for this is because there's no actual guarantee that items in a collection (which that variant of for iterates over) even have an index, or even have a defined order (some collections may change the order when you add or remove elements).
See for example, the following code:
import java.util.*;
public class TestApp {
public static void AddAndDump(AbstractSet<String> set, String str) {
System.out.println("Adding [" + str + "]");
set.add(str);
int i = 0;
for(String s : set) {
System.out.println(" " + i + ": " + s);
i++;
}
}
public static void main(String[] args) {
AbstractSet<String> coll = new HashSet<String>();
AddAndDump(coll, "Hello");
AddAndDump(coll, "My");
AddAndDump(coll, "Name");
AddAndDump(coll, "Is");
AddAndDump(coll, "Pax");
}
}
When you run that, you can see something like:
Adding [Hello]
0: Hello
Adding [My]
0: Hello
1: My
Adding [Name]
0: Hello
1: My
2: Name
Adding [Is]
0: Hello
1: Is
2: My
3: Name
Adding [Pax]
0: Hello
1: Pax
2: Is
3: My
4: Name
indicating that, rightly so, order is not considered a salient feature of a set.
There are other ways to do it without a manual counter but it's a fair bit of work for dubious benefit.
Using lambdas and functional interfaces in Java 8 makes creating new loop abstractions possible. I can loop over a collection with the index and the collection size:
List<String> strings = Arrays.asList("one", "two","three","four");
forEach(strings, (x, i, n) -> System.out.println("" + (i+1) + "/"+n+": " + x));
Which outputs:
1/4: one
2/4: two
3/4: three
4/4: four
Which I implemented as:
#FunctionalInterface
public interface LoopWithIndexAndSizeConsumer<T> {
void accept(T t, int i, int n);
}
public static <T> void forEach(Collection<T> collection,
LoopWithIndexAndSizeConsumer<T> consumer) {
int index = 0;
for (T object : collection){
consumer.accept(object, index++, collection.size());
}
}
The possibilities are endless. For example, I create an abstraction that uses a special function just for the first element:
forEachHeadTail(strings,
(head) -> System.out.print(head),
(tail) -> System.out.print(","+tail));
Which prints a comma separated list correctly:
one,two,three,four
Which I implemented as:
public static <T> void forEachHeadTail(Collection<T> collection,
Consumer<T> headFunc,
Consumer<T> tailFunc) {
int index = 0;
for (T object : collection){
if (index++ == 0){
headFunc.accept(object);
}
else{
tailFunc.accept(object);
}
}
}
Libraries will begin to pop up to do these sorts of things, or you can roll your own.
Java 8 introduced the Iterable#forEach() / Map#forEach() method, which is more efficient for many Collection / Map implementations compared to the "classical" for-each loop. However, also in this case an index is not provided. The trick here is to use AtomicInteger outside the lambda expression. Note: variables used within the lambda expression must be effectively final, that is why we cannot use an ordinary int.
final AtomicInteger indexHolder = new AtomicInteger();
map.forEach((k, v) -> {
final int index = indexHolder.getAndIncrement();
// use the index
});
I'm afraid this isn't possible with foreach. But I can suggest you a simple old-styled for-loops:
List<String> l = new ArrayList<String>();
l.add("a");
l.add("b");
l.add("c");
l.add("d");
// the array
String[] array = new String[l.size()];
for(ListIterator<String> it =l.listIterator(); it.hasNext() ;)
{
array[it.nextIndex()] = it.next();
}
Notice that, the List interface gives you access to it.nextIndex().
(edit)
To your changed example:
for(ListIterator<String> it =l.listIterator(); it.hasNext() ;)
{
int i = it.nextIndex();
doSomethingWith(it.next(), i);
}
Idiomatic Solution:
final Set<Double> doubles; // boilerplate
final Iterator<Double> iterator = doubles.iterator();
for (int ordinal = 0; iterator.hasNext(); ordinal++)
{
System.out.printf("%d:%f",ordinal,iterator.next());
System.out.println();
}
this is actually the solution that Google suggests in the Guava discussion on why they did not provide a CountingIterator.
Though there are soo many other ways mentioned to achieve the same, I will share my way for some unsatisfied users. I am using the Java 8 IntStream feature.
1. Arrays
Object[] obj = {1,2,3,4,5,6,7};
IntStream.range(0, obj.length).forEach(index-> {
System.out.println("index: " + index);
System.out.println("value: " + obj[index]);
});
2. List
List<String> strings = new ArrayList<String>();
Collections.addAll(strings,"A","B","C","D");
IntStream.range(0, strings.size()).forEach(index-> {
System.out.println("index: " + index);
System.out.println("value: " + strings.get(index));
});
One of the changes Sun is considering for Java7 is to provide access to the inner Iterator in foreach loops. the syntax will be something like this (if this is accepted):
for (String str : list : it) {
if (str.length() > 100) {
it.remove();
}
}
This is syntactic sugar, but apparently a lot of requests were made for this feature. But until it is approved, you'll have to count the iterations yourself, or use a regular for loop with an Iterator.
For situations where I only need the index occasionally, like in a catch clause, I will sometimes use indexOf.
for(String s : stringArray) {
try {
doSomethingWith(s);
} catch (Exception e) {
LOGGER.warn("Had some kind of problem with string " +
stringArray.indexOf(s) + ": " + s, e);
}
}
If you need a counter in an for-each loop you have to count yourself. There is no built in counter as far as I know.
There is a "variant" to pax' answer... ;-)
int i = -1;
for(String s : stringArray) {
doSomethingWith(s, ++i);
}
i usually use this workaround by using array
List<String> list = new ArrayList<String>();
final int[] counter = new int[1];
list.foreach( item -> {
list.get(counter[0]) // code here that can use counter[0] for counter value
counter[0]++; // increment the counter
}
The best and optimized solution is to do the following thing:
int i=0;
for(Type t: types) {
......
i++;
}
Where Type can be any data type and types is the variable on which you are applying for a loop.
I'm a little surprised no-one suggested the following (I admit it's a lazy approach...);
If stringArray is a List of some sort, you could use something like stringArray.indexOf(S) to return a value for the current count.
Note: this assumes that the elements of the List are unique, or that it doesn't matter if they are non-unique (because in that case it will return the index of the first copy found).
There are situations in which that would be sufficient...
Here is an example of how I did this. This gets the index at the for each loop. Hope this helps.
public class CheckForEachLoop {
public static void main(String[] args) {
String[] months = new String[] { "JANUARY", "FEBRUARY", "MARCH", "APRIL", "MAY", "JUNE", "JULY", "AUGUST",
"SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER" };
for (String s : months) {
if (s == months[2]) { // location where you can change
doSomethingWith(s); // however many times s and months
// doSomethingWith(s) will be completed and
// added together instead of counter
}
}
System.out.println(s);
}
}