I am trying to add Books in my code. Let's say someone wants to add 30 books, the iteration goes from 0 to 30 which is fine. What if he wants to add 10 more books later, then it will simply do nothing useful, since I need them to start from 30 to 40. How can I fix this?
int currentBooks = 0;
do {
System.out.print("How many books would you like to add? ");
int nbBooks = sc.nextInt();
// Add nbBooks amount to inventory array
if (inventory.length-currentBooks >= nbBooks) {
for (int w = 0; w < inventory.length; w++) {
inventory[currentBooks] = new Book();
currentBooks = w;
}
valid = true;
break password;
}
else {
System.out.print("You can only add " + inventory.length + " books.\n");
add = true;
}
} while(add);
The disadvantage of a plain array (Book[] in your case) is that its length cannot be changed. You should use a List (despite the fact that you're not allowed to, for some strange reason).
With the List interface
Therefore, you are better off using the List interface (and an implementation of it, for instance, ArrayList), which uses an array internally, but it automatically extends its internal array if needed, so you don't have to worry about it:
// List is an interface, so we need a certain implementation of that interface
// to use. ArrayList is a good candidate:
List<Book> books = new ArrayList<>();
Now we have created an ArrayList with the initial length of 0. The length can be obtained using the size() method, as opposed to an array's length property.
int nbBooks = sc.nextInt();
for (int i = 0; i < nbBooks; i++) {
books.add(new Book());
}
Without the List interface
However, if you cannot or may not use the List interface, you have a few options, depending on what exacly you want.
One of the options is to create a class which holds your array with Books, and a length as a property, because you have to store the length somewhere:
class BookList {
private Book[] books = new Book[100]; // Or some maximum length
private int size;
public void add(Book book) {
this.books[this.size] = book;
this.size++;
// You could optionally 'extend' the array with System.arraycopy
// when the internal array exceeds 100, but I'll leave that to
// you
}
}
Note that this is virtually a kind of homebrew version of the ArrayList class.
In your case you have defined inventory somewhere. You'll need to introduce inventorySize or something, and each time you add a book, you also increment the inventorySize variable.
Book[] inventory;
int inventorySize;
and your method:
...
System.out.print("How many books would you like to add? ");
int nbBooks = sc.nextInt();
for (int i = 0; i < nbBooks; i++) {
this.inventory[this.inventorySize + i] = new Book();
}
this.inventorySize += nbBooks;
You can also check for the last non-null element (or the first null element) and consider that the length of the array, but that would be very bad code, because, for instance, you have to walk over the array to calculate its length, which might be pretty expensive in performance.
Related
ArrayList<Integer> companiesId = new ArrayList<>();
int numberOfCompanies = 10; // Just for example
...
float profit;
Scanner input = new Scanner(System.in);
for(int i = 0; i < numberOfCompanies; i++) {
int companyId = input.nextInt();
if (!companiesId.contains(companyId)) {
companiesId.add(companyId);
}
if (companiesId.get(i) == 1) {
profit = 1000;
} else if (companiesId.get(i) == 2) {
profit = 2000;
}
}
Now I want to print all the companyIds from the ArrayList with the profit entered for each id, but I don't know how to do it with the ArrayList.
It should be:
1 1000
2 2000
...
You cannot do what you ask because part of the information you need to print (profit) is lost. What you need to do first is to create a class that holds a company ID and the profits. With the new version of Java, you can create a record that will hold such information. A Java Record is nothing more than a POJO that is identified with that new keyword (record) and does not require you to create all the boilerplate code. Your record class will look something like this:
public record CompanyRecord(int companyID, float profit) {
}
You don't even need to override toString(). That is, unless you want to print the contents of the record in a different way than the default. Then, you will need to create a list of CompanyRecord objects:
ArrayList<CompanyRecord> companies = new ArrayList<>();
Then, you can do whatever you need. For example, I created this simple demo that create a list of 10 company records and uses the loop counter to set the company ID and as a multiplier for the profits. Lastly, it prints out the record to the console.
public class CompanyRecordDemo {
public static void main(String[] args) {
ArrayList<CompanyRecord> companies = new ArrayList<>();
float profit = 1000.0f;
for (int i = 1; i <= 10; i++) {
CompanyRecord rec = new CompanyRecord(i, profit * i);
companies.add(rec);
System.out.println(rec);
}
// do whatever you need with the list...
}
}
The output of this small program is:
CompanyRecord[companyID=1, profit=1000.0]
CompanyRecord[companyID=2, profit=2000.0]
CompanyRecord[companyID=3, profit=3000.0]
CompanyRecord[companyID=4, profit=4000.0]
CompanyRecord[companyID=5, profit=5000.0]
CompanyRecord[companyID=6, profit=6000.0]
CompanyRecord[companyID=7, profit=7000.0]
CompanyRecord[companyID=8, profit=8000.0]
CompanyRecord[companyID=9, profit=9000.0]
CompanyRecord[companyID=10, profit=10000.0]
This is probably the simplest way to accomplish what you need. You will need to use Java 14 or later to make use of Java Records, but I recommend you use the latest version.
UPDATE: One important thing to note is that Java records are immutable. So, they have no setters (mutator methods). You will have to set the values through the constructor and values cannot be changed afterwards. You can access (get) the property values by calling a method that has the same name as the field. For example, the getter method for profit is profit(). For example rec.profit().
I want to add a new element that has several arguments in an array. I know how to add with just one argument, but with more i don't know.
My code is:
private Calculate[] calculation;
public Element(int numElements) {
calculation = new Calculate[numElements];
}
public void addElement(Date elementDate, double price, ElementType type) {
int numElements = elements.length;
int i = 0;
if (i < numElements) {
Calculate[i] = calculation.elementDate;
Calculate[i] = calculation.price;
Calculate[i] = calculation.type;
i++;
}
}
Calculate[i] = calculation.elementDate;
Calculate[i] = calculation.price;
Calculate[i] = calculation.type;
You shouldn't assign to the same array index 3 times. You're overriding what you've just set.
Try this (Calculate should have a constructor):
Calculate[i] = new Calculate(elementDate, price, type);
You're also maintaining an index i, but you're not looping over anything. i is just incremented from from zero to one and is not really used (apart from an almost useless conditional check).
I suggest you read over a beginners Java tutorial. You seem to be missing a lot of the fundamentals, and Stack Overflow is not a place where we should have to show you how to write a for-loop. It's well-documented and demonstrated in a tonne of tutorials already.
I assume Calculate is a class defined else where with a constructor
There is a couple of issues in this piece of code:
You want to update the array, but specify array. Calculate[] is the array type. The name if the array is calculation. The other thing is that you are trying to access calculation.elementDate etc. but since that is an array, it does not have the field elementDate. I assume your Calculate class has that field. Also, you are not applying a loop. So currently your code will only update the array on index 0.
My code:
public void addElement(Date elementDate, double price, ElementType type) {
for(int i = 0; i < elements.length; i++) { // for loop over all elements in your array
Calculate calculate = new Calculate(elementDate, price, type) // I assume this constructor is available to initialize the Calculate object
calculation[i] = calculate;
}
Hope this helps.
I need to pre-populate a List with a large number of integer values.
Is there are faster way to do this other than iteration?
Current Code:
class VlanManager {
Queue<Integer> queue = Lists.newLinkedList();
public VlanManager(){
for (int i = 1; i < 4094; i++) {
queue.add(i);
}
}
This code is in the constructor of a class that is created pretty frequently so I'd like this to be as efficient (read:performance not lines of code) as possible
4094 isnt to many items to loop but if it is getting called very frequently you might look at doing something with a static variable.
private static Integer[] theList;
static {
theList = new Integer[4094];
for (int i = 1; i < 4094; i++) {
theList[i-1] = i;
}
}
then make that list a List
Queue<Integer> intQue = new LinkedList(Arrays.asList(theList));
There is a danger of using this method if you have a list of mutable objects. Heres an example of what can happen. Integers are immutable so this doesnt actually apply to your question as it stands
class MyMutableObject {
public int theValue;
}
class Test {
private static MyMutableObject[] theList;
static {
theList = new MyMutableObject[4094];
for (int i = 1; i <= 4094; i++) {
theList[i-1] = new MyMutableObject();
theList[i-1].theValue = i;
}
}
public static void main(String [] args) {
Queue<MyMutableObject> que = new LinkedList(Arrays.asList(theList));
System.out.println(que.peek().theValue); // 1
// your actually modifing the same object as the one in your static list
que.peek().theValue = -100;
Queue<MyMutableObject> que2 = new LinkedList(Arrays.asList(theList));
System.out.println(que2.peek().theValue); // -100
}
}
#Bohemian Has some good points on using a static List instead of an array, while the performance gains are very small they are none the less performance gains. Also because the 'array' is actually only ever being used as a List not an array it should be declared as such.
private static List<Integer> theList;
static {
theList = new ArrayList(4094);
for (Integer i = 0; i < 4094; i++) {
theList.add(i+1);
}
}
The fastest way would be to create a reference list (initialized using an instance block - neatly wrapping it all up in one statement):
private static final List<Integer> LIST = new ArrayList<Integer>(4094) {{
for (int i = 1; i < 4094; i++)
LIST.add(i);
}};
Then in your constructor, initialize the queue using the copy constructor:
Queue<Integer> queue;
public VlanManager(){
queue = new LinkedList<Integer>(LIST);
}
You will not write a faster implementation than what's in the JDK.
I realize this question has already been answered. But I think one important answer is missing: The fastest way to initialize a LinkedList with the values 0..4093 is .. DON'T DO IT AT ALL. Especially if speed is an issue.
What you basically are doing is creating a structure consisting of 4093 Node elements each consiting of two pointers to prev/next element and one pointer to an Integer object. Each of this Nodes must be created (and free). In addition nearly each contained Integer must be created (and freed). 'Nearly' because Java uses a cache for Integer but normally (you can change this with system properties) in the range of -127..127.
This is a lot to do in order to get a simple list of integer and if used intensively gives the GC a lot to do afterwards.
That being said there are numerous possible ways of doing this in a more efficient way. But they depend on what your concrete usage pattern is. Just to name a few:
Use an Array: boolean [] inUse' and set the taken vlan-id totrue` if it's taken
Even better use a BitSet instead of the array
Don't store which vlan is free, but which vlan is taken. I think they tend to be free and so there are much more free as there are taken ones. (this means much less to keep track of).
If you insist on using a LinkedList don't initialize it with your class but have it already initialized. This depends on how much of them you would need. You could keep a pool of them. Or perhaps your codes allows reusage of old lists. (yes, you could sort them after usage.)
Surely there are more...
All of this methods require you to build your own 'Queue' interface. But perhaps this has not to be as rich as Java's. And it really isn't that difficult. If you really use this intensively you could reach perfomance improvement factor 10x-1000x++.
A possible implementation using BitSet with an instantiation cost of nearly nothing could be:
import java.util.BitSet;
import org.testng.annotations.Test;
public class BitSetQueue {
// Represents the values 0..size-1
private final BitSet bitset;
private final int size;
private int current = 0;
private int taken = 0;
public BitSetQueue( int size ){
this.bitset = new BitSet( size );
this.size = size;
this.current = size-1;
}
public int poll(){
// prevent endless loop
if( taken == size ) return -1;
// seek for next free value.
// can be changed according to policy
while( true ){
current = (current+1)%size;
if( ! bitset.get( current ) ){
bitset.set( current );
taken++;
return current;
}
}
}
public boolean free( int num ){
if( bitset.get( num ) ){
bitset.clear( num );
taken--;
return true;
}
return false;
}
#Test
public static void usage(){
BitSetQueue q = new BitSetQueue( 4094 );
for( int i = 0; i < 4094; i++ ){
assertEquals( q.poll(), i );
}
assertEquals( q.poll(), -1 ); // No more available
assertTrue( q.free( 20 ) );
assertTrue( q.free( 51 ) );
assertEquals( q.poll(), 20 );
assertEquals( q.poll(), 51 );
}
}
I don't know if this is right, so I need your comments guys. I have an array of employee names. It will be displayed on the console, then will prompt if the user wants to insert another name. The name should be added on the end of the array(index 4) and will display again the array but with the new name already added. How do I do that? Btw, here's my code. And I'm stuck. I don't even know if writing the null there is valid.
public static void list() {
String[] employees = new String[5];
employees[0] = "egay";
employees[1] = "ciara";
employees[2] = "alura";
employees[3] = "flora";
employees[4] = null;
for(int i = 0; i < employees.length; i++) {
System.out.println(employees[i]);
}
}
public static void toDo() {
Scanner input = new Scanner(System.in);
System.out.println("What do you want to do?");
System.out.println("1 Insert");
int choice = input.nextInt();
if(choice == 1) {
System.out.print("Enter name: ");
String name = input.nextLine();
You can't, basically.
Arrays have a fixed size when they've been constructed. You could create a new array with the required size, copy all the existing elements into it, then the new element... or you could use a List<String> implementation instead, such as ArrayList<String>. I'd strongly advise the latter approach.
I suggest you read the collections tutorial to learn more about the various collections available in Java.
Also note that you've currently just got a local variable in the list method. You'll probably want a field instead. Ideally an instance field (e.g. in a class called Company or something similar) - but if you're just experimenting, you could use a static field at the moment. Static fields represent global state and are generally a bad idea for mutable values, but it looks like at the moment all your methods are static too...
Arrays are fixed in size. Once you declare you can not modify it's size.
Use Collection java.util.List or java.util.Set. Example ArrayList which is dynamic grow-able and backed by array.
If you really have to use arrays then you will have to increase the size of the array by using an intermediate copy.
String[] array = new String[employees.length + 1];
System.arraycopy(employees, 0, array, 0, employees.length);
array[employees.length] = newName;
employees = array;
However, the best way would be to use a List implementation.
It depends on whether the user can enter more than 4 employee names. If they can then using ArrayList is the better choice. Also the employees variable needs to be a static property of your class since being used in a static method.
private static String[] employees = new String[5];
static {
employees[0] = "egay";
employees[1] = "ciara";
employees[2] = "alura";
employees[3] = "flora";
employees[4] = null;
}
public static void list() {
for(int i = 0; i < employees.length; i++) {
System.out.println(employees[i]);
}
}
public static void addEmployeeName(String name, int index) {
employees[index] = name;
}
Here you are using static array which is fixed at the time of creation.I think you should use
java.util.Arraylist which will provide you facility of dynamic array.
I have a list of Nodes which I loop through:
for (int i = 0; i < aNodes.getLength(); i++) {
//
}
For example, assume the list contains 12 items, and that I know 3 items represent a row in a table, and the next 3 belong to the following row. That means my list of 12 Nodes originates from an HTML table containing 4 rows.
After each row I want to do something, for example, create a new object and populate it...or whatever. I have a solution:
ArrayList<Account> mAccounts = new ArrayList<Account>();
Account account = new Account();
for (int i = 0; i < aNodes.getLength(); i++) {
String nodeValue = aNodes.item(i).getNodeValue();
boolean isNewRow = (i % COLS_PER_ROW == 0);
if (isNewRow) {
account = new Account();
mAccounts.add(account);
}
switch (i % COLS_PER_ROW) {
case ACCOUNT_POS:
account.setAccount(nodeValue);
break;
case BALANCE_POS:
account.setBalance(nodeValue);
break;
case DATE_POS:
account.setDate(nodeValue);
break;
}
}
But there are numerous things I don't like about this solution:
An Account instance is created twice first time, once outside the loop and then once because a new row is detected.
It uses integer constants ACCOUNT_POS=0, BALANCE_POS=1, DATE_POS=2...this doesn't feel very nice and I think I should be using an enum.
I can't use an enum with the loop variable 'i'.
I can't use a for each loop since Nodes doesn't implement the correct interface,
Can anyone suggest a better way of doing this that solves the list of things I don't like about it?
Thank you.
You can increment i by COLS_PER_ROW instead of 1 and then write:
for (int i = 0; i < aNodes.getLength(); i += COLS_PER_ROW) {
account = new Account();
String account = aNodes.item(i).getNodeValue();
account.setAccount(account);
String balance = aNodes.item(i+1).getNodeValue();
account.setBalance(balance);
String date = aNodes.item(i+2).getNodeValue();
account.setDate(date);
mAccounts.add(account);
}
I can't use an enum with the loop variable 'i'.
No, but you can use:
for(SomeEnum item : SomeEnum.values()){
// code here
}
You're working with a position based file, so, if I understand you correctly, you've a structure which is organized as follows:
account;
balance,
date;
Knowing this, maybe you could better deal with the index in a while loop, rather than using a for loop, as follows:
int i = 0;
while (i < aNodes.getLength()) {
account = new Account();
account.setAccount(aNodes.item(i).getNodeValue());
account.setBalance(aNodes.item(i+1).getNodeValue());
account.setBalance(aNodes.item(i+2).getNodeValue());
mAccounts.add(account)
i += 3;
}
I would also suggest to extract the account-filling-code in a new method, like 'extractAccountFromNodes', and then call it inside the loop; so the loop could become:
int i = 0;
while (i < aNodes.getLength()) {
mAccount.add(extractAccountFromNodes(aNodes, i));
i += 3;
}
with the method extractAccountFromNodes like this:
private Account extractAccountFromNodes(Nodes nodes, int position) {
account = new Account();
account.setAccount(nodes.item(i).getNodeValue());
account.setBalance(nodes.item(i+1).getNodeValue());
account.setBalance(nodes.item(i+2).getNodeValue());
return account;
}