I've got this constructor in the class Music:
protected String Title;
protected String Autor;
protected String Type;
protected int Code;
public Music (String title, String autor, String type, int code){
this.setTitle(title);
this.setAutor(autor);
this.setType(type);
this.setCode(code);
}
#Override
public String toString(){
return this.Title + " " + this.Autor + " " + this.Type + " " + this.Code;
}
Then, in other class called ManageMusic I create some methods to then use them on the main class. I also define a String array refered to the Music class which I will use in the main class:
private final Music[] musicList;
private int counter;
public ManageMusic(int maxSize) {
musicList= new Music[maxSize];
counter= 0;
public void add(Music m){
musicList[counter] = m;
counter++;
}
Here, I have to create a delete method which would delete a especific object from the musicList and return this list without that object.
This is the way I add music elements to the musicList on the main class:
static ManageMusic musiclist = new ManageMusic(20);
musicList.add(new Music(title, autor, format, code));
My approach for the delete method in the ManageMusic class is to copy this list into a new String[] and then copy it back to the list. But as I'm using an objet from Music instead of from String, I cannot make the copy back because it does not cast the String to the musicList:
public void delete(int code){
String[] newString = new String[musicList.length];
int index_1 = 0;
for (int i=0; i<musicList.length; i++){
if(i != code){
newString[index_1] = musicList[i].toString();
index_1++;
}
}
int index_2 = 0;
for (int i=0; i<newString.length; i++){ //THIS IS WHERE IT SAYS: Cannot convert
// from String to Music
musicList[index_2] = newString[i];
index_2++;
}
}
I have to do something not far from this, because then I've got a method that list elements from the musicList, so I cannot set a return statement for the method.
Why you can do it without an ArrayList
As some people suggested in the comments, you should probably use ArrayLists or similar stuff from the java.util.collection package.
However I assume you want to learn how such things work, so I will not provide you with code (First because I'm too lazy, second to encourage you to learn it yourself) but with some explanation.
edit: First: Your problem is that you copy strings, not references. Instead of trying to use the toString method, try to handle it with the "objects" (i.e. their references) themselves.
Error checking
As you might have noticed your add will cause an IndexOutOfBoundsException if you try to add another entry after your list reached your max_size. You should check for that. You should also check lots of things in the following explanations, I'll provide a few suggestions.
Simple deletion with your exact example
Just use Music[] instead of String[] and copy the reference of the temp Music[] to your musicList.
Better way to handle it: dynamic array structure
So what I suggest is to make use of a dynamic array structure. You will have to copy arrays around a lot, which can be a bit difficult. But basically it's what an ArrayList does.
Constructor
But how to implement that dynamic structure? Let's first start with the initialization, i.e. your constructor. You will not need a parameter for a maximum size (unless you want to restrict your music collection for some reason). Just create an array with size 0. (Of course you can also implement copy constructors and other things, but for the start keep it simple.)
add()
To add more music, you simply create a new array with the size of your current collection + 1, copy all references (this is probably the answer you were looking for. You take the strings, but just take the objects themselves) from the current array to the new array, add the new Music and change the reference of your member variable to your newly created, bigger array (i.e. assigning musicList = tempArray; or something similar). (Error checking: is the object null?)
delete()
For deletion you can do just the same. Create a new temporary array (this time with a reduced size), copy all values over but leave out the one you want to delete. To determine which shall be deleted you can either check for indices or even check the objects for equality. (Error checking: size of temp array should never be smaller than 1, so check if your array is empty - consider to use a method like isEmpty() for that.)
Why should I do this?
Once you got those concepts you will be able to manage your array in whatever way you like. Search through it, delete all elements, copy your music collection to your friend's, etc. etc.
And beyond that?
And after you learned this, go ahead and try the ArrayList - you will figure out it works very much like what you have just written. And now you can be proud that you not only can use ArrayLists, but also know how and why they behave like they do.
Its better to use ArrayList than writing own logic to delete object from existing array. Here is how you can use ArrayList :
{
ArrayList<Music> list = new ArrayList<Music>();
Music m1 = new Music(title, autor, format, code);
list.add(m1);
// similarly you can check whether object is present in ArrayList or not using
if(list.contains(m1)){ // This check whether object is present in ArrayList or not
//Do whatever you want
}
}
ArrayList example:
List<Music> musicList = new ArrayList<Music>();
adding to end of list list:
musicList.add(new Music(...));
adding to specified position in list (later ones all move up one place)
musicList.add(index, new Music(..));
remove from list:
musicList.remove(index);
or
musicList.remove(someMusic);
Size of list:
int size = musicList.size();
Get first music:
Music first = musicList.get(0);
Get last music:
Music last = musicList.get(musicList.size()-1);
Loop:
for (Music : musicList) {
//do stuff
}
do like this
public void delete(int code){
List<Music> list = new ArrayList<Music>(); //creating new empty list
for (Music m:musicList){
if(m.code != code){ // removing condition
list.add(m); // adding music to new list
}
}
musicList = list.toArray(new Music[list.size()]); // assigning back list to musicList
}
Related
I'm trying to remove null values from an array, and returning them to do some other stuff with the new values. However, I'm confused about how to get the updated array.
This is the null removal code.
String[] removeNull(String[] nullArray) {
int nullCounter = 0;
//checking if any is null
for(int i = 0; i < nullArray.length; i++) {
if(nullArray[i]==null) {
nullCounter++;
}
}
String[] noNulls = new String[nullArray.length-nullCounter];
if(nullCounter>0) {
//make a non null array
for(int i = 0, j = 0; i <noNulls.length; i++) {
if(nullArray[i]!=null) {
noNulls[j] = nullArray[i];
j++;
}
}
}
return noNulls;
}
I'm pretty sure that is already correct (Please correct me if I'm wrong). Then, I called it inside a constructor.
public theBoundary(String[] bounds){
removeNull(bounds);
}
After I called removeNull(bounds), will the value of the new array be stored in the array bounds? Or will it be stored in the array noNull? I can't seem to find where the new values are stored.
Thank you, and please tell me if there are mistakes. I've been going around this for half an hour now.
Note: If possible, please don't give me answers that include importing something else. Vanilla Java would be preferred.
removeNull() returns the array noNulls, created inside the method. Currently, in theBoundary(), you simply call removeNull(bounds), but do not assign it to a variable. The newly created null-free array is created, not assigned, and immediately garbage collected.
If you wish to do something with your non-null-containing array (which I assume you do), do this:
public theBoundary(String[] bounds) {
String[] withoutNulls = removeNull(bounds);
doSomething(withoutNulls); // whatever you need here
}
Note, unless you really have to use an array, consider using a List or even a Stream.
List example:
List<String> list = ... // from somewhere else
list.removeIf(s -> s == null);
doSomething(list);
Stream example:
Stream<String> stream = ... //from somewhere else
stream.filter(s -> s != null);
doSomething(stream);
EDIT
Even if you do really need arrays, the following will also work:
String[] noNulls = (String[]) Arrays.stream(inputArray).filter(Objects::nonNull).toArray();
I don't think there is any need to iterate the array twice!
You can instead use a stream on array and filter the indexes without that are NOT NULL.
Also, you can do this without needing to create the removeNull method, and do this directly in your theBoundary method.
Here is how your code will look like:
String[] arrayWithoutNull = Arrays.stream(bounds).filter(Objects::nonNull).toArray(String[]::new)
I hope this solves your problem.
Do you mean this?
public theBoundary(String[] bounds){
String[] cleanedBounds = removeNull(bounds);
}
You are not doing it inplace so you need to assign it back to a new array
I'm trying to figure out how to get a undo function for a small maze game. First I worked out a way to do this by checking what the last direction was and just going back in the opposite direction. But this code was getting way too long since I also had to track back possible item pickups or hidden walls etc.
Background info on the code: I use a String[][] to store the maze as this was the easiest. I use an Arraylist<String[][]> to store all the strings.
After each step the player takes I save the String[][] array to the arraylist. When the player say undo I look at the second last String[][] in the arraylist and want to set the String[][] back to this. But the currentPos never seems to get updated. I'm not sure where the problem lies.
if (direction.equals("north")) {
if (currentPos[i - 1][j].equals("---")) {
continue;
} else {
currentPos[i][j] = " ";
currentPos[i - 2][j] = "P";
break;
}
}
if (direction.equals("undo")) {
currentPos = history.get(history.size()-2);
history.remove(history.size()-1);
break;
}
Without understanding the way you are setting history, I've made the assumption from your question that you are simply adding the current map to the history list. If you aren't careful, you will be simply adding the same Object, populating the history with multiply Object references to the current map state. This would have the effect you are observing with the state not changing, because you the history only contains a reference to the most recent map (Not storing any actual history).
To obtain the value from an Object, you typically need to clone the object (invoking the clone() method). However, cloning a 2-dimensional array is somewhat problematic. Invoking the clone() method on a 2-dimensional array "shallow" clones the object, essentially only cloning the first dimension while leaving the second as a reference to the same object (The reason for this is that the first 1-dimension of the array holds a reference to the second 1-dimension). Changing the value on a shallow copied object will change the value of the original and vice-versa, not what you want if you want to keep the Objects distinct.
To create two distinct objects, you will need to perform a "deep" clone, which can be easily implemented in a helper method. The below code illustrates the importance of ensuring you fully clone the object before storing it in the history list.
public static void main (String args[]) throws Exception {
ArrayList<String[][]> list = new ArrayList<>();
String[][] shallowClonedMap = new String[1][1];
String[][] deepClonedMap = new String[1][1];
shallowClonedMap[0][0] = "Old";
deepClonedMap[0][0] = "Old";
list.add(shallowClonedMap.clone());
list.add(deepClone(deepClonedMap));
shallowClonedMap[0][0] = "New";
deepClonedMap[0][0] = "New";
list.add(shallowClonedMap.clone());
list.add(deepClone(deepClonedMap));
for (String[][] item : list) {
System.out.print(item[0][0]);
}
}
public static String[][] deepClone(String[][] arry) {
if (arry == null) {
return null;
}
String[][] clone = new String[arry.length][];
for (int i = 0; i < arry.length; i++) {
clone[i] = arry[i].clone();
}
return clone;
}
The output for executing this code is : NewOldNewNew whereas the "intended" output is "OldOldNewNew". From this you can see the shallowClonedMap was updated to "New" even after being cloned and added to the list.
So far this is my problematic code, assume everything else is made and done:
public GameRecord[] updateHighScoreRecords(GameRecord[] highScoreRecords, String name, int level, int score) {
// write your code after this line
int i = 0;
for (GameRecord gr : highScoreRecords){
if (gr.getScore() >= gr.getScore()){
highScoreRecords.add(i+(gr.getLevel()-level),(Object) new GameRecord(name, level, score)); /*
*adds the new GameRecord in at the (i+gr's level - level)th iteration.
*note it does this because of the assumtion that highScoreRecords is ordered becuase of only using this function
*/
break; //no more need to continue the loop
}
i += 1;
}
return highScoreRecords;
}
as you may have noticed, my code is part of a course, so that is why I'm assuming all other implementations are perfect.
You are passing in a GameRecord[] highScoreRecords array,
but calling a List method add - this does not exist on an Array. You should be getting a compile error.
If you are sure that the array has capacity for insertion then you could do
highScoreRecords[i+(gr.getLevel()-level)] = new GameRecord(name, level, score);
but I guess you would be better off using a List like ArrayList, and keeping your existing code. For this you will to pass a List to the method not an Array.
Java arrays are not dynamic data structures,
highScoreRecords.add(i+(gr.getLevel()-level),
(Object) new GameRecord(name, level, score));
I think you wanted
// Using a List.
GameRecord[] updateHighScoreRecords(List<GameRecord> highScoreRecords,
String name, int level, int score) {
Also, don't cast to Object. That's raw-typeing.
I have this array
public static ArrayList<String> inventory = new ArrayList<String>();
and have the players items stored inside of it.
I want to add a feature in the shop class that will sell everything but the pickaxe, how can I create a loop to check if theres something in the array other than "pickaxe" and if there is to remove it?
To remove I have a void
public void removeAllInventory() {
inventory.clear();
}
or
public void removeInventory(String item) {
inventory.remove(item);
}
Could I just edit the removeAllInventory to ignore the pickaxe and make a new void called removeAllShop or something? If so what would go in that void?
This is where I need to put it in:
else if (input.input.equalsIgnoreCase("all")) {
}
Loop over the list, check if if each element is equal to pickaxe, and remove it if it not.
Iterator<String> i = inventory.iterator();
while (i.hasNext()) {
if (i.next().equalsIgnoreCase("pickaxe"))
i.remove()
}
You shouldn't edit removeAllInventory() to remove everything but the pickaxe. Its name would no longer make sense, and it seems a reasonable routine to keep around.
But you could add a new method, removeAllInventoryExcept(String item), that removes everything but the given the item.
Hope this helps.
EDIT: In attempt to beef up this answer, I'd also like to suggest the "out of the box" solution of:
public void removeAllInventoryExcept(String item) {
ArrayList<String> newInv = new ArrayList<String>();
newInv.add(item);
inventory = newInv;
}
This avoids the costly iteration and string comparisons.
I'm assuming all of the items in the inventory are an ancestor of an item class, and not just a String.
You could loop through the ArrayList, getting the names of each element and comparing it with the pickaxe name.
or
You could loop through the ArrayList and checking if each item is an instanceof
pickaxe, and if it is not, remove it from the ArrayList
edit* It seems you specified the ArrayList is of type String, so ignore the second option
for (String item : inventory) {
if (!"pixckaxe".equalsIgnoreCase(item)) {
inventory.remove(item);
}
}
I started down this path of implementing a simple search in an array for a hw assignment without knowing we could use ArrayList. I realized it had some bugs in it and figured I'd still try to know what my bug is before using ArrayList. I basically have a class where I can add, remove, or search from an array.
public class AcmeLoanManager
{
public void addLoan(Loan h)
{
int loanId = h.getLoanId();
loanArray[loanId - 1] = h;
}
public Loan[] getAllLoans()
{
return loanArray;
}
public Loan[] findLoans(Person p)
{
//Loan[] searchedLoanArray = new Loan[10]; // create new array to hold searched values
searchedLoanArray = this.getAllLoans(); // fill new array with all values
// Looks through only valid array values, and if Person p does not match using Person.equals()
// sets that value to null.
for (int i = 0; i < searchedLoanArray.length; i++) {
if (searchedLoanArray[i] != null) {
if (!(searchedLoanArray[i].getClient().equals(p))) {
searchedLoanArray[i] = null;
}
}
}
return searchedLoanArray;
}
public void removeLoan(int loanId)
{
loanArray[loanId - 1] = null;
}
private Loan[] loanArray = new Loan[10];
private Loan[] searchedLoanArray = new Loan[10]; // separate array to hold values returned from search
}
When testing this, I thought it worked, but I think I am overwriting my member variable after I do a search. I initially thought that I could create a new Loan[] in the method and return that, but that didn't seem to work. Then I thought I could have two arrays. One that would not change, and the other just for the searched values. But I think I am not understanding something, like shallow vs deep copying???....
The return value from getAllLoans is overwriting the searchedLoanArray reference, which means that both loanArray and searchedLoanArray are pointing at the same underlying array. Try making searchedLoanArray a local variable, and then use Arrays.copyOf. If you're trying not to use standard functions for your homework, manually create a new Loan array of the same size as loanArray, and then loop and copy the values over.
your searchloanarray and loanarray point to the same array. doing this
private Loan[] searchedLoanArray = new Loan[10]
does nothing as you never use that new Loan[10]
this is the key to your problem
searchedLoanArray = this.getAllLoans()
that just points searchedLoanArray at loanArray
You could rewrite it like this:
public Loan[] findLoans(Person p)
{
Loan[] allLoans = this.getAllLoans();
System.arraycopy(allLoans, searchedLoanArray, 0, 0, allLoans.length); // fill new array with all values
// remainder of method the same
}
But as it stands, the code still has some problems:
The maximum number of loans is fixed to the size of the array. You will avoid this problem when you switch to List<Loan>.
Using the id as an index means that your ids must be carefully generated. If IDs come from a database, you may find that the list tries to allocate a huge amount of memory to size itself to match the Id. You would be better using a Map, then the size of the map is based on the number of loans, rather than their IDs.
As the number of people and loans increase, the search time will also increase. You can reduce search time to a constant (irrespective of how many People) by using a Map>, which allows quick lookup of the loans associated just with that person.
Here's a version with these changes:
class AcmeLoanManager
{
public void addLoan(Loan l)
{
Person client = l.getClient();
List<Loan> loans = clientLoans.get(l);
if (loans==null)
{
loans = new ArrayList();
clientLoans.put(client, loans);
}
loans.add(l);
allLoans.put(l.getLoanId(), l);
}
public void removeLoan(int loanId)
{
Loan l = loans.remove(loanId);
clientLoans.remove(loan);
}
public Collection<Loan> getAllLoans()
{
return loans.values();
}
public List<Loan> findLoans(Person p)
{
List<Loan> loans = clientLoans.get(p);
if (loans==null)
loans = Collections.emptyList();
return loans;
}
private Map<Integer,Loan> allLoans = new HashMap<Integer,Loan>();
private Map<Person, List<Loan>> clientLoans = new HashMap<Person,List<Loan>>();
}
I hope this helps!
What I would do is loop through the values and reassign each value to the new variable. Alternatively, you could use "deep copy" technique as described here in Javaworld: http://www.javaworld.com/javaworld/javatips/jw-javatip76.html