Shortest path using Dijkstra's algorithm - java

I'm currently reviving an old homework assignment, where I'm writing a program that among other functions, involves finding the shortest path in a graph using Dijkstra's algorithm.
I think I've got it right for the most part, but I keep getting NullPointerException at line 58 when executing if(currentNode.getAktuell()).
I've been trying several solutions back and forth but can't seem to figure out what is wrong but prioQueue.poll(); returns null when the queue is empty. I've tried to handle that last currentNode that eventually turns into null but have not been able to find a working solution, so I'm starting to think that I've missed out on something here.
I would really appreciate it if someone familiar with dijkstras algorithm could help me out here. There's probably a better solution to the algorithm but I only want help with finding out what is wrong with the one I've written, and not "the answer" using someone else's algorithm.
public static List<String> shortestPath(Graph<String> graph, String från, String till){
//if(!pathExists(graph, från, till))
//return null;
PriorityQueue<DjikstraObjekt<String>> prioQueue = new PriorityQueue<DjikstraObjekt<String>>();
LinkedHashMap<String, DjikstraObjekt<String>> samling = new LinkedHashMap<String, DjikstraObjekt<String>>();
for(String bla : graph.getNodes())
samling.put(bla, new DjikstraObjekt<String>(bla, Integer.MAX_VALUE, null, false));
samling.get(från).updateVikt(0);
prioQueue.add(samling.get(från));
while(!samling.get(till).getAktuell())
{
DjikstraObjekt<String> currentNode = prioQueue.poll();
if(currentNode==null)
break;
if(currentNode.getAktuell())
continue;
currentNode.aktuellNod();
for(ListEdge<String> edge : graph.getEdgesFrom(currentNode.getNode()))
{
System.out.println("get edges from");
int nyVikt = edge.getVikt() + currentNode.getVikt();
DjikstraObjekt<String> toNode = samling.get(edge.getDest());
if(!toNode.getAktuell() && nyVikt < toNode.getVikt()) {
toNode.updateVikt(nyVikt);
toNode.setFrån(currentNode.getNode());
prioQueue.add(toNode);
}
}
}
List<String> djikstaList = new ArrayList<String>();
for(int i=0;i<samling.size();i++){
if(samling.get(i).getNode()!=från){
System.out.println(samling.get(i).getNode());
djikstaList.add(samling.get(i).getNode());
}
}
return djikstaList;
}
public class DjikstraObjekt<E> implements Comparable<DjikstraObjekt<E>> {
private E nod;
private int vikt;
private E frånNod;
private boolean aktuellNod=false;
public DjikstraObjekt(E nod, int vikt, E frånNod, boolean aktuellNod){
this.nod=nod;
this.vikt=vikt;
this.frånNod=frånNod;
this.aktuellNod=aktuellNod;
}
public E getNode() {
return nod;
}
public void updateVikt(int nyvikt){
vikt=nyvikt;
}
public int getVikt() {
return vikt;
}
public boolean getAktuell() {
return aktuellNod;
}
public void aktuellNod(){
aktuellNod=true;
}
public void setFrån(E från)
{
frånNod = från;
}
public int compareTo(DjikstraObjekt<E> other) {
return getVikt() - other.getVikt();
}
}
Heres my listEdge class:
public class ListEdge<E> {
private E dest;
private String namn;
private Integer vikt;
public ListEdge(E dest, String namn, Integer vikt){
this.dest=dest;
this.namn=namn;
this.vikt=vikt;
}
public E getDest(){
return dest;
}
public void ändraVikt(Integer nyVikt){
if(vikt<0)
throw new IllegalArgumentException();
vikt=nyVikt;
}
public String getNamn(){
return namn;
}
public int compareTo(ListEdge other) {
return this.vikt.compareTo(other.getVikt());
}
public int getVikt(){
return vikt;
}
public String toString(){
return "till " + dest + " med " + namn +" "+ vikt;
}
}
These should be the relevent methods from my ListGraph class:
public List<E> getNodes(){
List<E> temp = new ArrayList<E>();
for(E test : noder.keySet()){
temp.add(test);
}
return temp;
}
public List<ListEdge<E>> getEdgesFrom(E nod) {
List<ListEdge<E>> temp = new ArrayList<ListEdge<E>>();
if(noder.containsKey(nod)){
try{
for(Map.Entry<E, List<ListEdge<E>>> test : noder.entrySet()){
if(test.getKey().equals(nod)){
System.out.println(nod+" "+test.getKey());
for(ListEdge<E> e: test.getValue()){
temp.add(e);
}
}
}
}
catch(NoSuchElementException E){
}
}
return temp;
}

