For the following class I want to access an object if the name equals to something, let's say "you". Otherwise I want to create it.
I want to check if an object exists that has the name as 'you' and then add entries to the ArrayList contInstances. If such an instance doesn't already exist I want to create it. Next time I might have to use the same object so that I can add some more entries to the ArrayList.
public class Values {
String name;
ArrayList<anotherClass> classInstances = new ArrayList<anotherClass>();
}
This happens to be in a loop. How can I do that?
Edit: I'll quote an example here:
if (an object exists that contains field 'name' == 'YOU'){
add entries to the array list directly using the available object
}
else {
create a new object and set the 'name' = 'YOU';
add entries to the array list;
}
It sounds kind of like you want to have a cache by name. Instead of an ArrayList, consider using a Map<String, AnotherClass> to keep track of Name->Object mappings.
You can then use this approach:
Map<String, AnotherClass> instances = new LinkedHashMap<String, AnotherClass>();
for (...) {
String name = getNextName();
AnotherClass instance = instances.get(name);
if (instance == null) {
instance = makeInstance(name);
instances.put(name, instance);
}
useInstance(name, instance);
}
After that loop is finished, if you still want a List<AnotherClass>, you can use return new ArrayList<AnotherClass>(instances.values());
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I am fairly new to Java proramming, as I have already stated in the title I want to pass some parameters that could or could not exist as instances of a class. If it does not exist, I want to create it. My code so far:
public class TestClass {
public static void main(String[] args) {
Person Ted = new Person();
Person Jack = new Person();
Item it = new Item(Ted);
Item itSec = new Item (Pierce); //Person Pierce doesn't exist => should be created
}
}
public class Person {
public Person(){
//some code
}
}
public class Item {
public Item(Person name){
if(!(name instanceof Person)){
Person name = new Person(); //create that missing instance
}
else{
//some code
}
void getItem(Person name){
System.out.println(name);
}
}
You misunderstand some things.
You can't use an identifier before it's been declared. The following is not correct in your code:
Item itSec = new Item (Pierce);
That's because you didn't declare Pierce before this line. You don't have to create an instance of the class, but you need a valid identifier. You should have declared it before this way:
Person Pierce;
At this moment the identifier, or the reference, is empty, so to say, or it is equal null. All object references which are not local are initiated this way, or they are equal false or 0, whichever is correct for their type. The result is the same as if you declared explicitly:
Person Pierce = null;
But let's move on. Say the reference has been declared. (Btw, Java uses camelCaseNotation for variables, so pierce would be correct.) Let's say we're at a point when Pierce is null or refers to some object, we don't know. Now we call this:
Item itSec = new Item (Pierce);
new Item(Pierce) calls the constructor public Item(Person name){...} in the class Item, which you should know. But now, in that constructor there's the line:
if(!(name instanceof Person)){
which you misuse. What this line is checking is not if the variable name equals null or an existing object, but variable type of name is a subtype of Person. Which will always return true in this place, as the function heading public Item(Person name){...} says this: the function is public, is a constructor, and the argument is of type Person (so Person or a subtype).
What you want to say here is this:
public Item(Person name){
if(name==null)){ //if the reference is empty
this.name = new Person(); //I'll explain this below
}
else{
this.name = name; //otherwise the local "name" will stay null
}
}
I used this.name and it was a jump ahead. Why? In your code that is Person name = ..., which is not correct as that name wouldn't last once the constructor's finished. You need a field in the Item class object, which will hold this value. So the Item class might be defined this way:
public class Item {
Person name;
//...
}
And now the field name holds the value assigned in the line:
this.name = new Person();
You need to use this to disambiguate which name you means. One is the Item class field, the other one is Person name the constructor parameter.
Now, we go back to the main function. If you want the variable Pierce to reference the newly created Person, this still needs to be done. The assignment can take place here, but first you'd have to create a function in the class Item that returns the value of its field name. So:
getName() {
return name;
}
And now call it from the main function:
Pierce = itSec.getName();
That's it. Finally, this function doesn't make sense:
void getItem(Person name){
System.out.println(name);
}
}
It doesn't get any Item. It only prints what you pass to it. And this doesn't mean that if you call it with getItem(Pierce), you will see "Pierce" on the screen. It will call the toString function in the object Pierce denotes, and as it is, you will get a standard object identifier. But if you define a function:
void printItem() {
System.out.println(name);
}
Then you can call it this way. For an existing object itSec:
itSec.printItem();
As for a getter function, it should return what you ask for, but that's another story.
What do you expect without instancing?
Person Pierce = new Person();
Item itSec = new Item (Pierce);
You cannot use a variable that does not exist...
Item itSec = new Item (Pierce);
The snippet above will never work because Pierce is undefined.
The code:
if(!(name instanceof Person)){
Person name = new Person(); //create that missing instance
}
Does not really make any sense, because that is the same as calling
Person Ted = new Person();
Person Jack = new Person();
Wherein the Person instance does not actually contain any data (unless you have some magic going on when instantiating the Person!)
I assume what you really want to pass is not an object whose variable is the name of a person, but rather a Person object that contains the name of the person.
If so, your code should be like this:
Person p1 = new Person("Ted");
Person p2 = new Person("Jack");
If you really want to do some instantiation if something does not exist, you might be able to do something like this:
Item(String personName) {
if(isExisting(personName)) {
getPerson(personName);
} else {
Person p = new Person(personName);
}
boolean isExisting(String personName) {
// Check if person exists somewhere
}
Person getPerson(String personName) {
// Retrieve the Person instance with the same person name.
}
if(!(name instanceof Person)){
Person name = new Person();
}
is meaning less because 'name' is always object is instance of person in this situation..
Item itSec = new Item (Pierce);
Pierce is not an object.. We can pass only Person object to constructor of Item class.. There is no any method to create an object of any class by just passing unkown variable..
Item itSec = new Item (Pierce); //Person Pierce doesn't exist => should be created
If it does not exist, I want to create it.
There's no "if" about it. The code never declared that variable, so it will never exist. (And never compile in its current state. Surely your Java compiler is telling you this.) Given that it always needs to be created, just create it:
Person Pierce = new Person();
Item itSec = new Item(Pierce);
Edit: Based on ongoing comments, it sounds like you want to have something more like a Map. Consider an example:
Map<String,Person> people = new HashMap<String,Person>();
people.put("Pierce", new Person());
The Map would basically be a collection of key/value pairs where the name is the key and the Person is the value. You can dynamically add/edit/remove elements to the collection as you see fit.
Then to use it, you'd call another operation on the map:
Item itSec = new Item(people.get("Pierce"));
You could use various operations to check if a value exists in the collection, add it, etc. You might even extend the class to add your own operations which create one if it doesn't exist when trying to get it.
Java won't dynamically create variables for you if a variable doesn't exist, but operations on a Map (or potentially other similar structures) can check if an element exists, add it, remove it, etc.
I am trying to replace element in collection with new modified version. Below is short code that aims to demonstrate what I'd like to achieve.
The whole idea is that I have one object that consists of collections of other objects. At some point in time I am expecting that this objects in collections (in my example phones) might require some modifications and I'd like to modify the code in one place only.
I know that in order to update the object's attributes I can use setters while iterating through the collection as demonstrated below. But maybe there is better, more general way to achieve that.
public class Customer {
private int id;
private Collection<Phone> phoneCollection;
public Customer() {
phoneCollection = new ArrayList<>();
}
//getters and setters
}
and Phone class
public class Phone {
private int id;
private String number;
private String name;
//getters and setters
}
and
public static void main(String[] args) {
Customer c = new Customer();
c.addPhone(new Phone(1, "12345", "aaa"));
c.addPhone(new Phone(2, "34567", "bbb"));
System.out.println(c);
Phone p = new Phone(2, "9999999", "new name");
Collection<Phone> col = c.getPhoneCollection();
for (Phone phone : col) {
if (phone.getId() == p.getId()) {
// This is working fine
// phone.setNumber(p.getNumber());
// phone.setName(p.getName());
// But I'd like to replace whole object if possible and this is not working, at least not that way
phone = p;
}
}
System.out.println(c);
}
}
Is this possible to achieve what I want?
I tried copy constructor idea and other methods I found searching the net but none of them was working like I would expect.
EDIT 1
After reading some comments I got an idea
I added the following method to my Phone class
public static void replace(Phone org, Phone dst){
org.setName(dst.getName());
org.setNumber(dst.getNumber());
}
and now my foreach part looks like that
for (Phone phone : col) {
if (phone.getId() == p.getId()) {
Phone.replace(phone, p);
}
}
And it does the job.
Now if I change the Phone class attributes I only need to change that method. Do you think it is OK solving the issue that way?
You should not modify the collection while you're iterating through it; that's likely to earn you a ConcurrentModificationException. You can scan the collection for the first object that matches your search criterion. Then you can exit the loop, remove the old object, and add the new one.
Collection<Phone> col = c.getPhoneCollection();
Phone original = null;
for (Phone phone : col) {
if (phone.getId() == p.getId()) {
original = phone;
break;
}
}
if (original != null) {
Phone replacement = new Phone(original);
replacement.setNumber(p.getNumber());
replacement.setName(p.getName());
col.remove(original);
col.add(replacement);
}
Alternatively, you could declare a more specific type of collection, such as a List, that would allow you to work with indexes, which would make the replacement step much more efficient.
If your phone IDs are unique to each phone, you should consider using a Map<Integer, Phone> that maps each phone ID to the corresponding phone. (Alternatively, you could use some sort of third-party sparse array structure that doesn't involve boxing each ID into an Integer.) Of course, if your IDs aren't unique, then you might want to modify the above to gather a secondary collection of all matching phones (and reconsider the logic of your existing code as well).
You can also use a Set (HashSet), this is only when you don't want to do the way Mike suggested.
Use the Phone as an item in the set. Don't forget to implement hashCode() and equals() in Phone. hashCode() should return the id, as it is supposed to be unique.
Since you are concerned about replacing the item, here's how HashSet will help you :
Create an instance of your object.
Remove the object you want to replace from the set.
Add the new object (you created in step 1) back to the set.
Both these operations 2 & 3 are guaranteed in O(1) / constant time.
You don't need to maintain a map for this problem, that's redundant.
If you want to get the object from the collection itself and then modify it, then HashMap would be better, search is guaranteed in O(1) time.
Instead of a list, use a map with the Phone's id as the key. Then your code looks like this:
public static void main(String[] args) {
Customer c = new Customer();
c.addPhone(new Phone(1, "12345", "aaa"));
c.addPhone(new Phone(2, "34567", "bbb"));
System.out.println(c);
Phone p = new Phone(2, "9999999", "new name");
Map<Integer, Phone> phoneMap = c.getPhoneMap();
phoneMap.put(p.getId(), p);
System.out.println(c);
}
If you take the object out from the collection and update its properties, it will get reflected in the same object in collection too.. Hence, you dont have to technically replace object after updating it.
As "Mike M." pointed out, you can use hashmap to retrieve the object quickly without iteration and update the object values.
If order matters to you, you can change Collection to List (Since you're always using an ArrayList anyway) and then:
int index = col.indexOf(phone);
col.remove(phone);
col.add(p, index);
I am trying to add an object to an arraylist.
The object is defined as:
ExercisesGroup group = new ExercisesGroup();
Array List defined as:
ArrayList<ExercisesGroup> groups = new ArrayList<ExercisesGroup>();
I am then populating the object in a loop (rs is a result set from a database):
while (rs.next()){
group.setExerciseGroupId(rs.getInt("idbodyarea"));
group.setExerciseGroupDescription(rs.getString("bodyareadescription"));
groups.add(group);
}
When I return the arraylist 'groups' the correct number of results are added, however the data is all the same, i.e. the last record is added for every slot.
<exerciseGroupsReturn>
<exerciseGroupDescription>Description2</exerciseGroupDescription>
<exerciseGroupId>2</exerciseGroupId>
</exerciseGroupsReturn>
<exerciseGroupsReturn>
<exerciseGroupDescription>Description2</exerciseGroupDescription>
<exerciseGroupId>2</exerciseGroupId>
</exerciseGroupsReturn>
Any idea what I am doing wrong?
You need to create a new instance of the object on every iteration:
while (rs.next()){
group = new ExercisesGroup();
//...
}
Also, it would be better if you change the declaration of groups variable from ArrayList<ExercisesGroup> to List<ExercisesGroup>. Refer to What does it mean to "program to an interface"?
It looks like you're creating your ExcerciseGroup outside of the loop so you're always referencing the same object. Put the ExerciseGroup constructor inside the loop.
I'm trying to select and get values from customer objects. What I want to do is to enter a personal number like "702312" and search after the customer objects that has a data member personal number that are equal to "702312". And when I found it I want to get the rest of the values or change it's content. This is some code that creates the customer objects from the class Customer and then it's stored in a arraylist.
// create a new customer object and send personal number, name and account number to constructor
Customer customer = new Customer(personalNumber, name, newAccountNumber);
// create an arraylist to store customer objects
ArrayList<Customer> customerList = new ArrayList<Customer>();
// add the new customer object to arraylist that holds all customer objects
customerList.add(customer);
I have tried to reach the values like this, but it's not working, so I'm looking for some help?
// search for customer
for (Customer customer : customerList) {
if(customer.getAccountOwnerPersonalNumber() == "702312"){
System.out.println("OK!!!");
}
}
And instead of:
if(customer.getAccountOwnerPersonalNumber() == "702312")...
I have tried this:
if(customer.personalNumber == "702312")...
Finally I have also tested it like this:
for(int i=0;i<customerList.size();i++){
if(customerList.get(i).getAccountOwnerName() == "702312");
System.out.println("OK");
break;
}
I'm not sure if I'm doing right!? Help is preciated! Thanks!
You need to use the equals() method to compare objects like Strings by their internal value (otherwise they will only be compared by reference):
if (customer.getAccountOwnerPersonalNumber().equals("702312")) {
System.out.println("OK!!!");
}
or, better, if it can potentially return null:
if ("702312".equals(customer.getAccountOwnerPersonalNumber())) {
System.out.println("OK!!!");
}
or, if applicable, just make it a primitive like int, so that == will work the way you intended:
private int accountOwnerPersonalNumber;
with
if (customer.getAccountOwnerPersonalNumber() == 702312) {
System.out.println("OK!!!");
}
You want .equals():
if(customerList.get(i).getAccountOwnerName().equals("702312"))
customer.getAccountOwnerPersonalNumber() == "702312" this is wrong you cant compare them like that because it will compare their object type. you should do like this
if(customer.getAccountOwnerPersonalNumber().equals("702312"))
or this is better
if("702312".equals(customer.getAccountOwnerPersonalNumber()))
If the data that you query for is a String then in that case you have to use
equals()
So the entire things will turn out to be
if(customer.getAccountOwnerPersonalNumber().equals("702312")) {
System.out.println("OK!!!"); }
You're trying to compare customer.personalNumber to "702312" which is a String, so i conclude customer.personalNumber is a String too.
If I'm right, try String.equals() method instead of == operator. This is because this operator compares objects instead of value, which is what you want.
if(customer.getAccountOwnerPersonalNumber().equals("70312");)
...
I would suggest you use java.util.Collections binarySearch(...) method. It's of order O(log n)(in this case, since ArrayList implements RandomAccess) which is tons better than the O(n) order you get from doing your for loop.
You can make a separate comparator for the desired Customer fields(s) (in this case "personalNumber"):
private static class CustomerPersonalNumberComparator implements Comparator<Customer> {
#Override
public int compare(Customer o1, Customer o2) {
return o1.getPersonaNumber().compareTo(o2.getPersonaNumber());
}
}
Also, don't forget to sort your list according to your desired search criteria (the Comparator) before using binary search on it:
Collections.sort(customerList,new CustomerPersonalNumberComparator());
int desiredIndex = Collections.binarySearch(customerList, new Customer("658",null,null), new CustomerPersonalNumberComparator());
Customer desiredCustomer = desiredIndex == -1 ? null : customerList.get(desiredIndex);
And don't forget that the binary search returns the position inside our array for the desired element or -1 if nothing was found. So you must use that returned position to retrieve the desired instance from the list.
I need to develop a simple cache (no concurrency or refresh required) to hold different types of objects. The lookup of these objects may be in a different way. Like lets say we are caching book object which has ISBN number and author. Lookup of this object can be either by ISBN number like
Book lookupBookByISBN(String isbn);
OR it could be a lookupByAuthor like
List lookupBookByAuthor(String authorName);
In a very simple way, it means I can have a Cache object which has two maps one to store book object by ISBN and another to store the same object by authorname.
Like this, think of many such object type like book, so I do not want to store the same object in different maps just because the lookup of them are different.
One way I was thinking of having a single Map whose key is a custom Key object and value is Object (so that I can store any object or list of object)
The Key object is a immutable object which might look like this
public class Key {
private final Stirng keyName;
private final String keyValue;
public Key(String name,String value) {
this.keyName= name;
this.keyValue = value;
}
//getters for keyName and value
//hashcode and equals to be put as a key of a map
}
Implementation of lookup method will be
public Book lookupBookByISBN(String isbn) {
Key key = new Key("ISBN",isbn);
return ((Book)map.get(key));
}
public List<Book> lookupBookByAuthor(String isbn) {
Key key = new Key("Author",isbn);
return (List<Book>map.get(key));
}
The insert into map needs to be carefully done as the same object needs to be inserted twice into the map.
public void putBook(Book book) {
Key key = new Key("ISBN",book.getISBN());
map.put(key,book);
key = new Key("Author",book.getAuthor());
List<Book> list = map.get(key);
if (null == list) {
list = new ArrayList<Book>();
map.put(key,book);
}
list.add(book);
}
I somehow feel this might not be a good idea and I might need to put the same object in the map N number of times depending upon N dimensions by which I need to lookup the object.
Is there anyother way to design the same in a better way?
When you store an object in a collection (of any kind), you only store a reference to the object. So go ahead and use multiple maps, you will have only one copy of the actual object.
For example
Map<String,MyBigObject> map1 = new HashMap...
Map<String,MyBigObject> map2 = new HashMap...
MyBigObject mbo = new MyBigObject(...);
map1.put(mbo.getISBN(),mbo);
map2.put(mbo.getAuthor(),mbo);
The single object mbo is now accessible via either map.
EDIT: If you're worried about the complexity of multiple maps complicating the code, write a class MultiMap that contains all the maps and manages them in whatever way you want. You could have methods add(MyBigObject...) which inserts the object into all the maps using the various property accessors to set the correct key, and then lookup methods such as getByAuthor(...) and getByISBN(...), and whatever else you need. Hide all the complexity behind a simple unified interace.