Choosing between `Collection <MyObject>` inside vs outside of `MyObject` class [Java] - java

What is the best way to store a Collection<MyItem> ? This collection is effectively static for current user. Each user can only see their collection. MyItem item implements IItem:
public interface IItem {
public Integer getItemID();
public void setItemID(Integer id);
public String getTitle();
public void setTitle(String title);
/*more getters and setters*/
public IItem parseServerResponse(String response);
public int postItem(); //posts this IItem to server, return ok ->200, unauth->401, etc
public IItem findItem(String[] filters);
/*more advanced methods*/
}
I can store Collection<MyItem> elsewhere, but then I can't access private MyItem methods from CurrentMyItems:
public class CurrentMyItems{
private final List<IItem> allItemsList;
public CurrentMyItems(String allItemsServerResponseString){
JSONArray rawItems = parseResponse(allItemsServerResponseString);
int arrSize = rawItems.length()+estimateQuantityOfNewItems();
List<IItem> allItemsList = new ArrayList<>(arrSize);
for (int i = 0; i < Items.length(); i++) {
allItems.add(i, parseItem(Items.get(i)));
}
}
/*methods*/
}
Or inside of the MyItem class (see commented out options):
public class MyItem implements IItem {
/*
private final static List<IItem> allItemsStaticList = new ArrayList<>();
private final static Map<Integer, IItem> allItemsStaticMap = new HashMap<>();
private final List<IItem> allItemsList; //
private final static Map<Integer, IItem> allItemsMap;
*/
/*implemented methods*/
}
allItemsStaticList - stores a static list of all Items. Seems memory efficient, but what if I need to store separate collections of MyItems in future? This is highly unlikely, but still...
allItemsList - Same class has two distinct functions. It is either
storing a single Item, in which case allItemsList/Map = null;
or
allItemsList = new ArrayList<>();, while other fields are empty.
This seems OK, but it breaks the Least Surprise Principle.
Which approach to store a MyItemCollection is more natural?
Also, should I store Items as a Map or a List given that MyItem myItem = getMyItemByID(int id); is the main way to access MyItem?
Update
We can implement an Item class so that an instance can either hold a collection of Item instances or the modeled data, but not both.
public class Item {
private final Map<Integer, Item> itemsMap;
private final IntegerProperty itemID; // private final String[] names;
public Item(){
itemsMap = new HashMap<>();
itemID = null; //names = null;
}
private Item(Integer id) {
itemsMap= null;
itemID = new SimpleIntegerProperty(id); //names = new String[1];
}
public Item makeGenericItem(){
return itemsMap == null ? null : new Item(itemsMap.size());
}
// other methods, including getters and setters
}
But at what cost?.. This class violates single responsibility principle.
Conclusion - in most cases a Collection of Item instances should be stored outside of Item class.

In OOP the data elements of an object are also known as attributes of the object. So, you should ask yourself whether a collection of items is an attribute of an item or not.
For example, when you assume your items are students. Would you say that a list of students is an attribute of a student? -- Probably not, as a list of students is not part of a student. Instead a student is part of a list of students.
Since a list of students is not an attribute of students in real life, I would not model it differently in code just to make it technically more elegant.
The design of your classes should be driven by the structure of the domain that your are working in. When you need to decide where to put an attribute do not ask "does it make sense to put it here because of the features my programming language offers?" but ask "where does this attribute belongs to in my domain?".

Related

How to print an arrayList using a variable containing the arrayList name

