Stream Spliterator error after concat Java - java

When attempting to create an iterator from a Stream that was concatenated from two previous streams, I get a NoSuchElementException, as the iterator doesn't recognise that the Stream has elements due to some problems with the Spliterator. The concatenated Stream seems to have two spliterators, despite the previous streams being of the same type. I also get this error when I tried to convert the concatenated stream into an array to try get round the problem.
Stream<Node> nodeStream = Stream.of(firstNode);
while (!goal) {
Iterator<Node> iterator = nodeStream.iterator();
Node head = iterator.next();
Stream<Node> tail = Stream.generate(iterator::next).filter(n -> n != head);
Node[] newNodes = head.expand(end);
if (newNodes.length == 1) {
goal = true;
endNode = newNodes[0];
}
nodeStream = Stream.concat(Arrays.stream(newNodes), tail);
nodeStream = nodeStream.sorted(Comparator.comparing(n -> n.routeCost(end)));
}
The error is as follows:
Exception in thread "main" java.util.NoSuchElementException
at java.base/java.util.Spliterators$1Adapter.next(Spliterators.java:688)
at java.base/java.util.stream.StreamSpliterators$InfiniteSupplyingSpliterator$OfRef.tryAdvance(StreamSpliterators.java:1358)
at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.lambda$initPartialTraversalState$0(StreamSpliterators.java:292)
at java.base/java.util.stream.StreamSpliterators$AbstractWrappingSpliterator.fillBuffer(StreamSpliterators.java:206)
at java.base/java.util.stream.StreamSpliterators$AbstractWrappingSpliterator.doAdvance(StreamSpliterators.java:161)
at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.tryAdvance(StreamSpliterators.java:298)
at java.base/java.util.stream.Streams$ConcatSpliterator.tryAdvance(Streams.java:723)
at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.lambda$initPartialTraversalState$0(StreamSpliterators.java:292)
at java.base/java.util.stream.StreamSpliterators$AbstractWrappingSpliterator.fillBuffer(StreamSpliterators.java:206)
at java.base/java.util.stream.StreamSpliterators$AbstractWrappingSpliterator.doAdvance(StreamSpliterators.java:161)
at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.tryAdvance(StreamSpliterators.java:298)
at java.base/java.util.Spliterators$1Adapter.hasNext(Spliterators.java:681)
at java.base/java.util.Spliterators$1Adapter.next(Spliterators.java:687)
at inf.ed.ac.uk.Route.generateRoute(Route.java:35)
I am trying to expand the first node (returns 16 new nodes) add them to the stream, sort it, and repeat, this is part of an implementation of the A* algorithm on gps coordinates
Code for Node is
public class Node {
boolean goal = false;
Node parent;
final LngLat coords;
LngLat.Compass direction;
double cost;
private Route route;
public Node[] expand(LngLat end) {
ArrayList<Node> nodes = new ArrayList<>();
for (LngLat.Compass direction: LngLat.Compass.values()) {
Node node = new Node(coords.nextPosition(direction), this, direction);
if (noFlyClear(node)) {
if (!route.contains(node)) {
if (node.coords.closeTo(end)) {
node.goal = true;
return new Node[]{node};
}
nodes.add(node);
route.visited.add(node);
}
}
}
return nodes.toArray(Node[]::new);
}
But that's since I changed to ArrayLists
I'm still unsure what the issue was originally

Related

How to iterate through a list of nodes which might have sub-lists of nodes (unknown depth levels)

I have a list of nodes, and each node might have a list of subNodes (the number of levels are unknown):
class Node {
int score;
boolean selected;
List<Node> subNodes;
}
Here's how an hypothetical structure might look like:
NODE
+ NODE
+ NODE
+ NODE
+ NODE
+ NODE
+ NODE
+ NODE
+ NODE
+ NODE
+ NODE
+ NODE
Combinations are just countless. I need a way to sum NODE.score for all those nodes that have NODE.selected set to true, possibly using Java 8 features. Any hints would be really appreciated.
Something like:
public int recursiveTotal(final Node node) {
//node not select, don't count the node or any of its subnodes
if (!node.selected) {
return 0;
}
//no subnodes, only node score counts
if (node.subNodes.isEmpty()) {
return node.score;
}
//node has subnodes, recursively count subnode score + parent node score
int totalScore = node.score;
for (final Node subNode : node.subNodes) {
totalScore += recursiveTotal(subNode);
}
return totalScore;
}
Coded using stackoverflow as an IDE, no guarantee against compilation errors ;)
Create a recursive method in your Node class which returns a stream of nodes concatenating a stream of the parent node and the sub nodes:
class Node {
int score;
boolean selected;
List<Node> subNodes;
public Stream<Node> streamNodes() {
return Stream.concat(Stream.of(this), subNodes.stream().flatMap(Node::streamNodes));
}
}
and use it like below to stream over your list:
List<Node> myNodes = //your list
int sum = myNodes.stream()
.flatMap(Node::streamNodes)
.filter(Node::isSelected)
.mapToInt(Node::getScore)
.sum();
TL;DR
Judging by the structure, you've provided each Node in your List is the root of an N-ary Tree data structure (I assume that there are no circles).
And in order to get the required data, we can utilize one of the classic tree-traversal algorithms. In case when the average depth is lower than the average width Depth first search algorithm would be more suitable because it would be more space-efficient, in the opposite situation it would be better to use Breadth first search. I'll go with DFS.
It's easier to come up with a recursive implementation, so I'll start with it. But it has no practical value in Java, hence we would proceed with a couple of improvements.
Streams + recursion
You can create a helper-method responsible for flattening the nodes which would be called from the stream.
List<Node> nodes = // initializing the list
long totalScore = nodes.stream()
.flatMap(node -> flatten(node).stream())
.filter(Node::isSelected)
.mapToLong(Node::getScore)
.sum();
Recursive auxiliary method:
public static List<Node> flatten(Node node) {
if (node.getSubNodes().isEmpty()) {
return List.of(node);
}
List<Node> result = new ArrayList<>();
result.add(node);
node.getSubNodes().forEach(n -> result.addAll(flatten(n)));
return result;
}
No recursion
To avoid StackOverflowError method flatten() can be implemented without recursion by polling and allocating new nodes on the stack (represented by an ArrayDeque) iterativelly.
public static List<Node> flatten(Node node) {
List<Node> result = new ArrayList<>();
Deque<Node> stack = new ArrayDeque<>();
stack.add(node);
while (!stack.isEmpty()) {
Node current = stack.poll();
result.add(current);
current.getSubNodes().forEach(stack::push);
}
return result;
}
No recursion & No intermediate data allocation
Allocating intermediate data in the form of nodes which eventually would not be used is impractical.
Instead, we can make the auxiliary method to be responsible for calculating the total score produced by summarizing the score of each selected node in the tree of nodes.
For that we need to perform isSelected() while traversing the tree.
List<Node> nodes = // initializing the list
long totalScore = nodes.stream()
.mapToLong(node -> getScore(node))
.sum();
public static long getScore(Node node) {
long total = 0;
Deque<Node> stack = new ArrayDeque<>();
stack.push(node);
while (!stack.isEmpty()) {
Node current = stack.poll();
if (current.isSelected()) total += current.getScore();
current.getSubNodes().forEach(stack::push);
}
return total;
}