I couldn't reconstruct the NullPointerException you told us about. As Leandro pointed out, the problem might lay with your implementation of ListEdge and Graph.
I did an implementation of both classes myself to test your code.
The only problem I could find was in the end where you create the result list:
for(int i=0;i<samling.size();i++){
if(samling.get(i).getNode()!=från){
This will always Result in a NullPointerException because get() expects a key and in your case that's a String, not an int. To iterate over the Map use something like
List<String> djikstaList = new ArrayList<String>();
for(String key : samling.keySet()){
if(samling.get(key).getNode()!=från){
System.out.println(samling.get(key).getNode());
djikstaList.add(samling.get(key).getNode());
}
}
Furthermore, i assume you wan't to return the actual path from from to to so you would need to add a getter getFrån() to DijkstraObjekt and then build up the list like this:
String fromNode = samling.get(to).getNode();
djikstaList.add(to);
while(fromNode != from){
fromNode = samling.get(fromNode).getFrån();
djikstaList.add(fromNode);
}
After this the List will contain the complete path (including Start and End node) in reverse order.
If wanted, I can post all of my classes I used for testing/debugging.
Cheers
tannerli

I think this might be a problem:
//...
samling.put(bla, new DjikstraObjekt<String>(bla, Integer.MAX_VALUE, null, false));
samling.get(från).updateVikt(0);
EDIT:
Sorry I thought the {} is there. Everything is ok there. I will keep looking.

Maybe try this:
if(currentNode==null || currentNode.getAktuell() == null)
break;
if(currentNode.getAktuell())
continue;

Related

Search for an object in arrayList Java

package generics;
import java.util.ArrayList;
import java.util.List;
public class Generics {
private static List <Box> newlist = new ArrayList<>();
public static void main(String[] args) {
newlist.add(new Box("charlie",30));
newlist.add(new Box("max",29));
newlist.add(new Box("john",22));
// Testing method find -- Start
find ("max",29);
//Testing method find2 -- Start
Box <String,Integer> search = new Box("max",29);
find2(search);
}
public static void find (String parameter, Integer parameter1){
for (Box e : newlist){
if(e.getName() != null && e.getMoney() !=null
&& e.getName().equals(parameter)
&& e.getMoney().equals(parameter1)){
System.out.println("found on position " + newlist.indexOf(e));
break;
}
}
}
public static void find2 (Box e){
for (Box a : newlist){
if (a.equals(e)){
System.out.println("Found");
}else {
System.out.println("Not found");
}
}
}
}
public class Box<T , D>{
private T name;
private D money;
public Box(T name, D money) {
this.name = name;
this.money = money;
}
public T getName() {
return name;
}
public D getMoney() {
return money;
}
#Override
public String toString() {
return name + " " + money;
}
}
Can someone show me how to search for an object in ArrayList.
Method find() it works perfect but in my opinion is wrong and
the reason why I am thinking like that, because I am passing as parameter a string and an integer but should be an box object or maybe I wrong?
In my second method find2() I am trying to pass as parameter an object of Box and when I am trying to search for it I got a false result =(
I am noobie I am trying to understand and to learn.
Stop using raw types!
Box is generic, so if you are not targeting older Java versions, always add generic parameters!.
The declaration of find2 should be like this:
public static void find2 (Box<String, Integer> e)
And you should check whether two boxes are equal in exactly the way you did in find. equals will not work because you did not define an equals method in Box. So:
for (Box<String, Integer> a : newlist){
if (a.getName().equals(e.getName()) &&
a.getMoney().equals(e.getMoney())){
System.out.println("Found");
}else {
System.out.println("Not found");
}
}
You should override Object.equals() on the Box class.
Try to handle null correctly too. Because 2 Box with null names and/or null money are in fact equal.
(you DON'T need to override Object.hashCode() for this, but it's a good practice to do so, just in case it is used in a hashmap or hashset or such).
The easiest way to search and find something in an arraylist is to use the .equals method combined with a for loop to iterate through your lists.
for(int i = 0; i < newList; ++i)
{
if(newlist.equals(Stringname))
{
//it matches so do something in here
}
}
what it is doing here is moving through the list 1 by 1 until it finds something that matches what you entered -> stringName

(Java) How to return objects from a list as a string or array of strings?

EDIT: I have uploaded both full class files and a tester file to dropbox in case you want to try it out yourselves: https://www.dropbox.com/sh/03qsecq3bd55w78/AAA_PT_BczNGl7cTZO_Zf_qUa?dl=0
First off, sorry if this is a very simple question - I've only just started working with java and I don't have a lot of programming experience at all.
So I have two classes: Posada and Camino. Posada contains a list of Camino objects. I need to override Posada's toString method so that it returns a string containing all of the toString methods of the Camino objects its list contains, like so:
Posada:
public class Posada {
/*other attributes*/
private List<Camino> listCaminos;
/*Getters*/
public List<Camino> getLisaCaminos() {
return listCaminos;
}
/*Builder*/
public Posada(String nombre, int engRecuperada) {
/*Other attributes*/
this.listaCaminos = new ArrayList<Camino>();
}
/*Methods*/
public String getStringToReturn(){
String[] arrayCaminos = listCaminos.toArray(new
String[listCaminos.size()]);
String string = "";
string += /*Some other attributes that go before the Camino objects
and work fine*/;
for(int i = 0; i < listCaminos.size(); i++) {
string += ", " + arrayCaminos[i];
}
return string;
}
/*toString*/
#Override
public String toString() {
return getStringToReturn(); //failure
}
}
Camino
public class Camino {
/*Attributes*/
private Posada origin;
private Posada destination;
private int cost;
private static int costTotal;
/*Contructor*/
public Camino(Posada origin, Posada destination, int cost){
this.origin = origin;
this.destination = destination;
this.cost = cost;
Camino.costTotal = Camino.costTotal + cost;
}
/*Getters*/
public int getCost() {
return cost;
}
public Posada getDestination() {
return destination;
}
public Posada getOrigin() {
return origin;
}
/*Methods. Irrelevant*/
/*toString*/
#Override
public String toString() {
return "(" + getOrigin() + "--" + getCost() + "-->" +
getDestination()+ ")";
}
}
So the expected result when printing Posada should be something like "Other_attributes, (originCamino1--costCamino1-->destinationCamino1), (originCamino2--costCamino2-->destinationCamino2), (..), (originCaminoN--costCaminoN-->destinationCaminoN)", N being the total number of Camino objects in the list. However, I keep getting the following error:
Exception in thread "main" java.lang.ArrayStoreException
at java.lang.System.arraycopy(Native Method)
at java.util.ArrayList.toArray(Unknown Source)
at apartado1.Posada.getStringToReturn(Posada.java:106)
at apartado1.Posada.toString(Posada.java:121)
at java.lang.String.valueOf(Unknown Source)
at java.lang.StringBuilder.append(Unknown Source)
I know it's got to be something really easy to fix, but I've been looking around and it just won't work no matter what I do. I've tried using toString() and toArray(), but I get the same results. Here's hoping you can help me. Thanks in advance!
Why does the default implementation of ArrayList not work ? This this:
#Override
public String toString() {
return listCaminos.toString();
}
listCaminos.toArray() must return an array of Camino. You can't pass in String[].
Change the first line of Camino.getStringToReturn() to
Camino[] arrayCaminos = listCaminos.toArray(new Camino[listCaminos.size()]);
Should work fine now.
EDIT: Looking at your code again, I don't understand why you're using an array in the first place. Why not iterate over the list elements?
A simpler way to do this with Java 8:
#Override
public String toString() {
return "Other_attributes, " +
listCaminos.stream()
.map(Camino::toString)
.collect(Collectors.joining(", "));
}
There is no need to convert the list to an array before iterating it, and with Java 8 streams there is no need to even iterate the list to create a comma-delimited string of the Camino objects.
You could use either what Matthias suggested, or if you want a customized output, you could loop through each of the elements and concatenate their toString outputs.
Something like this:
public String toString(){
StringBuilder builder = new StringBuilder();
for (Camino camino : listCaminos) {
builder.append(camino.toString()).append(",");
}
return builder.toString();
}
You can strip off the extra "," at the end.
I'd rather use a foreach loop inside toString, like:
public String toString()
{
String ret = "";
for (Camino camino : listCaminos)
{
ret += camino.toString() + "\n";
}
return ret;
}
Instead of using \n (as new line), you can use comma as your example.
Evenmore, it could be improved using StringBuilder if performance improvements are necessary.

Java Queues and Stacks

I have a small problem that I'm hoping someone could help me with.
This is an assignment, so I am not supposed to use classes imported from the java API nor am I supposed to do this in any other way (arraylist would have made this much easier.)
I created a Queue class and a Stack class.
I am trying to retrieve the head of the Queue, and add it to the Stack.
I am guessing I will need to create a method that somehow gets the value of the head of the list and stores it so I can use it.
For example, if I enqueued " bob", "jack", and "jill" to the Queue in that order, it will look like:
bob
jack
jill
I want to dequeue bob from the queue list, but add him to the head of the Stack list, but I can't figure out how. I'm sorry if my question is not very precise, I'm having problems wording what I really need. If any other information is needed I will update my post. Thanks for any help.
Here is my Queueclass:
(LL is my Link List class)
public class Queue<T extends Comparable<T>> {
LL<T> theQueue;
public Queue() {
theQueue = new LL<T>();
}
public boolean isEmpty() {
return theQueue.isEmpty();
}
public void enqueue(T value) {
theQueue.insertTail(value);
}
public T dequeue() throws QueueException {
T retval = null;
try {
retval=theQueue.deleteHead();
}catch (LLException e) {
throw new QueueException ("Queue is empty");
}
return retval;}
public String toString() {
return theQueue.toString();
}}
And my Stack Class:
public class Stack<T extends Comparable<T>>{
LL<T> theStack;
public Stack()
{
theStack = new LL<T>();
}
public boolean isEmpty()
{
return theStack.isEmpty();
}
public void push(T value)
{
theStack.insertHead(value);
}
public T pop() throws StackException
{
T retval = null;
try
{
retval = theStack.deleteHead();
}
catch (LLException e)
{
throw new StackException("Stack Underflow");
}
return retval;
}
public boolean isFull()
{
return false;
}
public String toStrin()
{
return theStack.toString();
}
Main Class:
public static void main(String[] args) {
Stack <String> hired = new Stack<String>();
Stack <String> fired = new Stack<String>();
Queue <String> apps = new Queue<String>();
String temp;
for (int i = 0; i < 1000; i++) {
System.out.println("Enter the number of the action to perform:");
System.out.println("1. Accept Application");
System.out.println("2. Hire");
System.out.println("3. Fire");
System.out.println("4. Exit");
Scanner kb = new Scanner(System.in);
int key = kb.nextInt();
switch (key) {
case 1:
System.out.println("Enter applicant's name and ID separated by semi-colon:");
String applicant = kb.next() + "\n";
System.out.println("You entered " + applicant);
apps.enqueue(applicant);
break;
case 2:
try{
temp = apps.dequeue();
} catch (QueueException s) {
}
try{ apps.dequeue(); }
catch (QueueException s){
System.out.println("Queue is empty");}
hired.push(temp);
case 3:
System.out.println();
case 4: System.out.println("Bye");
System.exit(0);
}
}
So it won't let me assign apps.dequeue() to temp without the try and catch. but then when I do hired.push(temp); I get an error saying temp may have not been initialized.
I think what you want to do is "To dequeue "bob" from the Queue and add it to the Stack", isn't it?
So I think you have already tell what to do:
Queue<String> q = new Queue<String>();
Stack<String> s = new Stack<String>();
// ... enqueue three strings
String temp = q.dequeue();
s.push(temp);
Yes - this task has nothing to do with the implementation of your Queue and Stack class. It's only about using the interface. As long as you have implemented them correctly, these code work.
EDIT
So maybe this is what you want:
String temp = ""; // explicitly initialize
try {
temp = q.dequeue();
s.push(temp);
} catch {
}
I put both dequeue and push in try block: if dequeue fails, nothing is to be pushed. Is this right for you ?
Use iterator (if you need to push value from a random position of queue into stack). For your assignment, simply dequeue method should work fine as pointed in another answer.
Before calling the dequeue method, call this iterator and check if hasNext(). if true, get the value using iterator.next() and store it.
Now you have the value in 'head' position. Now call dequeue method and delete the head value. Now simply push your stored value into stack

how to make more than condition in toString method

I want to list all names that end with "Reda" and ignore case sensitivity, I have tried the condition in the toString method at the bottom, but it would not print any thing.
public class Customer {
public static void main(String[] args) throws IOException {
File a = new File("customer.txt");
FileWriter v = new FileWriter(a);
BufferedWriter b = new BufferedWriter(v);
PrintWriter p = new PrintWriter(b);
human Iman = new human("Iman", 5000);
human Nour = new human("Nour", 3500);
human Redah = new human("Redah", 0);
human iman = new human("iman", 200);
human MohamedREDA = new human("MohamedREDA", 3000);
human Mohamed_Redah = new human("Mohamed Redah", 2000);
human[] h = new human[6];
h[0] = Iman;
h[1] = Nour;
h[2] = Redah;
h[3] = iman;
h[4] = MohamedREDA;
h[5] = Mohamed_Redah;
p.println(Iman);
p.println(Nour);
p.println(Redah);
p.println(iman);
p.println(MohamedREDA);
p.println(Mohamed_Redah);
p.flush();
}
}
class human {
public String name;
public double balance;
public human(String n, double b) {
this.balance = b;
this.name = n;
}
#Override
public String toString() {
if (name.equalsIgnoreCase("Reda") && (name.equalsIgnoreCase("Reda"))) {
return name + " " + balance;
} else
return " ";
}
}
Please avoid putting condition in toString method. Remove the condition there
public String toString() {
return name + " " + balance;
}
and change your logic in Customer class
human[] h = new human[6];
h[0] = Iman;
h[1] = Nour;
h[2] = Redah;
h[3] = iman;
h[4] = MohamedREDA;
h[5] = Mohamed_Redah;
for (int i = 0; i < h.length; i++) {
if (h[i].name.toLowerCase().endsWith("reda")) { // condition here
p.println(h[i]);
}
}
And make use of loops do not duplicate the lines of code.Every where you are manually writing the lines.
Check Java String class and use required methods to add condition.
String redahname = ("Redah").toLowerCase(); //put your h[0] instead of ("Redah")
if(name.endsWith("redah")){ //IMPORTANT TO BE IN LOWER CASE, (it is case insenitive this way)
//your code here if it ends with redag
System.out.println(redahname);
} //if it does not end with "redah" it wont out print it!
You can use this, but can you please explain your question more? What exactly do you need?
try this
#Override
public String toString() {
if (name.toLowerCase().endsWith("reda"))) {
return name + " " + balance;
} else
return " ";
}
String.equals() is not what you want as you're looking for strings which ends with "Reda" instead of those equal to "Reda". Using String.match or String.endsWith together with String.toLowerCase will do this for you. The following is the example of String.match:
public class Reda {
public static void main(String[] args) {
String[] names = {"Iman", "MohamedREDA", "Mohamed Redah", "reda"};
for (String name : names) {
// the input to matches is a regular expression.
// . stands for any character, * stands for may repeating any times
// [Rr] stands for either R or r.
if (name.matches(".*[Rr][Ee][Dd][Aa]")) {
System.out.println(name);
}
}
}
}
and its output:
MohamedREDA
reda
and here is the solution using endsWith and toLowerCase:
public class Reda {
public static void main(String[] args) {
String[] names = {"Iman", "MohamedREDA", "Mohamed Redah", "reda"};
for (String name : names) {
if (name.toLowerCase().endsWith("reda")) {
System.out.println(name);
}
}
}
}
and its output:
MohamedREDA
reda
You shouldn't put such condition in toString() method cause, it's not properly put business application logic in this method.
toString() is the string representation of an object.
What you can do, is putting the condition before calling the toString() , or making a helper method for this.
private boolean endsWithIgnoringCase(String other){
return this.name.toLowerCase().endsWith(other.toLowerCase());
}
None of your humans are called, ignoring case, Reda, so your observation of no names printed is the manifestation of properly working logic.
Your condition is redundant: you perform the same test twice:
name.equalsIgnoreCase("Reda") && (name.equalsIgnoreCase("Reda"))
If you need to match only the string ending, you should employ a regular expression:
name.matches("(?i).*reda")
toString is a general-purpose method defined for all objects. Using it the way you do, baking in the business logic for just one special use case, cannot be correct. You must rewrite the code so that toString uniformly returns a string representation of the object.

Recursive function for getters/setters?

I am looking for an algorithm in Java that creates an object thats attributes are set to the first not-null value of a string of objects. Consider this array (I will use JSON syntax to represent the array for the sake of simplicity):
{
"objects": [
{
"id": 1,
"val1": null,
"val2": null,
"val3": 2.0
},
{
"id": 2,
"val1": null,
"val2": 3.8,
"val3": 6.0
},
{
"id": 3,
"val1": 1.98,
"val2": 1.8,
"val3": 9.0
}
]
}
In the end, I want one object that looks like this:
{
"id": 1,
"val1": 1.98,
"val2": 3.8,
"val3": 2.0
}
Where val1 comes from the third object, val2 from the secound and val3 and id from the first, because these are the first objects found where the attribute isn't null.
What I have done so far in Java and what works really fine is this:
// Java code that proceeds a deserialized representation of the
// above mentioned array
int k = 0;
while (bs.getVal1() == null) {
k++;
bs.setVal1(objectArray.get(k).getVal1());
}
However, I am not satisfied, because I would have to write this code four times (getId, getVal1, getVal2, getVal3). I am sure there must be a rather generic approach. Any Java guru who could give a Java beginner an advice?
Before getting to your actual question, here's a better way of writing your existing loop: (replace Object with whatever the actual type is)
for (Object o : objectArray) {
Double d = o.getVal1();
if (d != null) {
bs.setVal1(d);
break;
}
}
Considering the way your objects are laid out now, there isn't a better way to do it. But you can do better if you change the structure of your objects.
One way is to put your different value fields (val1, val2, ...) into an array:
Double val[] = new Double[3]; // for 3 val fields
public Double getVal(int index) {
return val[index];
}
public void setVal(int index, Double value) {
val[index] = value;
}
Then you can simply access the fields by their index and set all fields of bs in one iteration of the object array:
for (Object o : objectArray) {
for (int i = 0; i < 3; i++) {
Double d = o.getVal(i);
if (d != null) {
bs.setVal(i, d);
break;
}
}
}
+1 - Code repetition is a problem that can sometimes be hard to overcome in Java.
One solution is to create an Iterable class which allows you to iterate over the values in one of those objects as if they were in an array. This takes away some of the repetition from your code without sacraficing the legibility benefits of named variables.
Note that In my code below, I've created a separate iterable class, but you could also simply make the POD class iterable (which one of these is the best option for you depends on details you didn't cover with your example code):
(warning - not tested yet)
import java.util.Iterator;
public class Main {
static class POD{
Integer id; Double val1; Double val2; Double val3;
public POD(Integer i, Double v1, Double v2, Double v3){
id=i; val1=v1; val2=v2; val3=v3;
}
public POD(){ }
}
static class PODFields implements Iterable<Number>{
private POD pod;
public PODFields(POD pod){
this.pod=pod;
}
public PODFieldsIterator iterator() {
return new PODFieldsIterator(pod);
}
}
static class PODFieldsIterator implements Iterator<Number>{
int cur=0;
POD pod;
public PODFieldsIterator(POD pod) {
this.pod=pod;
}
public boolean hasNext() { return cur<4; }
public Number next() {
switch(cur++){
case 0:
return pod.id;
case 1:
return pod.val1;
case 2:
return pod.val2;
case 3:
return pod.val3;
}
return null;//(there are better ways to handle this case, but whatever)
}
public void remove() { throw new UnsupportedOperationException("You cannot remove a POD field."); }
}
public static void main(String[] args) {
POD [] objectArray = {new POD(1,null,null,2.0),
new POD(1,null,null,2.0),
new POD(1,null,null,2.0),
new POD(1,null,null,2.0)};
POD finalObject=new POD();
for (POD cur : objectArray){
PODFieldsIterator curFields = new PODFields(cur).iterator();
for (Number finalValue : new PODFields(finalObject)){
Number curValue = curFields.next();
if (finalValue==null)
finalValue=curValue;
}
}
for (Number finalValue : new PODFields(finalObject))
System.out.println(finalValue);
}
}
Edit: Oops - looks like I forgot Numbers are immutable. I suppose you could overcome this by having the iterator return functors or something, but that's possibly going a bit overboard.
Whenever you want to eliminate code duplication, one of the first things you look for is whether you can extract a reusable method. Reflection helps you call arbitrary methods in a reusable way. Its not the prettiest thing in the world, but this method works for you:
#SuppressWarnings("unchecked")
public <T> T firstNonNull(String methodName, TestObject... objs) {
try {
Method m = TestObject.class.getMethod(methodName, (Class[])null);
for (TestObject testObj : objs) {
T retVal = (T)m.invoke(testObj, (Object[])null);
if (retVal != null) return retVal;
}
return null;
} catch (Exception e) {
//log, at a minimum
return null;
}
}
Testing with a class like this:
public class TestObject {
Integer id;
String val1;
Map<String, Boolean> val2;
public int getId() {
return id;
}
public String getVal1() {
return val1;
}
public Map<String, Boolean> getVal2() {
return val2;
}
}
This JUnit test demonstrates its usage:
#org.junit.Test
public void testFirstNonNull() {
TestObject t1 = new TestObject();
t1.id = 1;
t1.val1 = "Hello";
t1.val2 = null;
TestObject t2 = new TestObject();
Map<String, Boolean> map = new HashMap<String, Boolean>();
t2.id = null;
t2.val1 = "World";
t2.val2 = map;
TestObject result = new TestObject();
result.id = firstNonNull("getId", t1, t2);
result.val1 = firstNonNull("getVal1", t1, t2);
result.val2 = firstNonNull("getVal2", t1, t2);
Assert.assertEquals(result.id, (Integer)1);
Assert.assertEquals(result.val1, "Hello");
Assert.assertSame(result.val2, map);
}

Categories