I know this sounds a bit stupid but I am extending a text-based game and I'm trying to figure out a way to print out the items that are in the room. I have made an arrayList for the room and added and "Item" into it by using a separate class called Item.
import java.util.ArrayList;
import java.util.List;
public class Item
{
private String itemName;
private Boolean pickUp;
private Integer itemWeight;
/**
* Constructor for objects of class Items
*/
public Item(String itemName, Boolean pickUp, Integer itemWeight)
{
this.itemName = itemName;
this.pickUp = pickUp;
this.itemWeight = itemWeight;
}
/**
* Returns the name of the item
*/
public String getItemName()
{
return itemName;
}
/**
* Returns the pickUp ability of the item
*/
public Boolean getPickUp()
{
return pickUp;
}
/**
* Returns the weight of the item
*/
public Integer getItemWeight()
{
return itemWeight;
}
}
The part I'm stuck on now is printing those elements out when entering the room. Theres a function in the code that keeps track of the current room and I am trying to use that function to then print out the elements of that corresponding rooms arrayList. Each room in the game is an object from the "Room" class.
public class Room {
private String description;
private String listName;
private HashMap<String, Room> exits; // stores exits of this room.
public Room(String description, String listName)
{
this.description = description;
this.listName = listName;
exits = new HashMap<>();
}
public String getListName()
{
return listName;
}
private void createRooms()
{
Room playground, assemblyHall, groundHallway, lunchHall, ITRoom, groundToilets, headteacherOffice, footballCage, stairs, groundClassroom, firstFloorClassroom, exitGate;
// create the rooms
assemblyHall = new Room("in the assembly hall", "assemblyHallList");
playground = new Room("in the main playground of the school", "playgroundList");
.......
currentRoom = assemblyHall;
ArrayList<Item> assemblyHallList = new ArrayList<Item>();
assemblyHallList.add(new Item("Piece of paper", true, 1));
These last 2 lines are an example of what I will be doing for every room with more items. I feel like I am going about this the wrong way but this was the only idea I had. The problem I have is printing out the elements when I enter a room as I would need to call on the method to get the name of the arrayList then use that name to then access the list and print out the elements in it but I have no idea how to use the string to accesss the array as I would have to use
currentRoom.getListName()
to get the name of the list but when I try to print it out it prints the name itself, not the elements. Any help is appreciated and I can send more parts of the class if needed. Didn't include it all as its very big and a lot of it is irrelevant to my problem
To answer your question, you can't just directly access a variable from a string containing its name. Technically, you could make a static Hashmap<String, ArrayList<Item>> to keep track of all the lists and access them by their name.
However, I don't see why you couldn't just make the list itself part of the constructor and then store the list as an instance variable of the Room.
Something like this would work better:
ArrayList<Item> items = new ArrayList<>();
items.add(new Item("Piece of paper", true, 1));
Room room = new Room("best room", items);
public class Room {
private String description;
private ArrayList<Item> items;
private HashMap<String, Room> exits;
public Room(String description, ArrayList<Item> items)
{
this.description = description;
this.items = items;
exits = new HashMap<>();
}
// Other members not shown
}
You create a ArrayList<Item> assemblyHallList, then you create
Room assemblyHall = new Room("in the assembly hall", "assemblyHallList");
And now you expect that room to have some reference to the List.
That's not how this works. The Room now has the String value "assemblyHallList" in the listName field. But there is no reference to the List. And since assemblyHallList is a local variable in your createRooms method, there's no way of resolving it.
The Solution is this: Don't assign the name of the List variable to the Room (why would that work?), instead assign the List itself:
List<Item> assemblyHallList = ...;
Room assemblyHall = new Room("in the assembly hall", assemblyHallList);
Of course you need to change the type of the constructor's parameter and Room's listName field to List<Item>.

How can I store objects from another class in an ArrayList from a different class?

I am quite new to programming. I am creating a project where in class Countries there is data in the form of objects which contain the name of the country and the country's dialing code. There is another class called CountriesList where all the objects from class Country will be stored in an ArrayList. And finally class person where the user will input his country name and phone number and the system will match the country name with the data stored in the array-list and output the dialing code/phone code.
Now the problem is, I can't store the objects from class Countires in the array-list from class CountriesList. Does anyone know how I can store objects from another class in an array list? I tried various ways, but it kept giving errors.
Source code:
public class Countries {
private String countryname;
private int phonecode;
public Countries(String n,int c){
this.countryname=n;
this.phonecode=c;
}
public String getCountryName(){
return this.countryname;
}
public int getPhoneCode(){
return this.phonecode;
}
public static void main(String[] args){
Countries usa = new Countries("USA",1);
Countries india = new Countries("India",91);
Countries antarctica = new Countries("Afg",677);
Countries bangladesh = new Countries("Bangladesh",880);
Countries uk = new Countries("UK",44);
}
}
public class CountriesList {
private Countries list;
public CountriesList(){
ArrayList<Countries> list = new ArrayList<>();
}
public static void main(String[] args){
}
}
Name your class in the singular rather than plural. Each object will represent a single county.
Java conventions for naming variables is camelCase.
More briefly define your country class as a record.
record Country ( String name, int phoneCode ) {}
Just use a List such as ArrayList directly. No need to make a class just to contain a list.
List < Country > countries = new ArrayList<>() ;
countries.add( new Country( "United States , 1 ) ) ;
Use List.of to put all the countries into an unmodifiable list in a single statement.
Search list by the property of name.
String target = "United States" ;
Optional < Country > c =
countries
.stream()
.filter( country -> country.name.equals( target ) )
.findAny()
;
Wrapping your collections (in your case the List) CAN be best-practice, and this is what is then referred to as First Class Collections, but you should only wrap them if you want to add additional behavior (methods) around the collection e.g. some predefined filter behavior or optional appending based on domain checks.
If you are not planning to create or use additional methods others than the ones the collection API already provides, then you don't need the wrapper and you can simply use the List/Set/Queue whatever that you have.
Btw; you should have written your code like below in order to achieve what you wanted to do, passing the collection via the constructor of the wrapper.
public class CountyList {
private static final Predicate<Country> disallowPhoneCode999Predicate = (c) -> c.getPhoneCode() != 999;
private final List<Country> list;
public CountyList(List<Country> countyList){
this.list = countyList;
}
// delegate methods
public Country get(int index) {
return list.get(index);
}
// add additional methods (very silly example)
public void addConditionally(Country country) {
if (disallowPhoneCode999Predicate.test(country)) {
list.add(country);
}
}
// or return the wrapped collection (not the best approach design-wise but possible)
public List<Country> getWrappedCollection() {
return list;
}
}
public class Application {
public static void main(String[] args){
List<Country> countries = List.of(
new Country("USA",1),
new Country("India",91));
CountyList countriesList = new CountyList(countries);
countriesList.addConditionally(new Country("Bangladesh", 882));
countriesList.add(new Country("Afg", 677));
}
}

How to use hashtable and bucket hashing

So recently I have been trying to practice with hashing and using linkedlists in a table to store values. I understand the concept but I am having trouble putting it into practice and can't seem to find what I'm looking for online.
For example:
Let's say I wanted to use a hashtable to store things like for a computer such as the monitor, mouse, etc. I would want methods such as:
boolean addMonitor(String id, String description, double price, int units, String size)
boolean addMouse(String id, String description, double price, int units, int buttons)
I don't understand how to use these methods to store them in a hashtable. I would obviously like to use other methods to access and change the values within each later too. Any help is appreciated. Thank you.
Even if its name says "table", HashTable is not like a "database table" where you have columns, and each column store values... it seems that you want use hashtable as a database table.
Hashtable store objects! So your methods should look better like this:
public class Example {
public static void main(String[] args) {
ItemStore store;
Monitor monitor;
Mouse mouse;
store = new ItemStore();
monitor = new Monitor();
monitor.id = 2;
monitor.price = 6;
mouse = new Mouse();
mouse.id = 7;
mouse.buttons = 3;
store.addItem(monitor);
store.addItem(mouse);
System.out.println(store.getItem(2).price); // = 6
System.out.println(((Monitor) store.getItem(2)).dpi);
System.out.println(((Mouse) store.getItem(7)).buttons); //Downcasting ... = 3
}
public static class Item {
String id;
String description;
int price;
// common attributes here!
}
public static class Monitor extends Item {
private int dpi;
// monitor particular atributes here!!
}
public static class Mouse extends Item {
private int buttons;
// mouse particular attributes here!!!
}
public static class ItemStore {
private Hashtable<String, Item> table = new HashTable<>();
public boolean addItem(Item item) {
this.table.put(item.getId(), item);
}
public Item getItem(String id) {
return this.table.get(id);
}
}
}

Adding subcategories to a java Enum

Suppose I have a simple Java Enum:
public Enum itemType
{
FRUITS("fru"),
VEGETABLES("veg"),
LIQUOURS("liq"),
SODAS("sod");
private String dbCode;
public ItemType(String dbCode){
this.dbCode = dbCode;
}
public String getDbCode(){
return this.dbCode;
}
}
I would now like to introduce a "category" to this enum, for example to make the distinction between liquid items and solid items. I found two ways of doing this within the enum class, see below. However, both suffer from the same anti-pattern: if the amount of categories or amount of items ever increases/decreases (imagine 100 item types with 10 categories!), I've got a lot of updating to do. What patterns can I use to design this enum as cleanly and re-usable as possible?
First approach: Add additional properties to the enum
public Enum itemType
{
FRUITS("fru",false),
VEGETABLES("veg",false),
LIQUOURS("liq",true),
SODAS("sod",true);
private String dbCode;
private boolean liquid;
public ItemType(String dbCode, boolean liquid){
this.dbCode = dbCode;
this.liquid = liquid;
}
public String getDbCode(){
return this.dbCode;
}
public boolean isLiquid(){
return this.liquid;
}
}
Second approach: Use static methods to ask about subcategories
public Enum itemType
{
FRUITS("fru"),
VEGETABLES("veg"),
LIQUOURS("liq"),
SODAS("sod");
private String dbCode;
public ItemType(String dbCode){
this.dbCode = dbCode;
}
public String getDbCode(){
return this.dbCode;
}
public static boolean isLiquid(ItemType type){
switch(t){
case SODA:
case LIQOURS: return true;
default: return false;
}
}
How about using an EnumSet for that?
public enum ItemType
{
FRUITS("fru"),
VEGETABLES("veg"),
LIQUOURS("liq"),
SODAS("sod");
public static final EnumSet<ItemType> LIQUIDS = EnumSet.of(LIQUOURS, SODAS);
// ...
}
Then you can use ItemType.LIQUIDS.contains(someItemType) to check if someItemType is a "liquid".
I would do something like:
enum Category {
LIQUID, SOLID;
}
enum ItemType {
FRUITS("fru", SOLID),
VEGETABLES("veg", SOLID),
LIQUOURS("liq", LIQUID),
SODAS("sod", LIQUID);
private String dbCode;
private Category category;
public ItemType(String dbCode, Category category){
this.dbCode = dbCode;
this.category = category;
}
/* getters / setters */
}
That would allow, for example, that you can add new products and categories (e.g. BUTANE("but", GAS)) without having to modify the existing code (as would happen in Approach 2).
On the other hand, if the number of categories and items is long and changing, I would consider to use a SQL database.
Since you are modeling something that has no logic that can be encoded in an algorithmic way (i.e. there's no algorithm that would figure out that "sod" is liquid and "veg" is not) there is no way around enumerating all related pairs of (item, category) in one way or the other.
There are three approaches to implementing it:
Enumerate categories on item's side - this is what your code does in both cases, or
Enumerate items on category's side - this would build an enum of categories, and attach a full list of items to each of them, or
Enumerate item+category pairs independently - this approach may be useful when storing item/category mapping in the database or in a configuration file.
I would recommend taking the third approach as it is the most "symmetric" one. Make a table for categories with category codes, and add a "cross-table" (or a cross-file) that has all pairs of categories and their corresponding items. Read the cross table/file at startup, and set up the dependencies on both sides.
public Enum ItemType {
FRUITS("fru")
, VEGETABLES("veg")
, LIQUOURS("liq")
, SODAS("sod");
public void addCategory(ItemCategory category) ...;
public EnumSet<ItemCategory> getItemCategories() ...;
}
public Enum ItemCategory {
LIQUIDS("liq")
, SNACKS("snk")
, FAST("fst");
public void addItem(ItemType type) ...;
public EnumSet<ItemType> getItemTypes() ...;
}
Cross-file or cross-table may look like this:
liq liq
sod liq
fru snk
fru fst
sod fst
You process it by enumerating pairs, and calling addCategory on the pair's item side, and calling addItem on the pair's category side.
These were three excellent answers, but I think I can combine all three in one nice package:
public enum ItemType {
FRUITS("fru",PERISHABLE),
VEGETABLES("veg",PERISHABLE),
LIQUOURS("liq",LIQUIDS),
SODAS("sod",LIQUIDS),
FRESH_SQUEEZED_ORANGE_JUICE("orgj",LIQUIDS,PERISHABLE);
private final String dbCode;
private final EnumSet<ItemCategory> categories;
private static final Map<ItemCategory,Set<ItemType>> INDEX_BY_CATEGORY = new EnumMap<>(ItemCategory.class);
ItemType(String dbcode,ItemCategory... categories) {
this.dbCode = dbcode;
this.categories = EnumSet.copyOf(Arrays.asList(categories));
//for (ItemCategory c:categories) {
// // Illegal Reference to Static Field!
// INDEX_BY_CATEGORY.put(c, this);
//}
}
static {
for (ItemCategory c:ItemCategory.values()) {
INDEX_BY_CATEGORY.put(c, EnumSet.noneOf(ItemType.class));
}
for (ItemType t:values()) {
for (ItemCategory c:t.categories) {
INDEX_BY_CATEGORY.get(c).add(t);
}
}
}
public boolean is(ItemCategory c) {
return INDEX_BY_CATEGORY.get(c).contains(this);
}
public Set<ItemType> getAll(ItemCategory c) {
return EnumSet.copyOf(INDEX_BY_CATEGORY.get(c));
}
public String getDbCode() {
return dbCode;
}
}
Now,
we can easily ask about additional subcategories without writing the code for it: boolean isVegetableLiquid = VEGETABLES.is(LIQUIDS);
we can easily assign not only one, but multiple categories to an item as you can see for FRESH_SQUEEZED_ORANGE_JUICE.
we are using EnumSet and EnumMap for performance, including their methods like contains.
we absolutely are minimizing the amount of code required to add an additional item. This could be further minimized by setting this up by database or configuration. However, in that case we would have to avoid the use of Enum as well.

iterate through the objects of a class

I have created this class.
public class UserData {
private int user_Id;
private List<String> disease_List=new LinkedList<String>();
/*Constructor*/
public UserData(int u_id, List<String> d_list)
{
this.user_Id=u_id;
this.disease_List=d_list;
}
}
I created around 500 objects by reading data from file.
Now I want to search for values. For example If user enters a disease= allergy.
I want to iterate through all objects and display users that have allergy in disease_list.
I have searched on internet for this but have not found anything useful.
You have to add the userData objects in a collection and iterate them and access it's attributes say disease_list.
For example, if you've added the userData objects in a arrayList, you can iterate in the following way
for(UserData user:UserDataList){
if(user.disease_list.contains("allergy") {
//display the user details
}
}
I might have misunderstood the question.. But basically what you want to do is search for a certain keyword in the disease_List inside every UserData object, right?
So say you have added a String allergy = "allergy"; inside the disease_list.
What you want to do in order to find this you need to iterate through the disease_list instance.
for (int i = 0; i < disease_list.size(); i++) {
if(disease_list.get(i).equals("allergy")){
//Now you know this UserData object contains "allergy". Now you
//can choose to break the iteration and do something with
//the object.
}
}
You can use Lambdas expression (with java 8 or higher) to sort lists.
public class UserData {
private int user_Id;
private ArrayList<String> diseases;
/*Constructor*/
public UserData(int u_id, ArrayList<String> d_list)
{
this.user_Id=u_id;
this.diseases=d_list;
}
public ArrayList filter(String disease) {
return Arrays.stream(diseases).filter(x -> x.equal(disease)).toArray();
}
}
Java doesn't just automatically make a list of every object you create, you need to make the list yourself.
public class UserData {
/* This must exist somewhere */
private static List<UserData> everyone = new LinkedList<UserData>();
private int user_Id;
private List<String> disease_List=new LinkedList<String>();
/*Constructor*/
public UserData(int u_id, List<String> d_list)
{
this.user_Id=u_id;
this.disease_List=d_list;
everyone.append(this);
}
/* Iterator for all the objects */
public static Iterator<UserData> iterator() {
return everyone.iterator();
}
}
if you want to iterate here is one way
for(String disease:disease_list){
if(disease.equalsIgnoreCase("allergy")
log.info("You find out the Result");
}
else you can use contains() method
if(disease_list.contains("allergy")){
log.info("found the disease");
}

Categories

Resources