Data structures get maximum value at each level of N-ary tree

Lets say I have a n-ary tree something like below I need to find maximum value at each level and return like :
[8,7,32] .
8
4 3 7
1 4 3 3 5 6 7 12 32 3 1
My Node will look something like below :
public class Node {
public int val;
public List<Node> children;
public Node() {
}
public Node(int _val,List<Node> _children) {
val=_val;
children=_children;
}
I tried through recursion at each level get the elements and find the maximum but unable to do so.
We can get the level-maximum by a level order traversal / Breadth-first search. The idea is that we have a list/queue of nodes on one level. For all nodes in this list the algorithm does two things:
It calculates the maximum value on this level.
It iterates over all nodes of the list/queue, gets all children of those nodes and put them in a new list/queue, which it can then process in the next iteration.
The algorithm starts with a list/queue holding the root of the (sub)-tree and ends when the list/queue is empty.
This can be expressed nicely with Stream operations:
public static List<Integer> getMaxValuePerLevel(Node node) {
final ArrayList<Integer> maxPerLevel = new ArrayList();
maxPerLevel.add(node.getValue());
List<Node> children = node.getChildren();
while (!children.isEmpty()) {
maxPerLevel.add(children.stream()
.mapToInt(Node::getValue)
.max()
.getAsInt());
children = children.stream()
.map(Node::getChildren)
.flatMap(List::stream)
.collect(Collectors.toList());
}
return maxPerLevel;
}
Ideone demo
This implementation has two nice properties:
It is iterative, not recursive, i.e. the algorithm is not subject to a StackOverflowError
It has linear time- and memory complexity
With a little bit of effort, we are even able to make the algorithm work with generic Node<T extends Comparable<T>>:
public static <T extends Comparable<T>> List<T> getMaxValuePerLevel(Node<T> node) {
final ArrayList<T> maxPerLevel = new ArrayList<>();
maxPerLevel.add(node.getValue());
List<Node<T>> children = node.getChildren();
while (!children.isEmpty()) {
final Node<T> defaultNode = children.get(0);
maxPerLevel.add(children.stream()
.map(Node::getValue)
.max(Comparator.naturalOrder())
.orElseGet(defaultNode::getValue));
children = children.stream()
.map(Node::getChildren)
.flatMap(List::stream)
.collect(Collectors.toList());
}
return maxPerLevel;
}
Ideone demo
The root node is going to be the highest of its level. For the subsequent levels, call Collections.sort() (or any other comparison that will order your list) on the list of children nodes and take the last element (or whichever has the highest value according to the sorting method you used). Then iterate through the list of children nodes that you just sorted and for each node, apply the same treatment to its list of children.
A recursive solution is surprisingly simple. First create a list to hold the result. Then iterate through all the nodes: at each node you compare the node's value with the value in the list at the same level. If the node's value is greater, you replace the value in the list.
class Node {
public int val;
public List<Node> children;
public Node(int _val, List<Node> _children) {
val = _val;
children = _children;
}
public List<Integer> getMaxPerLevel() {
List<Integer> levels = new ArrayList<>();
getMaxPerLevel(0, levels);
return levels;
}
private void getMaxPerLevel(int level, List<Integer> levels) {
if (level >= levels.size()) {
levels.add(level, val);
} else {
levels.set(level, Math.max(val, levels.get(level)));
}
for (Node child : children) {
child.getMaxPerLevel(level + 1, levels);
}
}
}
Thanks everyone I did using below solution:
public List<Integer> levelOrder(Node node){
List<Integer> result = new ArrayList<>();
Queue<Node> queue = new LinkedList<Node>();
queue.add(node);
while(!queue.isEmpty()) {
int size = queue.size();
List<Integer> currentLevel = new ArrayList<Integer>();
for(int i=0;i<size;i++) {
Node current = queue.remove();
currentLevel.add(current.val);
for(Integer inte:currentLevel) {
System.out.println(inte);
}
if(current.children !=null) {
for(Node node1:current.children)
queue.add(node1);
}
}
result.add(Collections.max(currentLevel));
}
return result;
}

How to build undirected graph with adjacency list from a large OSM map in a linear time

This my first time programming web application with maps.
I am trying to create undirected graph with adjacency list for each node from a given OSM map.
Everything works fine when I tested it on small maps.
I unmarshal the OSM map (which is equal to XML file) and then create from the OSM object I received an undirected graph.
The problem starts when I try to create the graph from larger maps.
For example, took map with size of 6MB:
Number of nodes: 24828
Number of ways: 4535
In each way avearage number of 5 nodes.
All these together will be: 24828 * 4535 * 5 = 562,974,900 iterations !
Intuitively, in order to find neighbour for each node I need to go over each node1 in each way in each node2 from list of nodes.
If node1 equal node2 I need to take the next & previous node in the way to be its neighbors.
It took me approximately 1:30 minute to do it:
I am building web application which will run on smart phones and calculates random path to run.
If the user needs to wait 1:30 minute only for the creation of the graph it will be unusable.
I am familiar with BFS \ DFS but they won't help me in this case because I need to build the graph.
Maybe there is other efficient way to create the adjacency list for the nodes ?
How I build adjacency list:
public static List<Node> GetNodesFromXML(Osm i_Osm) {
List<Node> o_Nodes = new ArrayList<Node>();
long id;
double latitude;
double longtitude;
Map<Node, List<Node>> o_AdjList = new HashMap<Node, List<Node>>();
for (Osm.Node nodeChild : i_Osm.getNode()) {
id = nodeChild.getId();
latitude = nodeChild.getLat();
longtitude = nodeChild.getLon();
Node node = new Node(id, latitude, longtitude);
for (Osm.Way way : i_Osm.getWay()) // go over the node list of the specific way objects
{
for (Osm.Way.Nd nd : way.getNd()) {
// some manipulation to create the adjacency list
}
}
//List<Long> nodeAdjacenciesByRef = getNodeAdjacenciesByRef(node, i_Osm.getWay(), i_Osm.getNode());
// List<Edge> nodeAdjacencies = getNodeAdjacencies1(node, i_Osm.getWay(), i_Osm.getNode());
// List<Edge> nodeAdjacencies = getAdjacenciesListFromRefList(node, nodeAdjacenciesByRef, i_Osm.getNode());
// node.SetAdjacencies(nodeAdjacencies);
o_Nodes.add(node);
}
for(Node node : o_Nodes)
{
}
o_Nodes = updateAdjacenciesToAllNodes(o_Nodes);
return o_Nodes;
}
Classes I used for the graph:
// Node.java
public class Node implements Comparable<Node>
{
private long m_Id;
private List<Edge> m_Adjacencies = new ArrayList<Edge>();
private double m_Longtitude;
private double m_Latitude;
private Node m_Prev;
private double m_MinDistance = Double.POSITIVE_INFINITY; // this is for Dijkstra Algorithm
//used to reconstruct the track when we found the approproate length of the user request
//from the current level to the destination
public Node(long i_Id, double i_Latitude, double i_Longtitude)
{
m_Id = i_Id;
m_Latitude = i_Latitude;
m_Longtitude = i_Longtitude;
}
...
}
// Graph.java
private List<Node> m_Nodes = new ArrayList<>();
private List<Way> m_Ways = new ArrayList<>();
private List<Relation> m_Relations = new ArrayList<>();
private Bounds m_Bounds;
public Graph(List<Node> i_Nodes, List<Way> i_Ways, List<Relation> i_Relations, Bounds i_Bounds) {
m_Nodes = i_Nodes;
m_Ways = i_Ways;
m_Relations = i_Relations;
m_Bounds = i_Bounds;
}
...
}
// Edge.java
public class Edge {
Node m_Source;
Node m_Destination;
double m_Weight;
public Edge(Node i_Source, Node i_Destination, double i_Weight) {
m_Source = i_Source;
m_Destination = i_Destination;
m_Weight = i_Weight;
}
...
}
EDIT: Solved:
I used HashMap. This way I will be able to get every node in O(1).
So I run one time on all the nodes (1 second or less) and create this map.
After I have this mapping I can pass on each node in every Way without the external loop.
After this redesign the whole thing took something like 3 seconds.
So here is the solution:
public static List<Node> GetNodesFromXML(Osm i_Osm) {
List<Node> o_Nodes = new ArrayList<Node>();
long id;
double latitude;
double longtitude;
Map<Long, Node> o_NodesByRef = new HashMap<Long, Node>();
for (Osm.Node nodeChild : i_Osm.getNode()) {
id = nodeChild.getId();
latitude = nodeChild.getLat();
longtitude = nodeChild.getLon();
Node node = new Node(id, latitude, longtitude);
//o_Nodes.add(node);
o_NodesByRef.put(id, node);
}
o_Nodes = addAdjacencies(o_NodesByRef, i_Osm.getWay());
//o_Nodes = updateAdjacenciesToAllNodes(o_Nodes);
return o_Nodes;
}
private static List<Node> addAdjacencies(Map<Long, Node> i_NodesByRef , List<Osm.Way> i_Ways) {
List<Node> o_Nodes = new ArrayList<Node>();
long ndId;
int nodeIndex;
int lastNodeIndex;
Node previousNode;
Node nextNode;
double weight;
//System.out.println(i_SourceNode.getNodeId());
for (Osm.Way way : i_Ways) // go over the node list of the specific way objects
{
if (way.getNd().size() > 1) {
for (Osm.Way.Nd nd : way.getNd()) {
if(i_NodesByRef.containsKey(nd.getRef()))// found node in way
{
Node node = i_NodesByRef.get(nd.getRef());
nodeIndex = way.getNd().indexOf(nd);
Edge edge1;
Edge edge2;
Osm.Way.Nd temp_nd;
lastNodeIndex = way.getNd().size() - 1;
if (nodeIndex == 0) // node is the first in the way
{
temp_nd = way.getNd().get(nodeIndex + 1);
nextNode = i_NodesByRef.get(temp_nd.getRef());
weight = CoordinateMath.getDistanceBetweenTwoNodes(node, nextNode);
edge1 = new Edge(node, nextNode, weight);
i_NodesByRef.get(node.getNodeId()).getAdjacencies().add(edge1);
} else if (lastNodeIndex == nodeIndex) // node is the last
{
temp_nd = way.getNd().get(nodeIndex - 1);
previousNode = i_NodesByRef.get(temp_nd.getRef());
weight = CoordinateMath.getDistanceBetweenTwoNodes(node, previousNode);
edge1 = new Edge(node, previousNode, weight);
i_NodesByRef.get(node.getNodeId()).getAdjacencies().add(edge1);
} else // node is in the middle
{
temp_nd = way.getNd().get(nodeIndex - 1);
previousNode = i_NodesByRef.get(temp_nd.getRef());
weight = CoordinateMath.getDistanceBetweenTwoNodes(node, previousNode);
// node -> previousNode
edge1 = new Edge(node, previousNode, weight);
i_NodesByRef.get(node.getNodeId()).getAdjacencies().add(edge1);
temp_nd = way.getNd().get(nodeIndex + 1);
nextNode = i_NodesByRef.get(temp_nd.getRef());
weight = CoordinateMath.getDistanceBetweenTwoNodes(node, nextNode);
// node -> nextNode
edge2 = new Edge(node, nextNode, weight);
i_NodesByRef.get(node.getNodeId()).getAdjacencies().add(edge2);
}
}
}
}
}
for(Map.Entry<Long, Node> entry : i_NodesByRef.entrySet())
{
o_Nodes.add(entry.getValue());
}
return o_Nodes;
}
The problem is that you are trying to store an adjacency list where the elements are entire nodes.
Map<Node, List<Node>> o_AdjList = new HashMap<Node, List<Node>>();
And that is not efficient, because that graph store all of the node information and takes up a lot of memory.
Instead I would make a graph using just node ids, as integer:
Map<Integer, TreeSet<Integer>> o_AdjList = new HashMap<Integer, TreeSet<Integer>>();
And keep the remaining node information in a separate more efficient structure, like a HashSet.
This means you will have a graph representation using only integer ids, which is MANY times smaller object than the first graph. Now the processor can cache more of them and run SSSP faster.
If you really wanted a graph with actual Nodes, you can construct it afterward. This what I would do to build a graph from way:
for (Osm.Way way : i_Osm.getWay()) {
//I will asume the getNd() returns some sort of array or list an you can access the next or previous element
for (Osm.Way.Nd nd : way.getNd()) {
if(o_AdjList contains key nd){
o.AdjList.get(nd).add(nextNd);
} else {
o.AdjList.put(nd,nextNd);
}
}
Of course, you will have to implement some walk method, but after that...you have your node set.

deleting a node from a linked list based on a key

Hello i'm trying to delete a node based on a key. I'm learning dictionary implementation and decided to implement it from scratch to fully understand the concept. i successfully was able to add and return the value using 2 node references a head and tail pointer. But i'm having difficulty using the key to delete a node from the list.
Below is my code to delete from the list
public V remove(K key) {
V result = null;
if(!this.isEmpty()&&head==tail){ //if head and tail are only nodes in the list
if(tail.getKey().equals(key)){
result = tail.getValue();
head=null;
tail = null;
count--; //decrements the count in the list
}
}
else {
boolean found = false;
Node current = head;
Node previous = null;
while(current!=null&&!found){
if(current.getKey().equals(key)){
previous = current;
result = current.getValue();
previous.setNextNode(current.getNextNode());
previous.setNextNode(null);
count--;
found = true;
}
current = current.getNextNode();
}
}
return result;
}
when i enter the desired key to be deleted. It deletes all the keys after the desired key to be deleted.
PS it's not a double linked list. I just created a tail node to access the last node in the list
It seems that you are getting bogged down with updating the list. This code simplifies your algorithm:
// this method returns the head of the new list with the item possibly removed
public Node remove (K key) {
V result = null;
if (this.isEmpty()) {
return null; // return null for empty list
} else if (head.getKey().equals(key)) {
return head.getNextNode(); // return head if head matches
} else {
Node current = head;
Node next = head.getNextNode();
while (next != null) { // walk down the list and search
if (next.getKey().equals(key)) {
current.setNextNode(next.getNextNode());
break;
}
current = next; // advance list pointers
next = next.getNextNode();
}
return head; // will return head for list with 1 element
}
}
You got the general idea, except for 2 things: previous = current should be done outside the if block so that it always gets assigned before moving current forward, and previous.setNextNode(null) should be removed as it undoes the preceding line.
Also, you need to make a special case when the first node in the list matches the key so you can reassign the head.
while(current!=null&&!found){
if(current.getKey().equals(key)){
result = current.getValue();
previous.setNextNode(current.getNextNode());
count--;
found = true;
}
previous = current;
current = current.getNextNode();
}
You have two errors:
You set previous = current;, as the first statement, meaning previous and current are always the same.
You should remove this line:
previous.setNextNode(null); As you do it right after assigning the new next node.

adding and removing from a singly linked list

I thought I had understanding of this in my previous question about linked lists, but I was terribly mistaken, I'm just as lost as I was when I initially posted.
I realize that I'm technically asking two questions, but hopefully getting at least one should make the other easy (assuming they are just reverse of each other).
I have 3 classes already given to me, they are:
SLinkedList.java
package chapter3.linkedList;
public class SLinkedList<V> {
// instance variables. Add the tail reference.
protected Node<V> head, tail;
protected long size;
// methods, empty list constructor first
public SLinkedList () {
head = null;
tail = null;
size = 0;
} // end constructor of a SLinkedList
// method to add nodes to the list. Storage space for the node
// is already allocated in the calling method
public void addFirst (Node<V> node) {
// set the tail only if this is the very first node
if (tail == null)
tail = node;
node.setNext (head); // make next of the new node refer to the head
head = node; // give head a new value
// change our size
size++;
} // end method addFirst
// addAfter - add new node after current node, checking to see if we are at the tail
public void addAfter (Node<V>currentNode, Node<V>newNode) {
if (currentNode == tail)
tail = newNode;
newNode.setNext (currentNode.getNext ());
currentNode.setNext (newNode);
// change our size
size++;
} // end method addAfter
// addLast - add new node after the tail node. Adapted from Code Fragment 3.15, p. 118.
// Mike Qualls
public void addLast (Node<V> node) {
node.setNext (null);
tail.setNext (node);
tail = node;
size++;
} // end method addLast
// methods to remove nodes from the list. (Unfortunately, with a single linked list
// there is no way to remove last. Need a previous reference to do that. (See
// Double Linked Lists and the code below.)
public Node<V> removeFirst () {
if (head == null)
System.err.println("Error: Attempt to remove from an empty list");
// save the one to return
Node<V> temp = head;
// do reference manipulation
head = head.getNext ();
temp.setNext(null);
size--;
return temp;
} // end method removeFirst
// remove the node at the end of the list. tail refers to this node, but
// since the list is single linked, there is no way to refer to the node
// before the tail node. Need to traverse the list.
public Node<V> removeLast () {
// // declare local variables/objects
Node<V> nodeBefore;
Node<V> nodeToRemove;
// make sure we have something to remove
if (size == 0)
System.err.println("Error: Attempt to remove fron an empty list");
// traverse through the list, getting a reference to the node before
// the trailer. Since there is no previous reference.
nodeBefore = getFirst ();
// potential error ?? See an analysis and drawing that indicates the number of iterations
// 9/21/10. size - 2 to account for the head and tail nodes. We want to refer to the one before the
// tail.
for (int count = 0; count < size - 2; count++)
nodeBefore = nodeBefore.getNext ();
// save the last node
nodeToRemove = tail;
// now, do the pointer manipulation
nodeBefore.setNext (null);
tail = nodeBefore;
size--;
return nodeToRemove;
} // end method removeLast
// method remove. Remove a known node from the list. No need to search or return a value. This method
// makes use of a 'before' reference in order to allow list manipulation.
public void remove (Node<V> nodeToRemove) {
// declare local variables/references
Node<V> nodeBefore, currentNode;
// make sure we have something to remove
if (size == 0)
System.err.println("Error: Attempt to remove fron an empty list");
// starting at the beginning check for removal
currentNode = getFirst ();
if (currentNode == nodeToRemove)
removeFirst ();
currentNode = getLast ();
if (currentNode == nodeToRemove)
removeLast ();
// we've already check two nodes, check the rest
if (size - 2 > 0) {
nodeBefore = getFirst ();
currentNode = getFirst ().getNext ();
for (int count = 0; count < size - 2; count++) {
if (currentNode == nodeToRemove) {
// remove current node
nodeBefore.setNext (currentNode.getNext ());
size--;
break;
} // end if node found
// change references
nodeBefore = currentNode;
currentNode = currentNode.getNext ();
} // end loop to process elements
} // end if size - 2 > 0
} // end method remove
// the gets to return the head and/or tail nodes and size of the list
public Node<V> getFirst () { return head; }
public Node<V> getLast () { return tail; }
public long getSize () { return size; }
} // end class SLinkedList
There's also Node.java
package chapter3.linkedList;
public class Node<V>
{
// instance variables
private V element;
private Node<V> next;
// methods, constructor first
public Node ()
{
this (null, null); // call the constructor with two args
} // end no argument constructor
public Node (V element, Node<V> next)
{
this.element = element;
this.next = next;
} // end constructor with arguments
// set/get methods
public V getElement ()
{
return element;
}
public Node<V> getNext ()
{
return next;
}
public void setElement (V element)
{
this.element = element;
}
public void setNext (Node<V> next)
{
this.next = next;
}
} // end class Node
and finally GameEntry.java
package Project_1;
public class GameEntry
{
protected String name; // name of the person earning this score
protected int score; // the score value
/** Constructor to create a game entry */
public GameEntry(String name, int score)
{
this.name = name;
this.score = score;
}
/** Retrieves the name field */
public String getName()
{
return name;
}
/** Retrieves the score field */
public int getScore()
{
return score;
}
/** Returns a string representation of this entry */
public String toString()
{
return name + ", " + score + "\n";
}
}
EDIT POINT
I created a driver called Scores.java, in it so far all I have is **I have added what I THINK i need for the classes, I'm probably wrong though:
package Project_1;
import chapter3.linkedList.*;
import java.util.*;
/** Class for storing high scores in an array in non-decreasing order. */
public class Scores
{
//add function
public SLinkedList<GameEntry> add(GameEntry rank, SLinkedList<GameEntry> scores)
{
Node<GameEntry> currentNode = scores.getFirst();
Node<GameEntry> nextNode = null;
Node<GameEntry> previousNode = null;
Node<GameEntry> newNode = new Node<GameEntry>();
newNode.setElement(rank);
if(scores.getSize() == 0)
{
scores.addFirst(newNode);
}
else
{
while(currentNode != null)
{
nextNode = currentNode.getNext();
if(nextNode == null)
{
scores.addLast(newNode);
}
else
{
scores.addAfter(currentNode, newNode);
break;
}
previousNode = currentNode;
currentNode = currentNode.getNext();
}
}
return scores;
}
//remove function
public void remove(int i)
{
}
//print function
/*gameenter printing;
printing=node.Getelement; //pseudo code for making it work right
print(printing.getscore)
print(print.getname)
*/
public void print(SLinkedList<GameEntry> scores)
{
Node<GameEntry> currentNode = scores.getFirst();
GameEntry currentEntry = currentNode.getElement();
System.out.printf("[");
for(int i = 0; i < scores.getSize(); i++)
{
System.out.printf(", %s", currentEntry.toString());
currentNode = currentNode.getNext();
currentEntry = currentNode.getElement();
}
System.out.println("]");
}
}
I have my test driver called ScoresTest.java, that I have pretty much filled out:
package Project_1;
import chapter3.linkedList.SLinkedList;
public class ScoresTest {
/**
* #param args
*/
public static void main(String[] args)
{
SLinkedList<GameEntry> highScores = new SLinkedList<GameEntry>(); //Linked List for Game Entry
GameEntry entry;
Scores rank = new Scores();
entry = new GameEntry("Flanders", 681);
highScores = rank.add(entry, highScores);
entry = new GameEntry("Krusty", 324);
highScores = rank.add(entry, highScores);
entry = new GameEntry("Otto", 438);
highScores = rank.add(entry, highScores);
entry = new GameEntry("Bart", 875);
highScores = rank.add(entry, highScores);
entry = new GameEntry("Homer", 12);
highScores = rank.add(entry, highScores);
entry = new GameEntry("Lisa", 506);
highScores = rank.add(entry, highScores);
entry = new GameEntry("Maggie", 980);
highScores = rank.add(entry, highScores);
entry = new GameEntry("Apoo", 648);
highScores = rank.add(entry, highScores);
entry = new GameEntry("Smithers", 150);
highScores = rank.add(entry, highScores);
entry = new GameEntry("Burns", 152);
highScores = rank.add(entry, highScores);
System.out.println("The Original High Scores");
rank.print(highScores);
entry = new GameEntry("Moe", 895);
highScores = rank.add(entry, highScores);
System.out.println("Scores after adding Moe");
rank.print(highScores);
//highScores = rank.remove(4);
System.out.println("Scores after removing Apoo");
rank.print(highScores);
}
}
That's entirely finished, pretty sure I have nothing left to add for it.
I'm not looking for someone to answer it for me, but I have no clue where to start or how to make the add or remove function, in any way. This is an intermediate course, the book does nothing for explaining linked lists (go ahead and look for yourself if you don't believe me, text is called Datastructures and Algorithms in Java, 5th edition). It shows how to do such with an array quite easily...which works perfectly for a linked list, but apparently the teacher does not want us doing it this way, so sadly I am now utterly lost on how to do this.
I've tried looking at other peoples answers on here, and google, and so far nothing has clicked or made any sense at all, I just can't grasp how it works, and the teacher's explanation and example was only to draw boxes on the board, I've never seen a sort, add, or remove function coded for a linked list...can't know what I've not been taught or can't locate.
Any help is greatly appreciated, and thank you in advance!
EDIT
I looked at the import java.util.*; and the commands within it for linked lists, they seem painfully easy. to remove I'd just use list.sublist(i, i).clear(); and the value I wish to remove is removed, super easy, it seems to be just trying to utilize the slinkedlist.java and node.java, I just can't seem to follow them in any way shape or form. I believe the teacher did indeed write them, and I've tried asking for his assistance, stayed 2 hours after class trying to get any understanding from him, and as you can see it did not help much at all. Thank you again for the assistance!
EDIT
I also apologize if this seems like it is vague, but I don't have a specific point where my confusion seems linked, I understand linked lists if we're talking about the java.util.linkedList;, but as far as using what I've been given in this circumstance, I can't follow the logic at all, leaving me quite lost and unsure of where to begin.
In pseudo-code (please note I am not including bound checking etc, simply the logic)
To add a node to the front of the list:
newNode->nextNode = startNode
startNode = newNode
To add to a specific index:
index = 0
currentNode = startNode
// find the node in the list. here you will need to do all kinds of bound checking
while index is less than position
currentNode = currentNode.nextNode // move your node pointer to the position
increment index
// so here we basically insert the new node into the list. what needs to happen is
// to NOT break the list by forgetting the node after the current node. this is why
// we first set the new nodes' next one, to the current nodes' (the one already in
// the list) next node. this way, we still have all the information we need. then,
// when we set the current nodes' next node to the new node, we essentially "break"
// the link and "repair" it by adding the new link.
newNode.nextNode = currentNode.nextNode // some more bound checking required
currentNode.nextNode = newNode
To remove from a specific index:
index = 0
delNode = startNode
// find the node in the list. here you will need to do all kinds of bound checking
while index is less than (position - 1)
delNode = delNode.nextNode // move your node pointer to the position
increment index
delNode.nextNode = delNode.nextNode.nextNode
// that's it. by setting the node's (before the one you whish to delete)
// next node to the node AFTER the one you want to delete, you basically
// "skip" over that node. since it is no longer referenced, the garbage
// collector will take care of the rest. if you wish to return that node
// you can do it quite easily by remembering it.
storeNode = delNode.nextNode // some more bound checking required
delNode.nextNode = delNode.nextNode.nextNode // some more bound checking required
// now you still have a reference to the deleted node in storeNode
UPDATE
OK, so if I understand correctly, you need to create a linked list that stores scores in a ascending order. As far as I can see, the entire linked list has been implemented for you, you simply need to use the classes provided, and add the logic in Scores.java to keep the list sorted.
First off, I see your nodes are not comparable. If you are at all allowed to change the source given to you, I would suggest having them implement Comparable<Node> and also override the equals(Object o) so that you have logic to compare them. Two nodes can contain the same element, but that does not mean that they are equal.
Please note the change in the method signatures!
//add function
public void add(Node<GameEntry> score) {
// adding is where you now want to keep everything sorted. so I highly
// recommend that you implement `Comparable` as I mentioned above. if not,
// you have to put the logic in here.
Node<GameEntry> currentNode = highScored.getFirst();
Node<GameEntry> prevNode = null;
// if the list is empty, or the new node must go in before the head,
// simply add it as the head.
if (highScores.size() == 0 || score.compareTo(currentNode) < 0) {
highScores.addFirst(score);
}
// search for the position of the new node. while the node has a higher score
// than the current node, we need to continue on so we can place it in the
// correct place.
while (currentNode != null && currentNode.compareTo(score) > 0) {
prevNode = currentNode;
currentNode = currentNode.getNext();
}
// if the currentNode is null, it means it is the highest score, so
// we can simply add it to the end
if (currentNode == null) {
highScores.addLast(score);
} else {
// otherwise just add it after the correct node
highScores.addAfter(prevNode, score);
}
}
//remove function
public void remove(Node<GameEntry> score) {
// removing an element should be as described above. if you keep
// your list sorted during the ADD method, removing any element
// should not break the order.
// find the element - removal from a linked list is O(n),
// since we need to know what the element BEFORE the one
// is that you want to remove. assuming you have implemented
// the equals method to check equality of nodes:
Node<GameEntry> currentNode = highScores.getFirst();
Node<GameEntry> prevNode = null;
while (currentNode != null && !currentNode.equals(score)) {
prevNode = currentNode;
currentNode = currentNode.getNext();
}
// if currentNode is null, the node we wanted to remove was not
// in the list.
if (currentNode == null) {
System.out.println("Node not found");
return;
}
// now, we need to check if there is a node after the one we want
// to remove.
if (prevNode.getNext().getNext() != null) {
// if there is, we follow the logic from the pseudo code
prevNode.setNext(prev.getNext().getNext());
} else {
// if not, we only need to remove the last entry (since the
// one we want to remove is the last one)
highScores.removeLast();
}
}
IMPORTANT
Please just double check the logic here. I did it really quickly without an IDE as I'm not at my development computer at the moment. If anyone finds any issues, please leave a comment and I'll fix it.
If this is not exactly what you asked (your question is a bit vague), let me know.
UPDATE 2
Read up on Comparators here, here and here.

Categories

Resources