Going back over my basic ADT stuff here to revise for an interview, and trying to kill two birds with one stone by learning Java while I am. Attempting to write a simple algorithm for a merge sort with a generic linked list ( which I am creating myself). It's proving to be far more difficult than I had first imagined ! Can anyone help me out please ? I will start out working on the basics and will update this post as I get further in.
My code for the generic linked list is as follows :
public class NodeList<T extends Comparable<T> > {
private T head;
public NodeList<T> tail;
public NodeList( T item, NodeList<T> list ) {
head = item;
tail = list;
}
}
I am trying to access this class in another class I have made, which is as follows :
public class MyList<T extends Comparable<T>> {
private NodeList<T> nodes;
private static int size;
public MyList( ) {
nodes = null;
}
public MyList(T[] array ){
for( T item : array ) {
nodes = new NodeList<T>(item, nodes);
}
size = array.length;
}
public void add( T item ) {
nodes = new NodeList<T>( item, nodes );
size++;
}
public void addEnd( T item ) {
NodeList<T> temp = nodes;
while ( temp == null || temp.tail != null) {
temp = temp.tail;
}
size++;
temp.tail = new NodeList<T> ( item, null);
}
I believe, so far, everything to be correct up until the add and addEnd methods, which should add a generic to the start of the list and end of the list respectively.
My code continues with :
public static <S extends Comparable<S>>
MyList<S> sort( MyList<S> list ) {
if ( size > 1 ) {
MyList<S> left = leftHalf( list );
MyList<S> right = rightHalf( list );
list = merge( left, right );
}
return list;
}
private static <S extends Comparable<S>>
MyList<S> merge( MyList<S> left, MyList<S> right ) {
}
private static <S extends Comparable<S>>
MyList<S> leftHalf( MyList<S> list ) {
MyList <S> leftSide = new MyList();
int middle;
if(size % 2 == 1) {
middle = size +1;
} else {
middle = size;
}
for ( int countToMiddle = 0; countToMiddle < middle ; countToMiddle++ ) {
leftSide.addEnd(nodes);
}
// return elements from 0 .. list.size() / 2
}
And I get the error:
addEnd(S) in MyList cannot be applied to (NodeList)
which occurs when I run
leftSide.addEnd(nodes);
Can anyone see a reason for this/ tell me if I am correct up to this point of my work ? Thanks so much again!
If you want NodeList and MyList to only contain Comparable items,
you can replace the generic parameter T with something like:
public class NodeList<T extends Comparable> {
Or
public class NodeList<T extends Comparable<T>> {
And replace where you use Comparable with T. This way, you know T at least implements Comparable's methods.
Oracle's tutorials for generics should be able to help you with getting the hang of them.
One problem you may be having is that you refer to member variables from static functions, like in leftHalf you have:
for ( int countToMiddle = 0; countToMiddle < middle ; countToMiddle++ ) {
leftSide.addEnd(nodes);
}
nodes is a member variable, i.e. a non-static variable, so you can't call it from static methods. For that example, you'd have to get it from the passed MyList:
for ( int countToMiddle = 0; countToMiddle < middle ; countToMiddle++ ) {
leftSide.addEnd(list.nodes);
}
And the same goes for your other static methods that try to use member variables.
Also, the reason you are getting an error like: addEnd(S) in MyList<S> cannot be applied to (NodeList<T>) is because S is, according to your type parameter, a Comparable. NodeList does not extend Comparable!
The two solutions you have is
Make NodeList extend Comparable so you can pass it to MyList.addEnd
Make an overload (i.e. a different method with the same name) for addEnd that takes a NodeList, and add all the items in the passed NodeList to MyList
Or come up with a different solution that better fits the need of your classes.
While I realize you are implementing a linked list just to sharpen your skills for an interview (I wish you good luck!), I just want to add that there is a generified LinkedList already available in Java.
Why do you post almost the same question twice?
You could extend your question, add comments etc.
We already gave you that hint. :)
The error is happening because the class NodeList doesn't have a constructor that receives a generic T class and a NodeList.
Actually, this implementation will replace the reference object that nodes is referring on every loop. You should also fix that.
What you should do is put T to be a Comparable itself, and change the attribute, something like:
public class NodeList<T extends Comparable> {
private T head;
private NodeList tail;
public NodeList( T item, NodeList list ) {
head = item;
tail = list;
}
}
It would be better if you tell us what exactly the code is for.
Related
I have created an object ArrayList,
private ArrayList<Object> objects;
and I am initializing it in a constructor.
public ObjectManager(Handler handler) {
this.handler = handler;
objects = new ArrayList<>();
}
This ArrayList is then painted/added it to a canvas.
public void renderObjects(Graphics g) {
handler.getObjectManager().addObject(new InstanceOfObject(handler, 1000, 1000, g));
}
The method addObject(), adds an object to the ArrayList.
public void addObject(Object e) {
objects.add(e);
}
I would like to remove this object later, by using a similar line of code,
public void removeObject(Object e) {
objects.remove(e);
}
however I do not know how to do that because I do not know how to pass in the object that is being removed. The only way I can think of passing in the object is by doing the following:
handler.getObjectManager().removeObject(new InstanceOfObject(handler, 1000, 1000, g));
I don't even know if this would work because it's removing an "new" object. And even if it does, "g" is not defined. If I define it in the constructor, I have to change many different things which results in an error (usually a NullPointerException), but even then I cannot figure out how to call this method by passing in the Graphics g parameters.
Your Question is not clear, but this might help.
The List interface implemented by ArrayList already offers a remove method. No need for you to re-invent that.
Object reference
To remove an object, keep and pass a reference to the particular object.
Dog alice = new Dog( "Alice" , "Labrador" ) ;
Dog bob = new Dog( "Bob" , "Chihuahua" ) ;
List< Dog > dogs = new ArrayList<>() ;
dogs.add( alice ) ;
dogs.add( bob ) ;
…
dogs.remove( bob ) ;
Index number
Alternatively, remember the slot (index) of the list containing the object you want to remove. Pass that zero-based index number to the remove method.
You can actually find Java's source code on the web (like https://github.com/AdoptOpenJDK/openjdk-jdk11/blob/master/src/java.base/share/classes/java/util/ArrayList.java#L644), or even as src.zip in the JDK itself. So this is how remove() looks like:
public boolean remove(Object o) {
final Object[] es = elementData;
final int size = this.size;
int i = 0;
found: {
if (o == null) {
for (; i < size; i++)
if (es[i] == null)
break found;
} else {
for (; i < size; i++)
if (o.equals(es[i]))
break found;
}
return false;
}
fastRemove(es, i);
return true;
}
and while the loops with the labeled breaks may look a bit esoteric, the important part is the o.equals(): if your "InstanceOfObject" class implements its own equals(), you can make the comparison work with freshly made throwaway instances too.
So I have been reviewing my data structures and came across an interesting thought regarding Java generics and the Object class. I have implemented and run a "generic bag" in two different ways (Notice below: IObjectBag.java, ObjectBag.java, IGenericBag.java, and GenericBag.java) and have used them both (Notice: Below main.java and Output). I have removed some of the unnecessary code as per stack overflow rules but if you want the full implementation, let me know.
Also, I have researched the topic in many websites, books and courses in addition to looking at the source code for the ArrayList class here and I understand that my GenericBag is a better option than my ObjectBag but not well enough to explain it in a practical way during an interview. And I am confused that my GenericBag uses more casting operations than my ObjectBag in its implementation (see Remove and PrintBag).
So, other than the syntactic sugar, why is my GenericBag better? Please use my classes as examples.
Are there any important differences in runtime/overhead/space/time I am not noticing?
How would you answer this question or expect it to be answered in an interview?
Bonus questions: If you want, please answer the bonus questions in the Main and GenericBag comments (I think I can answer them myself though, just want to hear your opinion).
IObjectBag interface:
public interface IObjectBag {
void add(Object item);
Object remove(Object item) throws NoSuchElementException;
boolean isEmpty();
int find(Object item);
Object get(int index);
int numItems();
}
ObjectBag class:
public class ObjectBag implements IObjectBag {
private Object [] items; // the java class attribute that will hold out "ints"
private int numItems;
public static void printBag(IObjectBag bag) {
for(int i = 0; i < bag.numItems(); i++) {
System.out.println(bag.get(i));
}
}
public ObjectBag(int size) {
this.items = new Object[size]; // fills array with null values
this.numItems = 0;
}
public void add(Object item){
// adds item to end of bag
}
public Object remove(Object item) {
int index = this.find(item);
if(index == -1) throw new NoSuchElementException("oops nothing found");
Object out = this.items[index];
this.items[index] = null;
this.numItems -= 1;
if(index + 1 != this.items.length && this.items[index + 1] != null) {
for(int i = index; i < this.items.length; i++) {
if(i + 1 != this.items.length) this.items[i] = this.items[i + 1];
}
this.items[this.items.length - 1] = null;
}
return out;
}
public int find(Object item) {
// return index given item or -1
}
public Object get(int index) {
// returns item given index
}
}
IGenericBag class:
public interface IGenericBag <T> {
void add(T item);
T remove(T item) throws NoSuchElementException;
boolean isEmpty();
int find(T item);
T get(int index);
}
GenericBag class:
public class GenericBag<T> implements IGenericBag<T> {
// private T[] items; can't use this b/c see comment in constructor
private Object[] items;
private int numItems;
public static void printBag(GenericBag bag) {
for(int i = 0; i < bag.numItems(); i++) {
System.out.println(bag.get(i));
}
}
public GenericBag(int size) {
// this.items = new T[size]; Bonus: throws generic array creation error (why?)
this.items = new Object[size];
this.numItems = 0;
}
public void add(T item){
this.items[this.numItems] = item;
this.numItems += 1;
}
public T remove(T item) {
int index = this.find(item);
if(index == -1) throw new NoSuchElementException("oops nothing found");
T out = (T) this.items[index];
this.items[index] = null;
this.numItems -= 1;
if(index + 1 != this.items.length && this.items[index + 1] != null) {
for(int i = index; i < this.items.length; i++) {
if(i + 1 != this.items.length) this.items[i] = this.items[i + 1];
}
this.items[this.items.length - 1] = null;
}
return out;
}
public int find(Object item) {
// given object return index or throw exception
}
public T get(int index) {
return (T) this.items[index];
}
}
Main class:
public class Main {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
System.out.println("Hello StackOverFlow!");
Object int1 = new Integer(1);
Object int2 = new Integer(2);
Object int3 = new Integer(3);
/* using my object bag ************************************************/
System.out.println("using my object bag");
IObjectBag myObjectBag = new ObjectBag(3);
myObjectBag.add(int1);
myObjectBag.add(int2);
myObjectBag.add(int3);
myObjectBag.remove(int2);
ObjectBag.printBag(myObjectBag);
/* using my generic bag ***********************************************/
System.out.println("using generic bag");
// Bonus Question: using object like above causes error at add method (why?)
Integer int4 = new Integer(4);
Integer int5 = new Integer(5);
Integer int6 = new Integer(6);
GenericBag<Integer> myGenericBag = new GenericBag<Integer>(3);
//Bonus Question: using Interface decllaration like above causes error in print bag (why?)
myGenericBag.add(int4);
myGenericBag.add(int5);
myGenericBag.add(int6);
myGenericBag.remove(int4);
GenericBag.printBag(myGenericBag);
}
}
Output:
Hello StackOverFlow!
using my object bag
1
3
using generic bag
5
6
Problems with your ObjectBag that are 'automaticaly' solved by the type safety offered by your GenericBag implementation:
Accessing an entry returns Object, at this stage you do not know of what type Object is.
You can insert any types of Objects (mixed) e.g a String and an Integer into the same list, this is an anti pattern and causes non readable code (try it with your Generics bag!)
Because your compiler knows the type of your GenericBag after you have declared it, at any stage of your code if you hover over your genericBag instance you will know its type, this makes your code more readable and also extendable for other people
Generics also offer way more, imagine you want your GenericBag to only accept numbers, then you could write it as follows:
public class GenericBag<T extends Number>
My suggestion for you is to read some articles on Java basics and especially Generics, having a praxis based way of learning is a good thing, but there are plenty articles that can give you some very nice theoretical insight on the matter.
https://www.baeldung.com/java-generics
Reason of using, let's say, GenericBag<String> over ObjectBag is essentially the same as for using String (or any other type) over an Object:
Type safety.
You declare that some method returns a collection of Strings and nothing else, thus preventing yourself from putting there other objects, or trying to treat what you get from a bag as some other type. This might sound stupid when you have 100 lines of code, but this may save you lot of debugging time when you work with decent codebase.
Although, type safety is not a silver bullet, it is just an instrument, that some people find useful and some don't. I'm pretty sure it is a popular holywar topic for any programming forum.
If you feel comfortable working without this paradigm (Javascript background, right?), you might consider trying some dynamically typed language like Python instead of Java.
I'm pretty new to Java and I'm attempting to implement a Generic LinkedList Class in java. Below is the code yet it doesn't quite work right. I have some extra free time this semester and want to use this generic linkedlist to solve the linkedlist programming challenges in my interview test prep book. What am I doing wrong here? Why won't this work the way I want it to?
Thanks for the help in advance.
public class LinkedList {
public static linkedlist ll;
public static void main(String[] args) {
// TODO Auto-generated method stub
ll = new linkedlist();
Node one = new Node(1);
Node two = new Node(2);
Node three = new Node(3);
Node four = new Node(4);
System.out.println("s");
}
public static class linkedlist<T>{
public Node head;
public Node tail;
int size;
#SuppressWarnings("unchecked")
public linkedlist(){
size = 0;
}
void add(Class<T> typeParameterClass){
if(head == null){
head = new Node(typeParameterClass);
}
Node temp = new Node(typeParameterClass);
Node headCopy = head;
if(headCopy != null){
while(headCopy.getNext()!= null){
headCopy = headCopy.getNext();
}
headCopy.setNext(temp);
}
size++;
}
}
public static class Node<T>{
//final Class<T> typeParameterClass;
Class<T> value;
int intValue;
Node next = null ;
Node prev = null;
public Node(Class<T> typeParameterClass){
value = typeParameterClass;
}
public Node(int i) {
intValue = i;
// TODO Auto-generated constructor stub
}
public Node getNext() {
// TODO Auto-generated method stub
return next;
}
public Node getPrev() {
return prev;
}
public void setNext(Node temp){
next = temp;
}
}
}
You would first spent some reading about Java naming conventions. Class names start Uppercase; always; even for inner static classes. You would also avoid using too many inner static classes in the first place. In your example, there is absolutely no need to do it this way. You would rather put the methods that make up a LinkedList directly on the LinkedList class. You want that users of that class use that class; and not some inner static thing like LinkedList.linkedlist or LinkedList.Node.
You see, right now, your methods are all on the inner Node class. So, do you want to deal with Nodes each time when doing something about your List?!
Then you read about how generics work in general. Example:
Node one = new Node(1);
is probably not even compiling, but even when it does, it creates a raw type; as you do not have the type parameter there. You need something like:
Node<Integer> one = new Node<>(1);
instead - you have to tell the compiler what real type you want to use instead of that anonymous T.
In other words: start reading here. Right now, you have like 25% knowledge/understanding; and that is not enough to start coding.
That is about what can be said without further description from your side about "what is not working" in your code. And even then: as said; your code is on such a low scale of "understanding" that the only reasonable answer is: step back and learn about the things you want to use.
I am trying to swap 2 objects within an ArrayList. To accomplish this, I am creating a new list where the objects are swapped, then overwrite the old list entirely with the, swapped list. However, I am having trouble adding the objects from the old list to the new list.
The program takes input from a text file, reads the data into objects (circles and rectangles, which are extensions of GeometricObject) and then adds those objects to an ArrayList called objectList.
Here is the code:
public static <E extends Comparable<E>> void swapCells
(ArrayList<E> objectList, int left, int right) {
/* The user may enter the two indices, "left,"
* and, "right," in any order which they desire.
* Because of this it will be necessary to determine
* which is larger or "right" index, and which is
* the smaller or "left" index
*/
int temp;
ArrayList<GeometricObject> swappedList = new ArrayList<GeometricObject>();
if (left > right) {
// Exchanges left and right
temp = left;
left = right;
right = temp;
}
for (int i = 0; i < objectList.size(); i++) {
if (i == left) {
swappedList.add(objectList.get(right));
System.out.println( swappedList.get(i).getArea());
} else {
swappedList.add((E) objectList.get(i));
}
}
} // End of swapCells
I get the following syntax error, and do not know what to do about it.
The method add(GeometricObject) in the type ArrayList<GeometricObject> is not applicable for the arguments (E)
The error is specifically at, swappedList.add(objectList.get(right)); and also wappedList.add((E) objectList.get(i));.
I do not believe this is exactly the answer your looking for, but it may help.
If you typecast with GeomtricObject you will get a functioning code, however, this defeats the purpose of using a generic if your just going to force it into a Geometric Object.
You also need to add else if to get the left object swapped to right position
You may also want to print out the swappedList to confirm that the action has been completed.
for (int i = 0; i < objectList.size(); i++)
{
if (i == left) {
swappedList.add((GeometricObject) objectList.get(right));
}else if (i == right)
swappedList.add((GeometricObject) objectList.get(left));
else {
swappedList.add((GeometricObject) objectList.get(i));
}
}
EDIT 2:
The following will aid you in the operation you were looking for in generics.
You will need to make a temp and cast it to E. You will also need to use the following code as well in its correct arguments and/or form / notation.
E temp
List.set(____ , _____)
List.get(____ )
If your still having trouble with this swap function look at one that is not Generic.
EDIT 3:
You most likely have the same problem as I do, and you also need to sort the Generic. You can use the selectionSort Method below to help you on the assignment. You will need to change the method so that it works for an ArrayList instead of a Array. This means you will need to make use of the suggestions in EDIT 2 to modify the code below. You may also need to use the compareTo Method.
private static void selectionSort(int[] list, int low, int high) {
if (low < high) {
int posMax = high;
int theMax = list[high];
for (int i = 0; i < high; i++) {
if (list[i] > theMax) {
theMax = list[i];
posMax = i;
}// if
}// for
list[posMax] = list[high];
list[high] = theMax;
selectionSort(list, low, high - 1);
}// if
}
You are trying to add E objects to a list of GeometricObject. That’s why you get an error. Your list swappedList should be of type ArrayList<E>, or better: List<E>.
On the other hand, you can modify the type of the function to:
public static void swapCells(ArrayList<GeometricObject> objectList, int left, int right)
Oh, and do something with this list you’ve built. Your current code just discards it.
I first want to thank everyone who contributed to answering this question. I consulted with my teacher on office hours sometime last week. My teacher had me draw out a mental picture of what the problem was before writing the code, then a physical picture on paper and pencil of the process to be used. Finally after writing the code, here is the solution:
public static <E extends Comparable<E>> void swapCells
(ArrayList<E> objectList, int left, int right) {
/*
* Create a temporary generic object so that the left and
* right objects can be swapped without losing any data.
*/
E temp = objectList.get(left);
// Place the right object into the left position
objectList.set(left, objectList.get(right));
/*
* Place the temporary (left) object into the right
* position.
*/
objectList.set(right, temp);
} // End of swapCells
There was really no need to even create a second array list, when one can simply use a temporary object E.
I am trying to add a linkedlist to another linkedlist using a method called addList in the MyLinkedList class. What I'm stuck on is how to implement the method so that I can add a linkedlist to another linkedlist of index location of my choosing.
Here's what I have in MyLinkedList:
public void addList(int index, E e){
if(index == 0){
addFirst(e);
} else if (index >= size){
addLast(e);
}
else{
Node<E> current = head;
for(int i = 1; i < index; i++){
current = current.next;
}
Node<E> temp = current.next;
current.next = new Node<E>(e);
(current.next).next = temp;
size++;
}
}
I know this method won't work, I've tried it.
In my program itself, I have these:
public class linkedlistwork {
public static void main(String[] args) {
MyLinkedList<String> alpha = new MyLinkedList<String>();
alpha.add("hello");
alpha.add("world");
alpha.add("this");
System.out.println(alpha);
MyLinkedList<String> beta = new MyLinkedList<String>();
beta.add("is");
beta.add("java");
beta.add("rocks");
System.out.println(beta);
alpha.addList(1, beta);
System.out.println(alpha);
}
}
The correct output would be something like:
OUTPUT:
[hello, is, java, rocks, world, this]
My program would not run, an error occurred in this line
alpha.addList(1, beta);
on the "beta" part, it says:
method addList in class MyLinkedList cannot be applied to given types;
required: int,String
found: int,MyLinkedList
reason: actual argument MyLinkedList cannot be converted to String by method invocation conversion
where E is a type-variable:
E extends Object declared in class MyLinkedList
How would I fix my method so that I can use it correctly?
Thanks in advance!!
It appears to me that your addList method signature is the problem.
It should look like:
public void addList(int index, List<E> list)
You need to have a method for adding MyLinkedList<String>. Something like this would do
public void addList(int index, MyLinkedList<E> beta){
//Somehow loop through the MyLinkedList, and do this.
add(index+i,beta(i));
}