I have done as much research as I can into similar questions about 2D arrays and NullPointerException (NPE), but have not found an answer that resembles my situation.
My program is supposed to be very basic: take an input "image"-file of integer values, and "soften" those values by taking the average of values around each.
I'm having trouble with the initial process of copying the file into a 2 dimensional array with while loops, though the loops do not seem to be the problem, as I have tried the do-while loop already.
I initially tried using Arrays.copyOf to copy the arrays, but that initially gave me an NPE, so I tried writing my own static methods to do the job, because I read somewhere that Arrays.copyOf only works for one dimensional arrays.
public class ex7_imageSmoother {
public static void main ( String[] args ) throws IOException {
// build utility object(s)
Scanner ScUser = new Scanner( System.in );
// ph for ascii art
System.out.println( "\n\nAre your ready to Soften some \"hard\" files?" );
////..repeat program by prompt
String stRepeat1;
do {
// get hard file name to be softened
System.out.print( "\n\nWhat file would you like to soften? " );
String StHardName = ScUser.nextLine().trim();
File FiHardIn = new File ( StHardName );
Scanner ScHardIn = new Scanner( FiHardIn );
//-- build 2d "Hard" array
// array will be of at least one cell
int[][] AyHardImg = { { 0 } } ;
int iRowCount = 0;
//// for every line in the file; i.e. check that there is a next line
while (ScHardIn.hasNextLine() ) {
// create a string that can be read through by scanner for every line of the file
String StInLine = ScHardIn.nextLine();
// build scanner to read through each row
Scanner ScInLine = new Scanner( StInLine );
// use static method to copy array; make larger on further iterations
AyHardImg = smCopyArrayOuter( AyHardImg, iRowCount );
int iColCount = 0;
//// for every integer in the row
while ( ScInLine.hasNextInt() ) {
// create temporary array in an attempt to circumvent NPE
int[] temp = new int[ AyHardImg[ iRowCount ].length ]; // ...--... this line creates the NPE!!
// copy into temp array all values from inner array of 2d array
for (int i = 0; i < AyHardImg[ iRowCount ].length; i++) {
temp[ i ] = AyHardImg[ iRowCount ][ i ];
}
// copy array and make larger on further iteration
temp = smCopyArrayInner( temp, iColCount );
// use temp array to copy into 2d inner array; included is commented out previous attempt without temp array
AyHardImg[ iRowCount ] = temp; //= smCopyArray( AyHardImg[ iRowCount ], iColCount );
AyHardImg[ iRowCount ][ iColCount ] = ScInLine.nextInt();
iColCount++;
}
iRowCount++;
ScInLine.close();
}
// test algorithm works as intended by printing hard array to screen
for ( int i = 0; i < AyHardImg.length; i++ ) {
for ( int j = 0; j < AyHardImg[i].length; j++ ) {
System.out.print ( AyHardImg[ i ][ j ] + " " );
}
System.out.println();
}
ScHardIn.close();
// get user response to repeat program
System.out.print( "Would you like to soften another file (y/n)? " );
stRepeat1 = ScUser.nextLine().trim().toLowerCase();
} while ( stRepeat1.equals( "y" ) );
}
/*-----
* copies inner array, hopefully to solve null
* pointer exception
*------------------*/
public static int[] smCopyArrayInner( int[] AyX, int growth ) {
int[] AyY = new int[ AyX.length +growth ];
for ( int i = 0; i < AyX.length; i++ ) {
AyY[ i ] = AyX[ i ];
}
return AyY;
}
/*-----
* copies outer array, hopefully to solve null
* pointer exception
*------------------*/
public static int[][] smCopyArrayOuter( int[][] AyX, int growth ) {
int[][] AyY = new int[ AyX.length +growth ][];
for ( int i = 0; i < AyX.length; i++ ) {
AyY[ i ] = AyX[ i ];
}
return AyY;
}
}
NPE is as follows
Exception in thread "main" java.lang.NullPointerException
at ex7_imageSmoother.main(ex7_imageSmoother.java:101)
Thank you to anyone who read this question in an attempt to help but I figured it out by "debugging" the logic manually.
Essentially I wrote a smaller test program that dealt with just a single line of input into a one dimensional array and noticed that my smCopyArrayOuter and smCopyArrayInner were both growing larger than they needed to be.
in essence the line
int[][] AyY = new int[ AyX.length +growth ][];
became
int[][] AyY = new int[ 1 +growth ][];
which solved the problem.
I was also able to do away with the temp array and deal with the AyHardImg directly which you can see if you check out the repo on gitHub which is github.com/q1pt2rx/ex7_imageSmoother.
as of posting this answer the program is incomplete but this NPE issue is resolved.
Related
I need to execute by command line a code that will provide a multidimensional array with elements with not necessarily equal lengths.
The execution string is bellow:
start /wait java -jar testMSMWithIndex.jar Foursquare_weather_day_root-type_type 0,1,2-4
I'm considering to pass the parameter 0,1,2-4 and then convert it in a multidimensional array with elements of different lengths in this case, i.e. {{0}, {1}, {2, 4}}.
Note that {{0, null}, {1, null}, {2, 4}} does not work to my problem.
Do you guys know how to develop a method or even get directly as an array from args?
I really appreciate any help you can provide.
It's doubtful that anything already exists to do this for you, so you'll have to parse the string for yourself. Something like this would do it:
public static int[][] parseRaggedArrayFromString(String s)
throws NumberFormatException {
String[] ss = s.split(",");
int[][] result = new int[ss.length][];
for (int i = 0; i < ss.length; ++i) {
if (!ss[i].contains("-")) {
result[i] = new int[1];
result[i][0] = Integer.parseInt(ss[i]);
} else {
String[] range = ss[i].split("-", 2);
int lo = Integer.parseInt(range[0]);
int hi = Integer.parseInt(range[1]);
int size = hi - lo + 1;
result[i] = new int[size > 0 ? size : 1];
int j = 0;
do {
result[i][j] = lo;
++lo;
++j;
} while (lo <= hi);
}
}
return result;
}
It's basically a split on , and -. From there is just handling the data. Comments in the code.
/**
* #author sedj601
*/
public class Main {
public static void main(String[] args) {
String input = "0,1,2-3";
String[] firstArray = input.split(",");//Split on ,.
String[][] outputArray = new String[firstArray.length][];//The array that will be holding the output
//Used to process the firstArray
for (int i = 0; i < firstArray.length; i++) {
if (firstArray[i].length() > 1) {//If the lenght is greater than one. split on -.
String[] secondArray = firstArray[i].split("-");
//Subtract the two numbers and add one to get the lenght of the array that will hold these values
int arrayLength = Integer.parseInt(secondArray[1]) - Integer.parseInt(secondArray[0]) + 1;
String[] tempArray = new String[arrayLength];
int increment = 0;//Keeps up with the tempArray index.
//loop from the first number to the last number inclusively.
for (int t = Integer.parseInt(secondArray[0]); t <= Integer.parseInt(secondArray[1]); t++) {
tempArray[increment++] = Integer.toString(t);//Add the data to the array.
}
outputArray[i] = tempArray;//Add the array to the output array.
} else {//If the lenght is 1, creat an array and add the current data.
String[] tempArray = new String[1];
tempArray[0] = firstArray[i];
outputArray[i] = tempArray;
}
}
//Print the output.
for (String[] x : outputArray) {
for (String y : x) {
System.out.print(y + " ");
}
System.out.println();
}
}
}
Output:
--- exec-maven-plugin:1.5.0:exec (default-cli) # JavaTestingGround ---
0
1
2 3
------------------------------------------------------------------------
BUILD SUCCESS
------------------------------------------------------------------------
Total time: 1.194 s
Finished at: 2021-01-08T00:08:15-06:00
------------------------------------------------------------------------
I really think that's possible when you create an array of type Object .(not a good idea) Since multi-D arrays can only hold arrays of same length (int[][]). Then you create and retrieve values from array by casting...
I am trying here to be creative and adopt to your requirements..
public class x {
public static void main(String[] args) {
Object[] arguments = new Object[args.length];
// Then make a loop to capture arguments in array..
// or add manually
arguments[0] = new String[]{args[0]};
arguments[1] = new String[]{args[1],args[2]};
//Then retrieve info from object later by casting
System.out.println(java.util.Arrays.toString((String[]) arguments[1]));
}
}
...
Although, please consider using a collection...
While I waited for the answer, I found a way to solve the problem.
The relevant information here is that we do not need to set the second array dimension in its instantiation.
The code is below:
// INPUT string = "2-3,1,4-5"
private static String[][] featuresConversion(String string) {
String[] firstLevel = string.split(","); // 1st lvl separator
String[][] features = new String[firstLevel.length][]; // Sets 1st lvl length only
int i = 0;
for (String element : firstLevel) {
features[i++] = element.split("-");
}
return features;
}
I want to thank you all. All suggested solutions also work fine!
I'm trying to add elements into Arraylist using non-continuous indexes, for example:
I want to add an element at index 3 first before adding any element at indexes (0,1,2). I will also fill up the indexes at 0,1,2 later.
Here's my code:
public static void main(String[] args) throws FileNotFoundException {
List<Integer> numbers= new ArrayList<Integer>();
Scanner inp = new Scanner(System.in);
int[] x = {1,5,2,4,3,0};
System.out.println("Enter elements:"+"\n");
for(int i=0;i<x.length;i++) {
int index = x[i];
numbers.add(index, inp.nextInt());
}
I seem to get this error:
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 1, Size: 0
at java.util.ArrayList.rangeCheckForAdd(Unknown Source)
at java.util.ArrayList.add(Unknown Source)
at Test1.main(Test1.java:28)
I understand the error, but I don't seem to find a way out of this problem. Any help will be much appreciated. If at all there's another fancy data structure which allows me to do non-continuous indexing please let me know, excuse me if my questions doesn't make any sense.
Have you thought about using a Map?
Map<Integer,Integer> myNumbers = new HashMap<>();
myNumbers.put( 4, 100 );
myNumbers.put( 2, 199 );
myNumbers.put( 1, 150 );
System.out.println( myNumbers.get( 2 ) );
There are multiple implementations of Map, e.g. HashMap or TreeMap. Which one to go for, depends on your requirements, for example, if you want to store the elements in a certain order or if you don't care about the order.
In your use case the Map could be used like this:
int[] x = { 1,5,2,4,3,0 };
// Try this instead of the above array, as well:
//int[] x = { 1337, 42, 777 };
// The Map can take an (almost) unlimited number of entries and gaps between
// indices / keys (e.g. 0, 1, 7, 23) are no problem. Only elements that you
// *put* into the Map, are part of it.
Map<Integer,Integer> numbersMap = new TreeMap<>();
System.out.println( "Enter elements:\n" );
try( Scanner inp = new Scanner( System.in ) ) {
for( int i=0; i<x.length; i++ ) {
int index = x[i];
System.out.print( "Enter element " + index + ": " );
int userInput = inp.nextInt();
numbersMap.put( index, userInput );
}
}
System.out.println( "Your Map contains these entries:" );
for( Map.Entry<Integer,Integer> entry : numbersMap.entrySet() ) {
System.out.println( "Element[" + entry.getKey() + "] = " + entry.getValue() );
}
As your indices in the x array are continuos, without gaps, zero-based and known in advance, you could use something like this in this special case, as well:
int[] x = { 1,5,2,4,3,0 };
Integer[] output = new Integer[ x.length ];
System.out.println( "Enter elements:\n" );
try( Scanner inp = new Scanner( System.in ) ) {
for( int i=0; i<x.length; i++ ) {
int index = x[i];
System.out.print( "Enter element " + index + ": " );
int userInput = inp.nextInt();
output[ index ] = userInput;
}
}
List<Integer> numbers = Arrays.asList( output );
System.out.println( numbers );
But I'd prefer the Map approach as it's more flexible and less error-prone.
You have an arraylist, but you seem to forget that you haven't set the initial size of the arraylist. The add method that you use places the value at the given index and then shifts everything over to the right, but in this case, there's no index 1. In order to do so, you should either do
List<Integer> numbers= Arrays.asList(new Integer[10]),
which will make an arrayList with 10 "indexes" which hold a value of null, or
ArrayList<Integer> arr=new ArrayList<Integer>(Collections.nCopies(10, 0));,
which will create anarrayList with 10 "indexes" each of which hold a value of 0. For more information, look at this question which you indirectly ask.
Initial size for the ArrayList
So I have a program that reads a file, adds the information into an array and then afterwards sorts the array using an exchange sort into alphabetical order. The problem is I misunderstood the assignment and need the strings to be sorted as they are entered into the array instead of using a separate sorting method after they are already entered. Heres what I have:
public class NumberCollection2
{
String nextName;
int nextNumber;
private Person[] people = new Person[50];
private int size =0;
public void load()
{
try
{
Scanner in = new Scanner(new File ("numbers.txt"));
while (in.hasNextLine())
{
nextName = in.next();
nextNumber = in.nextInt();
people[size]=new Person(nextName, nextNumber);
size++;
in.nextLine();
}
//use exchange sort to sort in ascending alphabetical order
int i, j;
for ( i = 0; i < size - 1; i++ )
{
for ( j = i + 1; j < size; j++ )
{
if ( people[ i ].getName().compareTo(people[ j ].getName()) > 0 )
{
Person temp = people [ i ];
people [ i ] = people [ j ];
people [ j ] = temp;
}
}
}
}
This works perfectly yet my professor needs it to be sorted as it is entered into the array "people" and I am not sure how to approach that. Any advice/help would be awesome, thanks!!!
this is the email I got from my prof : "To receive full credit, you must insert each item into its sorted position in the array as it is read in. It is not ok to read it all in and call a sort routine."
"my professor needs it to be sorted as it is entered"
This is a linked list, not an array.
So, you can only view the current item in stack/queue (FILO/FIFO).
You're professor probably means to print the list the way it was entered.
You can do this by creating the list as a queue.
Java has a call you can use, called Queue. There is also Stack and LinkedList.
import java.io.* ;
import java.util.ArrayList ;
public class WordSearchPuzzle;
{
private char[][] puzzle ;
private ArrayList<String> puzzleWords ;
private int letterCount = 0 ;
private int gridDimensions;
public WordSearchPuzzle(ArrayList<String> userSpecifiedWords)
{
this.puzzleWords = userSpecifiedWords ;
}
private void createPuzzleGrid()
{
int i;
for(i = 0; i < puzzleWords.size(i).length ; i++){
letterCount = puzzleWords + letterCount ;
}
}
gridDimensions = letterCount * 1.5;
puzzle[gridDimensions][gridDimensions];
}
public WordSearchPuzzle(String wordFile, int wordCount,
int shortest, int longest)
{
// puzzle generation using words from a file
// The user supplies the filename. In the file
// the words should appear one per line.
// The wordCount specifies the number of words
// to (randomly) select from the file for use in
// the puzzle.
// shortest and longest specify the shortest
// word length to be used and longest specifies
// the longest word length to be used.
// SO, using the words in the file randomly select
// wordCount words with lengths between shortest
// and longest.
}
private ArrayList<String> loadWordsFromFile(String filename, int shortest, int longest)
{
// BasicEnglish.txt - the 850 words of Basic English
// BNCwords.txt - "the 6,318 words with more than 800 occurrences in
//the whole 100M-word BNC"
try {
FileReader aFileReader = new FileReader(filename);
BufferedReader aBufferReader = new BufferedReader(aFileReader);
String lineFromFile;
int len ;
ArrayList<String> words = new ArrayList<String>();
lineFromFile = aBufferReader.readLine() ;
while (lineFromFile != null) {
len = lineFromFile.length() ;
if(len >= shortest && len <= longest) {
words.add(lineFromFile.toUpperCase());
}
lineFromFile = aBufferReader.readLine() ;
}
aBufferReader.close();
aFileReader.close();
return words ;
}
catch(IOException x)
{
return null ;
}
}
// The dimensions of the puzzle grid should be set
// by summing the lengths of the words being used in the
// puzzle and multiplying the sum by 1.5 or 1.75
// or some other (appropriate) scaling factor to
// ensure that the grid will have enough additional
// characters to obscure the puzzle words. Once
// you have calculated how many characters you are
// going to have in the grid you can calculate the
// grid dimensions by getting the square root (rounded up)
// of the character total.
}
Hi, small Java project I have to do here for college. Here is what I have so far. I don't understand why it's not compiling. I have code written for generating the grid; the grid dimensions are set by the input words (sum of letters of all input words * 1.5). I am not sure of the part which sums all the elements of the Array List together.
What's going on? Thanks in advance :)
I can see multiple problems:
In the class declaration line there should be no semi-colon.
public class WordSearchPuzzle
As Nettogrof has shown, you have too many }'s in the createPuzzleGrid method.
The loop within createPuzzleGrid uses methods that do not exist.
There is no size method that takes a parameter for array lists. Also it does not find the length of the string at that point
Your loop in createPuzzleGrid should be:
for (int i = 0; i < puzzleWords.size; i++) {
String item = puzzleWords.get(i);
int itemLength = item.length();
letterCount = letterCount + itemLength;
}
As an extra note, the last line of that method accesses the puzzle array but does not do anything, so this line could be removed. In fact no methods use the puzzle variable so it could be completely removed.
remove the semi-colon here.... public class WordSearchPuzzle;
this isn't a statement. puzzle[gridDimensions][gridDimensions];
puzzleWords.size(i).length in the for loop is giving issues. If you're wanting the number of elements in the list, puzzleWords.size() will work. And then letterCount = puzzleWords + letterCount ;
, you have incompatible types, ArrayList + int, are you meaning to use puzzleWords.size() instead of puzzleWords?
In your createPuzzleGrid, there's two } in your For-loop
The correctec version:
private void createPuzzleGrid()
{
int i;
for(i = 0; i < puzzleWords.size(i).length ; i++){
letterCount = puzzleWords + letterCount ;
}
gridDimensions = letterCount * 1.5;
puzzle[gridDimensions][gridDimensions];
}
I'm trying to output multiple lists of data, of varying length, to a CSV file. Each list should be a column in the output CSV file. Is there a straight-forward way of doing thing? If I were outputting each list as a row, I'd just loop over each list and output a return when I hit the end, but this approach does not work when working column-wise.
I thought of going over all the lists at once, item by item and incrementing a counter, but this would also fail because some lists are longer than others. To remedy this I would have to check at each iteration whether the counter is past the end of each list, which would be fairly expensive in terms of computations.
Thanks for any ideas!
I think this is pretty straight-forward:
public static void main(String... args) throws IOException {
ArrayList<ArrayList<String>> rows = getRandomData();
if (rows.size() == 0)
throw new RuntimeException("No rows");
// normalize data
int longest = 0;
for (List<String> row : rows)
if (row.size() > longest)
longest = row.size();
for (List<String> row : rows)
while (row.size() < longest)
row.add("");
if (longest == 0)
throw new RuntimeException("No colums");
// fix special characters
for (int i = 0; i < rows.size(); i++)
for (int j = 0; j < rows.get(i).size(); j++)
rows.get(i).set(j, fixSpecial(rows.get(i).get(j)));
// get the maximum size of one column
int[] maxColumn = new int[rows.get(0).size()];
for (int i = 0; i < rows.size(); i++)
for (int j = 0; j < rows.get(i).size(); j++)
if (maxColumn[j] < rows.get(i).get(j).length())
maxColumn[j] = rows.get(i).get(j).length();
// create the format string
String outFormat = "";
for (int max : maxColumn)
outFormat += "%-" + (max + 1) + "s, ";
outFormat = outFormat.substring(0, outFormat.length() - 2) + "\n";
// print the data
for (List<String> row : rows)
System.out.printf(outFormat, row.toArray());
}
private static String fixSpecial(String s) {
s = s.replaceAll("(\")", "$1$1");
if (s.contains("\n") || s.contains(",") || s.contains("\"") ||
s.trim().length() < s.length()) {
s = "\"" + s + "\"";
}
return s;
}
private static ArrayList<ArrayList<String>> getRandomData() {
ArrayList<ArrayList<String>> data = new ArrayList<ArrayList<String>>();
String[] rand = { "Do", "Re", "Song", "David", "Test", "4", "Hohjoh", "a \"h\" o", "tjo,ad" };
Random r = new Random(5);
for (int i = 0; i < 10; i++) {
ArrayList<String> row = new ArrayList<String>();
for (int j = 0; j < r.nextInt(10); j++)
row.add(rand[r.nextInt(rand.length)]);
data.add(row);
}
return data;
}
Output (pretty ugly since its random) (escapes):
Re , 4 , "tjo,ad" , "tjo,ad" ,
"tjo,ad" , "a ""h"" o" , , ,
Re , "a ""h"" o" , Hohjoh , "tjo,ad" , 4
4 , David , , ,
4 , Test , "tjo,ad" , Hohjoh , Re
Do , Hohjoh , Test , ,
Hohjoh , Song , , ,
4 , Song , , ,
4 , Do , Song , Do ,
Song , Test , Test , ,
It's worth having a look at http://commons.apache.org/sandbox/csv/
This also references some other CSV libraries.
Note that many answers have not considered strings which contain commas. That's the sort of reason why libraries are better than doing it yourself.
You can use String.format():
System.out.println(String.format("%4s,%4s,%4s", "a", "bb", "ccc"));
System.out.println(String.format("%4s,%4s,%4s", "aaa", "b", "c"));
The result will be a fixed column width of 4 characters - as long as the used values are shorter. Otherwise the layout will break.
a, bb, ccc
aaa, b, c
I'm not familiar with Java at all, but if you have a matrix oriented data type, you could fill the rows using easy looping, then transpose it, then write it out using easy looping. Your printing routine could handle null entries by outputting a null string, or fixed width spaces if you prefer.
Create an array of iterators (one for each list.) Then loop over the array, checking if the iterator hasNext(); if it does, output iterator.next(). Outputting commas and newlines is trivial. Stop when all iterators have returned hasNext()==false.
You can do something like this:
List<List<?>> listOfLists = new LinkedList<List<?>>();
List<Iterator<?>> listOfIterators = new LinkedList<Iterator<?>>();
for (List<?> aList : listOfLists) {
listOfIterators.add(aList.iterator());
}
boolean done = false;
while(!done)
{
done = true;
for (Iterator<?> iter : listOfIterators)
{
if (iter.hasNext())
{
Object obj = iter.next();
//PROCESS OBJ
done = false;
}
else
{
//PROCESS EMPTY ELEMENT
}
}
}
For CSV processing I have used this library several times: http://www.csvreader.com/java_csv.php Very simple and convenient.
Cheerz!
I would have to check at each iteration whether the counter is past the end of each list, which would be fairly expensive in terms of computations.
Get over it. This will, realistically, be small compared to the cost of actually doing the iteration, which in turn will be tiny compared to the cost of writing any given bit of text to the file. At least, assuming you have random access containers.
But you shouldn't be thinking in terms of a counter and indexing anyway; you should be thinking in terms of iterators (which sidestep the random-access question and simplify the code).
If you wanted to do this in one pair of loops and one method, you could do the following.
public static void writeCSV(PrintWriter pw, List<List<String>> columnsRows) {
for(int i=0;;i++) {
StringBuilder line = new StringBuilder();
boolean empty = true;
for (List<String> column : columnsRows) {
String text = i < column.size() ? column.get(i) : "";
found &= i >= column.size();
if (text.contains(",") || text.contains("\"") || text.contains("\n") || text.trim() != text)
text = '"' + text.replaceAll("\"", "\"\"") + '"';
line.append(text).append(',');
}
if (empty) break;
pw.println(line.substring(0, line.length()-1));
}
}
As an exercise, you could do this with one loop, but it wouldn't be as clear as to what its doing.
Using the sample data from #dacwe, this method takes 10 us (micro-seconds).