I have a series of small arrays (consisting of two doubles), many of which are
the same. E.g.
{5.0, 15.0}
{5.0, 15.0}
{5.0, 15.0}
{12.0, 8.0}
{10.0, 8.0}
{10.0, 8.0}
I want to be able to count the number of each arrays, i.e.
3 of {5.0, 15.0}
1 of {12.0, 8.0}
2 of {10.0, 8.0}
To do this, I tried making use of a LinkedHashMap (linked, because the order
might come into use later on):
import java.util.Map;
import java.util.LinkedHashMap;
public class MapArrayInt {
Map<double[], Integer> arrays = new LinkedHashMap<double[], Integer>();
public static void main(String[] args) {
MapArrayInt mapArrayInt = new MapArrayInt();
mapArrayInt.addArray(5.0, 15.0);
mapArrayInt.addArray(5.0, 15.0);
mapArrayInt.addArray(5.0, 15.0);
mapArrayInt.addArray(12.0, 8.0);
mapArrayInt.addArray(10.0, 8.0);
mapArrayInt.addArray(10.0, 8.0);
System.out.println(String.valueOf(mapArrayInt.arrays.get(new double[]{5.0, 15.0})));
System.out.println(String.valueOf(mapArrayInt.arrays.get(new double[]{12.0, 8.0})));
System.out.println(String.valueOf(mapArrayInt.arrays.get(new double[]{10.0, 8.0})));
}
void addArray(double val1, double val2) {
double[] newArray = new double[]{val1, val2};
if (!arrays.containsKey(newArray)) {
arrays.put(newArray, 1);
} else {
arrays.put(newArray, arrays.get(newArray) + 1);
}
}
}
I expected this output,
3
1
2
but got,
null
null
null
I'm quite new to Java, but I suspect this might be because each double[] counts as a unique because they are different instances, even though they contain the same two doubles.
How can I fix this, if I should at all (is there a better way)? I just need a data structure that allows me to
Add doubles[]
Preserves order of doubles[]
Easily iterate through to get doubles[] and number of said doubles[]
As I stated in my comment, with new you're creating a new instance of an object. Which means that the arrays you added with mapArrayInt.addArray(5.0, 15.0); and the arrays in mapArrayInt.arrays.get(new double[]{5.0, 15.0}) reference different objects. That's why you get null, because for the map those are different keys.
In order to circumvent this, you could create a custom wrapper class
import java.util.Arrays;
public class Exercise {
private final double[] array;
public Exercise(double first, double second) {
this.array = new double[]{first, second};
}
public boolean equals(Object obj) {
if(!(obj instanceof Exercise)) {
return false;
}
Exercise other = (Exercise)obj;
return Arrays.equals(this.array, other.array);
}
public int hashCode() {
return Arrays.hashCode(array);
}
}
The equals and hashCode methods are important, when you want to use this class in collections like Map, otherwise the hashcode of Object is used for checking equality and you'd have the same problem as you have now.
Then, in your main class you can use it like so:
void addArray(double val1, double val2) {
Exercise exercise = new Exercise(val1, val2);
if (!arrays.containsKey(exercise)) {
arrays.put(exercise, 1);
} else {
arrays.put(exercise, arrays.get(exercise) + 1);
}
}
And System.out.println(String.valueOf(mapArrayInt.arrays.get(new Exercise(5.0, 15.0))));
EDIT:
I changed one of the doubles to int (you said you're representing reps and weight ... and reps can only be a natural number right?)
You could build create an Exercise-Class like below and use the static method "of" to create the instances:
package somepackage;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
public class Exercise
{
private static final Map<Integer, Map<Double, WeakReference<Exercise>>> instances = new HashMap<>();
private final int reps;
private final double weight;
private Exercise(int reps, double weight)
{
this.reps = reps;
this.weight = weight;
}
public static Exercise of(int reps, double weight)
{
if (!instances.containsKey(reps))
{
instances.put(reps, new HashMap<>());
}
Map<Double, WeakReference<Exercise>> innerMap = instances.get(reps);
WeakReference<Exercise> weakRef = innerMap.get(weight);
Exercise instance = null;
if (weakRef != null)
{
instance = weakRef.get();
}
if (weakRef == null || instance == null || weakRef.isEnqueued())
{
instance = new Exercise(reps, weight);
innerMap.put(weight, new WeakReference<>(instance));
}
return instance;
}
public int getReps()
{
return this.reps;
}
public double getWeight()
{
return this.weight;
}
}
And then you could put those exercises in a map like below:
public void addArray(int reps, double weight)
{
Exercise exercise = Exercise.of(reps, weight);
if (!arrays.containsKey(exercise))
{
arrays.put(exercise, 1);
}
else
{
arrays.put(exercise, arrays.get(exercise) + 1);
}
}
OR:
Instead of an double[] as key you can use the a Map<Double, Integer> as your value for 2 values:
package somepackage;
import java.util.HashMap;
import java.util.Map;
public class MapArrayInt
{
private final Map<Double, Map<Double, Integer>> values;
public MapArrayInt()
{
this.values = new HashMap<>();
}
public void addArray(double val1, double val2)
{
if (!this.values.containsKey(val1))
{
this.values.put(val1, new HashMap<>());
}
Map<Double, Integer> innerValues = this.values.get(val1);
if (innerValues.containsKey(val2))
{
innerValues.put(val2, innerValues.get(val2) + 1);
}
else
{
innerValues.put(val2, 1);
}
}
public int getArrayValue(double val1, double val2)
{
Map<Double, Integer> innerValues = this.values.get(val1);
if (innerValues == null)
{
// you may also throw an Exception here
return 0;
}
Integer value = innerValues.get(val2);
if (value == null)
{
// also here you may throw an Exception
return 0;
}
return value;
}
public int getArrayValue(double[] values)
{
return getArrayValue(values[0], values[1]);
}
}
Related
I am trying to create a method which will return a random value from a list of strings in a hashmap when given a specific key. Here is my code below. (in particular look at the method "getRandomValue" as thats the one im having difficulty on). My question is: How do I look up a key in the map and return a random value from the hashmap?
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
public class Values {
private Map<String, List<String>> map;
private static Random rand = new Random();
public void ValueStore() {
this.map = new HashMap<String, List<String>>();
}
public boolean containsKey(String key) {
if(map.containsKey(key)) {
return true;
} return false;
}
public void put(String key, List<String> value) {
map = new HashMap<>();
map.put(key, value);
}
public String getRandomValue(String key) {
for (String key1 : map.keySet()) {
if(map.containsKey(key)) {
//not sure what to do here
}
}
return key;
}
}
First of all create an instance of java.util.Random as a static final field in your class, as your getRandomValue(String) method is going to need to use it each time it's called:
private static final Random RANDOM = new Random();
Now make use of this in your method:
public String getRandomValue(String key) {
List<String> list = map.get(key);
if (list == null) {
return null; // or throw an exception
}
int randomIndex = RANDOM.nextInt(list.length());
return list.get(randomIndex);
}
The Random.nextInt(int x) method will return a value between zero (inclusive) and x (exclusive), which makes it perfect for picking a random index (because List and array indices always begin at zero).
I have a set of objects. This objects calculate some numbers based on request parameters. Let's call them calculators. Each calculator has description where specified type of requests that this calculator the most suitable for.
For example,
Calculator1 : with this parameters : price > 10, gender = male, geo_id = 1, 2 or 3.
Calculator2 : with this parameters : price < 5, gender = male, geo_id = 1, 2.
For request : price = 11, gender = male, geo_id = 2 I should get calculator1 like the most suitable and then calculator2.
For request : price = 4, gender = male, geo_id = 2 I should get calculator2 and then calculator1.
For request : price = 3, gender = female, geo_id = 5 I should get only the second one.
Now I'm doing it with Lucene, but it's not really fit for this task. Can you recommend me some library or approach?
My suggestion would be to use a comparator. See a sketch of the classes below.
import java.util.HashMap;
import java.util.Map;
public abstract class Calculator {
public static Map<String, Integer> weights;
static {
weights = new HashMap<String, Integer>();
weights.put("price", 10);
weights.put("gender", 2);
weights.put("geo", 5);
}
public abstract int calculate(Map<String, Integer> request);
public abstract int fitnessFor(Map<String, Integer> request);
}
You can use the weights to adjust relative importance of the individual request parameters.
import java.util.Map;
public class Calculator1 extends Calculator {
public int calculate(Map<String, Integer> request) {
return -1;
}
#Override
public int fitnessFor(Map<String, Integer> request) {
int fitness = -1;
Integer price = request.get("price");
if (price == null)
return fitness;
if (price > 10)
fitness += weights.get("price");
return fitness;
}
public String toString() { return "Calculator1"; }
}
Calculator1 cares only about the pricey items.
import java.util.Map;
public class Calculator2 extends Calculator {
public int calculate(Map<String, Integer> request) {
return -1;
}
#Override
public int fitnessFor(Map<String, Integer> request) {
int fitness = -1;
Integer price = request.get("price");
if (price == null)
return fitness;
if (price < 5)
fitness += weights.get("price");
Integer gender = request.get("gender");
if (gender == null)
return fitness;
if (gender == 1)
fitness += weights.get("gender");
return fitness;
}
public String toString() { return "Calculator2"; }
}
Calculator2 cares about the less pricey items esp. if they are for gender 1.
The comparator just compares Calculators by their fitness relative to the request:
import java.util.Comparator;
import java.util.Map;
public class CalcComparator implements Comparator<Calculator> {
private Map<String, Integer> request;
public CalcComparator(Map<String, Integer> request) {
this.request = request;
}
#Override
public int compare(Calculator c1, Calculator c2) {
int c1Fitness = c1.fitnessFor(request);
int c2Fitness = c2.fitnessFor(request);
if (c1Fitness == c2Fitness)
return 0;
if (c1Fitness < c2Fitness)
return 1;
return -1;
}
}
Try it out with:
public class Main {
public static void main(String[] args) {
Map<String, Integer> request = new HashMap<String, Integer>();
request.put("price", 5);
request.put("gender", 1);
List<Calculator> calculators = new ArrayList<Calculator>();
calculators.add(new Calculator1());
calculators.add(new Calculator2());
Collections.sort(calculators, new CalcComparator(request));
System.out.println("For request: "+request);
for (Calculator c : calculators) {
System.out.println("\t"+c.toString() + "( fitness " + c.fitnessFor(request) + ")");
}
}
}
This is just a sketch to illustrate the idea. You will probably want to introduce an enum for the request parameters, maybe introduce a Request class, most likely change completely how fitness is computed, make some of the fields private and encapsulate them, etc.
The advantage is that you easily get an ordering of all the Calculators based on their fitness for the request.
Provided that I understood you correctly, I would suggest that you use the Specification design pattern which is used in cases like this. There's no need in such a fancy library like Lucene for such a simple task. The advantage of the Specification pattern is that it keeps all the filtering logic grouped and encapsulated. Your implementation may vary, but below is a simple example of what it could look like
public interface Specification<T> {
boolean isSatisfiedBy(T candidate);
Specification<T> and(Specification<T> specification);
Specification<T> or(Specification<T> specification);
Specification<T> not(Specification<T> specification);
}
public abstract class Calculator {
// ...
}
public class Calculator1 extends Calculator implements Specification<Request> {
public boolean isSatisfiedBy(Request request) {
// check if the request fits this calculator
}
}
public class Calculator2 extends Calculator implements Specification<Request> {
public boolean isSatisfiedBy(Request request) {
// check if the request fits this calculator
}
}
You can then have a collection or a pool of calculators such that
public class Calculators {
private final List<RequestSpecification> calculators;
public Calculator getOneSuitedFor(Request request) {
for (Calculator calculator : calculators) {
if (calculator.isSatisfiedBy(request)) {
return calculator;
}
}
return null;
}
}
And here how you would use it
Calculator calculator = Calculators.getOneSuitedFor(request);
Or, if needed, you can always go on and expand on it by making use of composition (see the reference link above) which allows for logic chaining and combining of different specifications depending on the context. This, however, would require a little bit different class design from that of above, but is more flexible
final Request request;
Specification<Calculator> price = new Specification<>() {
public boolean isSatisfiedBy(Calculator calculator) {
return calculator.supportsPrice(request.getPrice());
}
};
Specification<Calculator> gender = new Specification<>() {
public boolean isSatisfiedBy(Calculator calculator) {
return calculator.supportsGender(request.getGender());
}
};
Specification<Calculator> region = new Specification<>() {
public boolean isSatisfiedBy(Calculator calculator) {
return calculator.supportsRegion(request.getRegion());
}
};
Specification calcSpec = price.and(gender).and(region);
boolean isSatisfied = calcSpec.isSatisfiedBy(calculator);
Another interesting example is to use named specifications
Specification<Calculator> teenager = new Specification<>() {
public boolean isSatisfiedBy(Calculator calculator) {
return calculator.getAge() >= 13 && calculator.getAge() <= 19;
}
};
Specification<Calculator> male = new Specification<>() {
public boolean isSatisfiedBy(Calculator calculator) {
return calculator.getGender().equals("male");
}
};
Specification<Calculator> fromEurope = new Specification<>() {
public boolean isSatisfiedBy(Calculator calculator) {
return calculator.getRegion().equals("Europe");
}
};
Specification<Calculator> calcSpec = teenager.and(male).and(fromEurope);
boolean isSatisfied = calcSpec.isSatisfiedBy(calculator);
You could maybe try something like this:
public enum Calculator
{
CALC1
{
#Override
protected int matchCount( Map parameters )
{
// TODO count how many conditions match
return 0;
}
#Override
protected int calc( Map parameters )
{
// TODO
return 0;
}
},
CALC2
{
#Override
protected int matchCount( Map parameters )
{
// TODO count how many conditions match
return 0;
}
#Override
protected int calc( Map parameters )
{
// TODO
return 0;
}
};
protected abstract int matchCount( Map parameters );
protected abstract int calc( Map parameters );
public int doCalc( Map parameters )
{
Calculator mostSuited = null;
int maxCount = 0;
for ( Calculator calc : values() )
{
int matchCount = calc.matchCount( parameters );
if ( matchCount > maxCount )
{
mostSuited = calc;
}
}
return mostSuited.calc( parameters );
}
}
The way you would use the above is by invoking: int result = Calculator.doCalc( parameters )
Create a Calculator base class :
public static abstract class Calculator {
// This Contains the common score calculation methods.
public int getScore(int price, String gender, int geo_id) {
int score = 0;
if (gender.equalsIgnoreCase("male"))
score++;
if (getGeoIds().contains(geo_id))
score++;
return score;
}
public ArrayList<Integer> getGeoIds() {
// Fetching the common list of geo points to be compared.
ArrayList<Integer> lst = new ArrayList<Integer>();
lst.add(1);
lst.add(2);
return lst;
}
public abstract void doCalculation();
}
Then create your calculator classes by extending from this base.
public static class Calcualtor1 extends Calculator {
#Override
public int getScore(int price, String gender, int geo_id) {
// fetching score from common score calculation.
int score = super.getScore(price, gender, geo_id);
// Adding its own score logic.
if (price > 10)
score++;
return score;
}
#Override
public void doCalculation() {
// Do your actual work.
}
#Override
public ArrayList<Integer> getGeoIds() {
ArrayList<Integer> lst = super.getGeoIds();
// Adding the geo id to compare for this calculator.
lst.add(3);
return lst;
}
}
public static class Calcualtor2 extends Calculator {
#Override
public int getScore(int price, String gender, int geo_id) {
// fetching score from common score calculation.
int score = super.getScore(price, gender, geo_id);
// Adding its own score logic.
if (price < 5)
score++;
return score;
}
#Override
public void doCalculation() {
// Do your actual work.
}
}
Initialise values :
//To store the list of available calculators.
private static ArrayList<Class<? extends Calculator>> calculators;
static {
//Initializing the calculator list in static constructor.
calculators = new ArrayList<Class<? extends Calculator>>();
calculators.add(Calcualtor1.class);
calculators.add(Calcualtor2.class);
}
Actual processing :
public static void main(String[] args) {
int price = 10;
String gender = "male";
int geo_id = 2;
Calculator calculator = null;
int score = 0;
for (Class<? extends Calculator> calClass : calculators) {
Calculator cal = null;
try {
cal = calClass.newInstance();
} catch (Exception e) {
continue;
}
int calScore = cal.getScore(price, gender, geo_id);
if (calScore > score) {
calculator = cal;
score = calScore;
}
}
if (calculator != null) {
calculator.doCalculation();
}
}
I have an object which has a name and a score. I would like to sort a collection of such objects so that they are grouped by name and sorted by maximum score in each group (and within the group by descending score as well).
let me demonstrate what I intend to achieve. assume I have these objects(name, score):
(a, 3)
(a, 9)
(b, 7)
(b, 10)
(c, 8)
(c, 3)
then I would like them to be sorted like this:
(b, 10)
(b, 7)
(a, 9)
(a, 3)
(c, 8)
(c, 3)
is this feasible with a Comparator? I can't figure it out, so any hints would be appreciated.
No, you can't do it with a single sort with a single Comparator.
You have to:
group by name
sort the groups, by highest score in group
Then you need to flatten the groups back to a list.
With Java 8
Edit: Since i wrote this answer, Java 8 has come out, which simplifies the problem a lot:
import java.util.*;
import static java.util.Comparator.*;
import static java.util.stream.Collectors.*;
List<Record> result = records.stream()
.sorted(comparingInt(Record::getScore).reversed())
.collect(groupingBy(Record::getName, LinkedHashMap::new, toList()))
.values().stream()
.flatMap(Collection::stream)
.collect(toList());
First we sort by score reversed, and then we group using a LinkedHashMap, which will preserve the insertion order for the keys, so keys with higher score will come first.
Sorting first is OK if the groups are small, so the redundant compares between objects in different groups don't hurt so much.
Also, with this method, duplicates are preserved.
Alternatively, if you don't care about preserving duplicates, you can:
Comparator<Record> highestScoreFirst = comparingInt(Record::getScore).reversed();
List<Record> result = records.stream()
.collect(groupingBy(Record::getName,
toCollection(() -> new TreeSet<>(highestScoreFirst))))
.values().stream()
.sorted(comparing(SortedSet::first, highestScoreFirst))
.flatMap(Collection::stream)
.collect(toList());
Where the records are grouped into sorted TreeSets, instead of sorting the values as the first operation of the stream, and then the sets are sorted by their first, highest value.
Grouping before sorting is appropriate if the groups are big, to cut down on redundant compares.
Implementing Comparable:
And you can make it shorter by having your record implement Comparable
public class Record implements Comparable<Record> {
#Override
public int compareTo(Record other) {
// Highest first
return -Integer.compare(getScore(), other.getScore());
/* Or equivalently:
return Integer.compare(other.getScore(), getScore());
*/
}
...
}
List<Record> result = records.stream()
.collect(groupingBy(Record::getName, toCollection(TreeSet::new)))
.values().stream()
.sorted(comparing(SortedSet::first))
.flatMap(Collection::stream)
.collect(toList());
Before Java 8
Edit: Here is a really rough unit test that demonstrates one way to do it. I haven't cleaned it up as much as i would have liked.
Stuff like this is painful in Java, and i would normally use Google Guava for this.
import org.junit.Test;
import java.util.*;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
public class GroupSortTest {
#Test
public void testGroupSort() {
List<Record> records = asList(
new Record("a", 3),
new Record("a", 9),
new Record("b", 7),
new Record("b", 10),
new Record("c", 8),
new Record("c", 3));
List<SortedMap<Integer, Record>> recordsGroupedByName = groupRecordsByNameAndSortedByScoreDescending(records);
Collections.sort(recordsGroupedByName, byHighestScoreInGroupDescending());
List<Record> result = flattenGroups(recordsGroupedByName);
List<Record> expected = asList(
new Record("b", 10),
new Record("b", 7),
new Record("a", 9),
new Record("a", 3),
new Record("c", 8),
new Record("c", 3));
assertEquals(expected, result);
}
private List<Record> flattenGroups(List<SortedMap<Integer, Record>> recordGroups) {
List<Record> result = new ArrayList<Record>();
for (SortedMap<Integer, Record> group : recordGroups) {
result.addAll(group.values());
}
return result;
}
private List<SortedMap<Integer, Record>> groupRecordsByNameAndSortedByScoreDescending(List<Record> records) {
Map<String, SortedMap<Integer, Record>> groupsByName = new HashMap<String, SortedMap<Integer, Record>>();
for (Record record : records) {
SortedMap<Integer, Record> group = groupsByName.get(record.getName());
if (null == group) {
group = new TreeMap<Integer, Record>(descending());
groupsByName.put(record.getName(), group);
}
group.put(record.getScore(), record);
}
return new ArrayList<SortedMap<Integer, Record>>(groupsByName.values());
}
private DescendingSortComparator descending() {
return new DescendingSortComparator();
}
private ByFirstKeyDescending byHighestScoreInGroupDescending() {
return new ByFirstKeyDescending();
}
private static class ByFirstKeyDescending implements Comparator<SortedMap<Integer, Record>> {
public int compare(SortedMap<Integer, Record> o1, SortedMap<Integer, Record> o2) {
return o2.firstKey().compareTo(o1.firstKey());
}
}
private static class DescendingSortComparator implements Comparator<Comparable> {
public int compare(Comparable o1, Comparable o2) {
return o2.compareTo(o1);
}
}
}
Foreach over the collection, and put the objects into a Map<String, SortedSet<YourObject>>, keyed by name, where the SortedSet is a TreeSet with a custom comparator that compares by score.
Then foreach over the map's values() collection, and put the groups into a SortedSet<SortedSet<YourObject>>, with a second custom comparator that compares SortedSets according to their largest element. Actually, instead of foreaching, you can simply use addAll().
Here's the code:
public class SortThings {
static class Thing {
public final String name;
public final int score;
public Thing(String name, int score) {
this.name = name;
this.score = score;
}
#Override
public String toString() {
return "(" + name + ", " + score + ")";
}
}
public static void main(String[] args) {
Collection<Thing> things = Arrays.asList(
new Thing("a", 3),
new Thing("a", 9),
new Thing("b", 7),
new Thing("b", 10),
new Thing("c", 8),
new Thing("c", 3)
);
SortedSet<SortedSet<Thing>> sortedGroups = sortThings(things);
System.out.println(sortedGroups);
}
private static SortedSet<SortedSet<Thing>> sortThings(Collection<Thing> things) {
final Comparator<Thing> compareThings = new Comparator<Thing>() {
public int compare(Thing a, Thing b) {
Integer aScore = a.score;
Integer bScore = b.score;
return aScore.compareTo(bScore);
}
};
// first pass
Map<String, SortedSet<Thing>> groups = new HashMap<String, SortedSet<Thing>>();
for (Thing obj: things) {
SortedSet<Thing> group = groups.get(obj.name);
if (group == null) {
group = new TreeSet<Thing>(compareThings);
groups.put(obj.name, group);
}
group.add(obj);
}
// second pass
SortedSet<SortedSet<Thing>> sortedGroups = new TreeSet<SortedSet<Thing>>(new Comparator<SortedSet<Thing>>() {
public int compare(SortedSet<Thing> a, SortedSet<Thing> b) {
return compareThings.compare(a.last(), b.last());
}
});
sortedGroups.addAll(groups.values());
return sortedGroups;
}
}
Note that the output is in smallest-to-largest order. That's the natural order with Java's collections; it would be trivial to modify this to sort the other way if that's what you need.
public class ScoreComparator implements Comparator<Item>
{
public int compare(Item a, Item b){
if (a.name.equals(b.name){
return a.score.compareTo(b.score);
}
return a.name.compareTo(b.Name);
}
}
I think you can do this. First check to see if the group is equal. If it is then compare on score. Otherwise return which group you want to be more on top. Let me code it up.
class Item{
String name;
int score;
}
new Comparator<Item>(){
#Override
public int compare(Item o1, Item o2) {
if (o1.name.equals(o2.name)) {
return o1.score > o2.score ? 1 : -1; // might have to flip this. I didn't test
}else {
return o1.name.compareTo(o2.name);
}
}
};
Yes Go for Comparator
Give first preference to name in comparison and then to score. it will be grouped up with sorted score also
List<Score> scores = new ArrayList<Score>();
scores.add(new Score("a", 58));
scores.add(new Score("a", 10));
scores.add(new Score("b", 165));
scores.add(new Score("a", 1));
scores.add(new Score("b", 1658));
scores.add(new Score("c", 1));
scores.add(new Score("c", 10));
scores.add(new Score("c", 0));
Collections.sort(scores, new Comparator<Score>() {
public int compare(Score o1, Score o2) {
if (o1.getName().compareTo(o2.getName()) == 0) {
return o2.getScore() - o1.getScore();
} else {
return o1.getName().compareTo(o2.getName());
}
}
});
System.out.println(scores);
Update
As Chris pointed out.
import java.util.*;
/**
*
* #author Jigar
*/
class Score {
private String name;
private List<Integer> scores;
public Score() {
}
public Score(String name, List<Integer> scores) {
this.name = name;
this.scores = scores;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Integer> getScores() {
return scores;
}
public void setScores(List<Integer> scores) {
this.scores = scores;
}
#Override
public String toString() {
return name + " , " + scores + "\n";
}
}
public class ScoreDemo {
public static void main(String[] args) {
List<Score> scores = new ArrayList<Score>();
List<Integer> lstA = new ArrayList<Integer>();
lstA.add(3);
lstA.add(9);
lstA.add(7);
Collections.sort(lstA);
Collections.reverse(lstA);
List<Integer> lstB = new ArrayList<Integer>();
lstB.add(10);
lstB.add(8);
lstB.add(3);
Collections.sort(lstB);
Collections.reverse(lstB);
List<Integer> lstC = new ArrayList<Integer>();
lstC.add(8);
lstC.add(3);
Collections.sort(lstC);
Collections.reverse(lstC);
scores.add(new Score("a", lstA));
scores.add(new Score("b", lstB));
scores.add(new Score("c", lstC));
Collections.sort(scores, new Comparator<Score>() {
public int compare(Score o1, Score o2) {
return o2.getScores().get(0).compareTo(o1.getScores().get(0));
}
});
System.out.println(scores);
}
}
Here is working ideone demo
UPDATE: Here is working ideone demo
I have a list of "Report" objects with three fields (All String type)-
ReportKey
StudentNumber
School
I have a sort code goes like-
Collections.sort(reportList, new Comparator<Report>() {
#Override
public int compare(final Report record1, final Report record2) {
return (record1.getReportKey() + record1.getStudentNumber() + record1.getSchool())
.compareTo(record2.getReportKey() + record2.getStudentNumber() + record2.getSchool());
}
});
For some reason, I don't have the sorted order. One advised to put spaces in between fields, but why?
Do you see anything wrong with the code?
(originally from Ways to sort lists of objects in Java based on multiple fields)
Original working code in this gist
Using Java 8 lambda's (added April 10, 2019)
Java 8 solves this nicely by lambda's (though Guava and Apache Commons might still offer more flexibility):
Collections.sort(reportList, Comparator.comparing(Report::getReportKey)
.thenComparing(Report::getStudentNumber)
.thenComparing(Report::getSchool));
Thanks to #gaoagong's answer below.
Note that one advantage here is that the getters are evaluated lazily (eg. getSchool() is only evaluated if relevant).
Messy and convoluted: Sorting by hand
Collections.sort(pizzas, new Comparator<Pizza>() {
#Override
public int compare(Pizza p1, Pizza p2) {
int sizeCmp = p1.size.compareTo(p2.size);
if (sizeCmp != 0) {
return sizeCmp;
}
int nrOfToppingsCmp = p1.nrOfToppings.compareTo(p2.nrOfToppings);
if (nrOfToppingsCmp != 0) {
return nrOfToppingsCmp;
}
return p1.name.compareTo(p2.name);
}
});
This requires a lot of typing, maintenance and is error prone. The only advantage is that getters are only invoked when relevant.
The reflective way: Sorting with BeanComparator
ComparatorChain chain = new ComparatorChain(Arrays.asList(
new BeanComparator("size"),
new BeanComparator("nrOfToppings"),
new BeanComparator("name")));
Collections.sort(pizzas, chain);
Obviously this is more concise, but even more error prone as you lose your direct reference to the fields by using Strings instead (no typesafety, auto-refactorings). Now if a field is renamed, the compiler won’t even report a problem. Moreover, because this solution uses reflection, the sorting is much slower.
Getting there: Sorting with Google Guava’s ComparisonChain
Collections.sort(pizzas, new Comparator<Pizza>() {
#Override
public int compare(Pizza p1, Pizza p2) {
return ComparisonChain.start().compare(p1.size, p2.size).compare(p1.nrOfToppings, p2.nrOfToppings).compare(p1.name, p2.name).result();
// or in case the fields can be null:
/*
return ComparisonChain.start()
.compare(p1.size, p2.size, Ordering.natural().nullsLast())
.compare(p1.nrOfToppings, p2.nrOfToppings, Ordering.natural().nullsLast())
.compare(p1.name, p2.name, Ordering.natural().nullsLast())
.result();
*/
}
});
This is much better, but requires some boiler plate code for the most common use case: null-values should be valued less by default. For null-fields, you have to provide an extra directive to Guava what to do in that case. This is a flexible mechanism if you want to do something specific, but often you want the default case (ie. 1, a, b, z, null).
And as noted in the comments below, these getters are all evaluated immediately for each comparison.
Sorting with Apache Commons CompareToBuilder
Collections.sort(pizzas, new Comparator<Pizza>() {
#Override
public int compare(Pizza p1, Pizza p2) {
return new CompareToBuilder().append(p1.size, p2.size).append(p1.nrOfToppings, p2.nrOfToppings).append(p1.name, p2.name).toComparison();
}
});
Like Guava’s ComparisonChain, this library class sorts easily on multiple fields, but also defines default behavior for null values (ie. 1, a, b, z, null). However, you can’t specify anything else either, unless you provide your own Comparator.
Again, as noted in the comments below, these getters are all evaluated immediately for each comparison.
Thus
Ultimately it comes down to flavor and the need for flexibility (Guava’s ComparisonChain) vs. concise code (Apache’s CompareToBuilder).
Bonus method
I found a nice solution that combines multiple comparators in order of priority on CodeReview in a MultiComparator:
class MultiComparator<T> implements Comparator<T> {
private final List<Comparator<T>> comparators;
public MultiComparator(List<Comparator<? super T>> comparators) {
this.comparators = comparators;
}
public MultiComparator(Comparator<? super T>... comparators) {
this(Arrays.asList(comparators));
}
public int compare(T o1, T o2) {
for (Comparator<T> c : comparators) {
int result = c.compare(o1, o2);
if (result != 0) {
return result;
}
}
return 0;
}
public static <T> void sort(List<T> list, Comparator<? super T>... comparators) {
Collections.sort(list, new MultiComparator<T>(comparators));
}
}
Ofcourse Apache Commons Collections has a util for this already:
ComparatorUtils.chainedComparator(comparatorCollection)
Collections.sort(list, ComparatorUtils.chainedComparator(comparators));
Do you see anything wrong with the code?
Yes. Why are you adding the three fields together before you compare them?
I would probably do something like this: (assuming the fields are in the order you wish to sort them in)
#Override public int compare(final Report record1, final Report record2) {
int c;
c = record1.getReportKey().compareTo(record2.getReportKey());
if (c == 0)
c = record1.getStudentNumber().compareTo(record2.getStudentNumber());
if (c == 0)
c = record1.getSchool().compareTo(record2.getSchool());
return c;
}
I'd make a comparator using Guava's ComparisonChain:
public class ReportComparator implements Comparator<Report> {
public int compare(Report r1, Report r2) {
return ComparisonChain.start()
.compare(r1.getReportKey(), r2.getReportKey())
.compare(r1.getStudentNumber(), r2.getStudentNumber())
.compare(r1.getSchool(), r2.getSchool())
.result();
}
}
This is an old question so I don't see a Java 8 equivalent. Here is an example for this specific case.
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Compares multiple parts of the Report object.
*/
public class SimpleJava8ComparatorClass {
public static void main(String[] args) {
List<Report> reportList = new ArrayList<>();
reportList.add(new Report("reportKey2", "studentNumber2", "school1"));
reportList.add(new Report("reportKey4", "studentNumber4", "school6"));
reportList.add(new Report("reportKey1", "studentNumber1", "school1"));
reportList.add(new Report("reportKey3", "studentNumber2", "school4"));
reportList.add(new Report("reportKey2", "studentNumber2", "school3"));
System.out.println("pre-sorting");
System.out.println(reportList);
System.out.println();
Collections.sort(reportList, Comparator.comparing(Report::getReportKey)
.thenComparing(Report::getStudentNumber)
.thenComparing(Report::getSchool));
System.out.println("post-sorting");
System.out.println(reportList);
}
private static class Report {
private String reportKey;
private String studentNumber;
private String school;
public Report(String reportKey, String studentNumber, String school) {
this.reportKey = reportKey;
this.studentNumber = studentNumber;
this.school = school;
}
public String getReportKey() {
return reportKey;
}
public void setReportKey(String reportKey) {
this.reportKey = reportKey;
}
public String getStudentNumber() {
return studentNumber;
}
public void setStudentNumber(String studentNumber) {
this.studentNumber = studentNumber;
}
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
#Override
public String toString() {
return "Report{" +
"reportKey='" + reportKey + '\'' +
", studentNumber='" + studentNumber + '\'' +
", school='" + school + '\'' +
'}';
}
}
}
If you want to sort by report key, then student number, then school, you should do something like this:
public class ReportComparator implements Comparator<Report>
{
public int compare(Report r1, Report r2)
{
int result = r1.getReportKey().compareTo(r2.getReportKey());
if (result != 0)
{
return result;
}
result = r1.getStudentNumber().compareTo(r2.getStudentNumber());
if (result != 0)
{
return result;
}
return r1.getSchool().compareTo(r2.getSchool());
}
}
This assumes none of the values can be null, of course - it gets more complicated if you need to allow for null values for the report, report key, student number or school.
While you could get the string concatenation version to work using spaces, it would still fail in strange cases if you had odd data which itself included spaces etc. The above code is the logical code you want... compare by report key first, then only bother with the student number if the report keys are the same, etc.
I suggest to use Java 8 Lambda approach:
List<Report> reportList = new ArrayList<Report>();
reportList.sort(Comparator.comparing(Report::getRecord1).thenComparing(Report::getRecord2));
Sorting with multiple fields in Java8
package com.java8.chapter1;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import static java.util.Comparator.*;
public class Example1 {
public static void main(String[] args) {
List<Employee> empList = getEmpList();
// Before Java 8
empList.sort(new Comparator<Employee>() {
#Override
public int compare(Employee o1, Employee o2) {
int res = o1.getDesignation().compareTo(o2.getDesignation());
if (res == 0) {
return o1.getSalary() > o2.getSalary() ? 1 : o1.getSalary() < o2.getSalary() ? -1 : 0;
} else {
return res;
}
}
});
for (Employee emp : empList) {
System.out.println(emp);
}
System.out.println("---------------------------------------------------------------------------");
// In Java 8
empList.sort(comparing(Employee::getDesignation).thenComparing(Employee::getSalary));
empList.stream().forEach(System.out::println);
}
private static List<Employee> getEmpList() {
return Arrays.asList(new Employee("Lakshman A", "Consultent", 450000),
new Employee("Chaitra S", "Developer", 250000), new Employee("Manoj PVN", "Developer", 250000),
new Employee("Ramesh R", "Developer", 280000), new Employee("Suresh S", "Developer", 270000),
new Employee("Jaishree", "Opearations HR", 350000));
}
}
class Employee {
private String fullName;
private String designation;
private double salary;
public Employee(String fullName, String designation, double salary) {
super();
this.fullName = fullName;
this.designation = designation;
this.salary = salary;
}
public String getFullName() {
return fullName;
}
public String getDesignation() {
return designation;
}
public double getSalary() {
return salary;
}
#Override
public String toString() {
return "Employee [fullName=" + fullName + ", designation=" + designation + ", salary=" + salary + "]";
}
}
If the StudentNumber is numeric it will not be sorted numeric but alphanumeric.
Do not expect
"2" < "11"
it will be:
"11" < "2"
Use Comparator interface with methods introduced in JDK1.8: comparing and thenComparing, or more concrete methods: comparingXXX and thenComparingXXX.
For example, if we wanna sort a list of persons by their id firstly, then age, then name:
Comparator<Person> comparator = Comparator.comparingLong(Person::getId)
.thenComparingInt(Person::getAge)
.thenComparing(Person::getName);
personList.sort(comparator);
If you want to sort based on ReportKey first then Student Number then School, you need to compare each String instead of concatenating them. Your method might work if you pad the strings with spaces so that each ReportKey is the same length and so on, but it is not really worth the effort. Instead just change the compare method to compare the ReportKeys, if compareTo returns 0 then try StudentNumber, then School.
I had the same issue and I needed an algorithm using a config file. In This way you can use multiple fields define by a configuration file (simulate just by a List<String) config)
public static void test() {
// Associate your configName with your Comparator
Map<String, Comparator<DocumentDto>> map = new HashMap<>();
map.put("id", new IdSort());
map.put("createUser", new DocumentUserSort());
map.put("documentType", new DocumentTypeSort());
/**
In your config.yml file, you'll have something like
sortlist:
- documentType
- createUser
- id
*/
List<String> config = new ArrayList<>();
config.add("documentType");
config.add("createUser");
config.add("id");
List<Comparator<DocumentDto>> sorts = new ArrayList<>();
for (String comparator : config) {
sorts.add(map.get(comparator));
}
// Begin creation of the list
DocumentDto d1 = new DocumentDto();
d1.setDocumentType(new DocumentTypeDto());
d1.getDocumentType().setCode("A");
d1.setId(1);
d1.setCreateUser("Djory");
DocumentDto d2 = new DocumentDto();
d2.setDocumentType(new DocumentTypeDto());
d2.getDocumentType().setCode("A");
d2.setId(2);
d2.setCreateUser("Alex");
DocumentDto d3 = new DocumentDto();
d3.setDocumentType(new DocumentTypeDto());
d3.getDocumentType().setCode("A");
d3.setId(3);
d3.setCreateUser("Djory");
DocumentDto d4 = new DocumentDto();
d4.setDocumentType(new DocumentTypeDto());
d4.getDocumentType().setCode("A");
d4.setId(4);
d4.setCreateUser("Alex");
DocumentDto d5 = new DocumentDto();
d5.setDocumentType(new DocumentTypeDto());
d5.getDocumentType().setCode("D");
d5.setId(5);
d5.setCreateUser("Djory");
DocumentDto d6 = new DocumentDto();
d6.setDocumentType(new DocumentTypeDto());
d6.getDocumentType().setCode("B");
d6.setId(6);
d6.setCreateUser("Alex");
DocumentDto d7 = new DocumentDto();
d7.setDocumentType(new DocumentTypeDto());
d7.getDocumentType().setCode("B");
d7.setId(7);
d7.setCreateUser("Alex");
List<DocumentDto> documents = new ArrayList<>();
documents.add(d1);
documents.add(d2);
documents.add(d3);
documents.add(d4);
documents.add(d5);
documents.add(d6);
documents.add(d7);
// End creation of the list
// The Sort
Stream<DocumentDto> docStream = documents.stream();
// we need to reverse this list in order to sort by documentType first because stream are pull-based, last sorted() will have the priority
Collections.reverse(sorts);
for(Comparator<DocumentDto> entitySort : sorts){
docStream = docStream.sorted(entitySort);
}
documents = docStream.collect(Collectors.toList());
// documents has been sorted has you configured
// in case of equality second sort will be used.
System.out.println(documents);
}
Comparator objects are really simple.
public class IdSort implements Comparator<DocumentDto> {
#Override
public int compare(DocumentDto o1, DocumentDto o2) {
return o1.getId().compareTo(o2.getId());
}
}
public class DocumentUserSort implements Comparator<DocumentDto> {
#Override
public int compare(DocumentDto o1, DocumentDto o2) {
return o1.getCreateUser().compareTo(o2.getCreateUser());
}
}
public class DocumentTypeSort implements Comparator<DocumentDto> {
#Override
public int compare(DocumentDto o1, DocumentDto o2) {
return o1.getDocumentType().getCode().compareTo(o2.getDocumentType().getCode());
}
}
Conclusion : this method isn't has efficient but you can create generic sort using a file configuration in this way.
Here is a full example comparing 2 fields in an object, one String and one int, also using Collator to sort.
public class Test {
public static void main(String[] args) {
Collator myCollator;
myCollator = Collator.getInstance(Locale.US);
List<Item> items = new ArrayList<Item>();
items.add(new Item("costrels", 1039737, ""));
items.add(new Item("Costs", 1570019, ""));
items.add(new Item("costs", 310831, ""));
items.add(new Item("costs", 310832, ""));
Collections.sort(items, new Comparator<Item>() {
#Override
public int compare(final Item record1, final Item record2) {
int c;
//c = record1.item1.compareTo(record2.item1); //optional comparison without Collator
c = myCollator.compare(record1.item1, record2.item1);
if (c == 0)
{
return record1.item2 < record2.item2 ? -1
: record1.item2 > record2.item2 ? 1
: 0;
}
return c;
}
});
for (Item item : items)
{
System.out.println(item.item1);
System.out.println(item.item2);
}
}
public static class Item
{
public String item1;
public int item2;
public String item3;
public Item(String item1, int item2, String item3)
{
this.item1 = item1;
this.item2 = item2;
this.item3 = item3;
}
}
}
Output:
costrels
1039737
costs
310831
costs
310832
Costs
1570019
A lot of answers above have fields compared in single comparator method which is not actually working. There are some answers though with different comparators implemented for each field, I am posting this because this example would be much more clearer and simple to understand I am believing.
class Student{
Integer bornYear;
Integer bornMonth;
Integer bornDay;
public Student(int bornYear, int bornMonth, int bornDay) {
this.bornYear = bornYear;
this.bornMonth = bornMonth;
this.bornDay = bornDay;
}
public Student(int bornYear, int bornMonth) {
this.bornYear = bornYear;
this.bornMonth = bornMonth;
}
public Student(int bornYear) {
this.bornYear = bornYear;
}
public Integer getBornYear() {
return bornYear;
}
public void setBornYear(int bornYear) {
this.bornYear = bornYear;
}
public Integer getBornMonth() {
return bornMonth;
}
public void setBornMonth(int bornMonth) {
this.bornMonth = bornMonth;
}
public Integer getBornDay() {
return bornDay;
}
public void setBornDay(int bornDay) {
this.bornDay = bornDay;
}
#Override
public String toString() {
return "Student [bornYear=" + bornYear + ", bornMonth=" + bornMonth + ", bornDay=" + bornDay + "]";
}
}
class TestClass
{
// Comparator problem in JAVA for sorting objects based on multiple fields
public static void main(String[] args)
{
int N,c;// Number of threads
Student s1=new Student(2018,12);
Student s2=new Student(2018,12);
Student s3=new Student(2018,11);
Student s4=new Student(2017,6);
Student s5=new Student(2017,4);
Student s6=new Student(2016,8);
Student s7=new Student(2018);
Student s8=new Student(2017,8);
Student s9=new Student(2017,2);
Student s10=new Student(2017,9);
List<Student> studentList=new ArrayList<>();
studentList.add(s1);
studentList.add(s2);
studentList.add(s3);
studentList.add(s4);
studentList.add(s5);
studentList.add(s6);
studentList.add(s7);
studentList.add(s8);
studentList.add(s9);
studentList.add(s10);
Comparator<Student> byMonth=new Comparator<Student>() {
#Override
public int compare(Student st1,Student st2) {
if(st1.getBornMonth()!=null && st2.getBornMonth()!=null) {
return st2.getBornMonth()-st1.getBornMonth();
}
else if(st1.getBornMonth()!=null) {
return 1;
}
else {
return -1;
}
}};
Collections.sort(studentList, new Comparator<Student>() {
#Override
public int compare(Student st1,Student st2) {
return st2.getBornYear()-st1.getBornYear();
}}.thenComparing(byMonth));
System.out.println("The sorted students list in descending is"+Arrays.deepToString(studentList.toArray()));
}
}
OUTPUT
The sorted students list in descending is[Student [bornYear=2018, bornMonth=null, bornDay=null], Student [bornYear=2018, bornMonth=12, bornDay=null], Student [bornYear=2018, bornMonth=12, bornDay=null], Student [bornYear=2018, bornMonth=11, bornDay=null], Student [bornYear=2017, bornMonth=9, bornDay=null], Student [bornYear=2017, bornMonth=8, bornDay=null], Student [bornYear=2017, bornMonth=6, bornDay=null], Student [bornYear=2017, bornMonth=4, bornDay=null], Student [bornYear=2017, bornMonth=2, bornDay=null], Student [bornYear=2016, bornMonth=8, bornDay=null]]
im my case List of Lists (in the approximation examle):
List<T>.steam
.map(Class1.StaticInnerClass1::Field1)
.flatMap(x -> x.getField11ListStaticInnerClass2OfField1.stream())
.max(Comparator.comparing(Class1.StaticInnerClass2::Field21,Collections.reverseOrder())
.thenCompare(Class1.StaticInnerClass2::Field22));
For my case, I had 3 fields (For example - int index, bool isArchive ,bool isClassPrivate)
and I summed their comparison result like this-
Collections.sort(getData(), (o1, o2) ->
Integer.compare(o1.getIndex(getContext()), o2.getIndex(getContext()))
+ Boolean.compare(o1.isArchive(), o2.isArchive())
+ Boolean.compare(o1.isClassPrivate(), o2.isClassPrivate()
));
I am trying to return 2 values from a Java method but I get these errors. Here is my code:
// Method code
public static int something(){
int number1 = 1;
int number2 = 2;
return number1, number2;
}
// Main method code
public static void main(String[] args) {
something();
System.out.println(number1 + number2);
}
Error:
Exception in thread "main" java.lang.RuntimeException: Uncompilable source code - missing return statement
at assignment.Main.something(Main.java:86)
at assignment.Main.main(Main.java:53)
Java Result: 1
Instead of returning an array that contains the two values or using a generic Pair class, consider creating a class that represents the result that you want to return, and return an instance of that class. Give the class a meaningful name. The benefits of this approach over using an array are type safety and it will make your program much easier to understand.
Note: A generic Pair class, as proposed in some of the other answers here, also gives you type safety, but doesn't convey what the result represents.
Example (which doesn't use really meaningful names):
final class MyResult {
private final int first;
private final int second;
public MyResult(int first, int second) {
this.first = first;
this.second = second;
}
public int getFirst() {
return first;
}
public int getSecond() {
return second;
}
}
// ...
public static MyResult something() {
int number1 = 1;
int number2 = 2;
return new MyResult(number1, number2);
}
public static void main(String[] args) {
MyResult result = something();
System.out.println(result.getFirst() + result.getSecond());
}
Java does not support multi-value returns. Return an array of values.
// Function code
public static int[] something(){
int number1 = 1;
int number2 = 2;
return new int[] {number1, number2};
}
// Main class code
public static void main(String[] args) {
int result[] = something();
System.out.println(result[0] + result[1]);
}
You could implement a generic Pair if you are sure that you just need to return two values:
public class Pair<U, V> {
/**
* The first element of this <code>Pair</code>
*/
private U first;
/**
* The second element of this <code>Pair</code>
*/
private V second;
/**
* Constructs a new <code>Pair</code> with the given values.
*
* #param first the first element
* #param second the second element
*/
public Pair(U first, V second) {
this.first = first;
this.second = second;
}
//getter for first and second
and then have the method return that Pair:
public Pair<Object, Object> getSomePair();
You can only return one value in Java, so the neatest way is like this:
return new Pair<Integer>(number1, number2);
Here's an updated version of your code:
public class Scratch
{
// Function code
public static Pair<Integer> something() {
int number1 = 1;
int number2 = 2;
return new Pair<Integer>(number1, number2);
}
// Main class code
public static void main(String[] args) {
Pair<Integer> pair = something();
System.out.println(pair.first() + pair.second());
}
}
class Pair<T> {
private final T m_first;
private final T m_second;
public Pair(T first, T second) {
m_first = first;
m_second = second;
}
public T first() {
return m_first;
}
public T second() {
return m_second;
}
}
Here is the really simple and short solution with SimpleEntry:
AbstractMap.Entry<String, Float> myTwoCents=new AbstractMap.SimpleEntry<>("maximum possible performance reached" , 99.9f);
String question=myTwoCents.getKey();
Float answer=myTwoCents.getValue();
Only uses Java built in functions and it comes with the type safty benefit.
Use a Pair/Tuple type object , you don't even need to create one if u depend on Apache commons-lang. Just use the Pair class.
you have to use collections to return more then one return values
in your case you write your code as
public static List something(){
List<Integer> list = new ArrayList<Integer>();
int number1 = 1;
int number2 = 2;
list.add(number1);
list.add(number2);
return list;
}
// Main class code
public static void main(String[] args) {
something();
List<Integer> numList = something();
}
public class Mulretun
{
public String name;;
public String location;
public String[] getExample()
{
String ar[] = new String[2];
ar[0]="siva";
ar[1]="dallas";
return ar; //returning two values at once
}
public static void main(String[] args)
{
Mulretun m=new Mulretun();
String ar[] =m.getExample();
int i;
for(i=0;i<ar.length;i++)
System.out.println("return values are: " + ar[i]);
}
}
o/p:
return values are: siva
return values are: dallas
I'm curious as to why nobody has come up with the more elegant callback solution. So instead of using a return type you use a handler passed into the method as an argument. The example below has the two contrasting approaches. I know which of the two is more elegant to me. :-)
public class DiceExample {
public interface Pair<T1, T2> {
T1 getLeft();
T2 getRight();
}
private Pair<Integer, Integer> rollDiceWithReturnType() {
double dice1 = (Math.random() * 6);
double dice2 = (Math.random() * 6);
return new Pair<Integer, Integer>() {
#Override
public Integer getLeft() {
return (int) Math.ceil(dice1);
}
#Override
public Integer getRight() {
return (int) Math.ceil(dice2);
}
};
}
#FunctionalInterface
public interface ResultHandler {
void handleDice(int ceil, int ceil2);
}
private void rollDiceWithResultHandler(ResultHandler resultHandler) {
double dice1 = (Math.random() * 6);
double dice2 = (Math.random() * 6);
resultHandler.handleDice((int) Math.ceil(dice1), (int) Math.ceil(dice2));
}
public static void main(String[] args) {
DiceExample object = new DiceExample();
Pair<Integer, Integer> result = object.rollDiceWithReturnType();
System.out.println("Dice 1: " + result.getLeft());
System.out.println("Dice 2: " + result.getRight());
object.rollDiceWithResultHandler((dice1, dice2) -> {
System.out.println("Dice 1: " + dice1);
System.out.println("Dice 2: " + dice2);
});
}
}
You don't need to create your own class to return two different values. Just use a HashMap like this:
private HashMap<Toy, GameLevel> getToyAndLevelOfSpatial(Spatial spatial)
{
Toy toyWithSpatial = firstValue;
GameLevel levelToyFound = secondValue;
HashMap<Toy,GameLevel> hm=new HashMap<>();
hm.put(toyWithSpatial, levelToyFound);
return hm;
}
private void findStuff()
{
HashMap<Toy, GameLevel> hm = getToyAndLevelOfSpatial(spatial);
Toy firstValue = hm.keySet().iterator().next();
GameLevel secondValue = hm.get(firstValue);
}
You even have the benefit of type safety.
Return an Array Of Objects
private static Object[] f ()
{
double x =1.0;
int y= 2 ;
return new Object[]{Double.valueOf(x),Integer.valueOf(y)};
}
In my opinion the best is to create a new class which constructor is the function you need, e.g.:
public class pairReturn{
//name your parameters:
public int sth1;
public double sth2;
public pairReturn(int param){
//place the code of your function, e.g.:
sth1=param*5;
sth2=param*10;
}
}
Then simply use the constructor as you would use the function:
pairReturn pR = new pairReturn(15);
and you can use pR.sth1, pR.sth2 as "2 results of the function"
You also can send in mutable objects as parameters, if you use methods to modify them then they will be modified when you return from the function. It won't work on stuff like Float, since it is immutable.
public class HelloWorld{
public static void main(String []args){
HelloWorld world = new HelloWorld();
world.run();
}
private class Dog
{
private String name;
public void setName(String s)
{
name = s;
}
public String getName() { return name;}
public Dog(String name)
{
setName(name);
}
}
public void run()
{
Dog newDog = new Dog("John");
nameThatDog(newDog);
System.out.println(newDog.getName());
}
public void nameThatDog(Dog dog)
{
dog.setName("Rutger");
}
}
The result is:
Rutger
You can create a record (available since Java 14) to return the values with type safety, naming and brevity.
public record MyResult(int number1, int number2) {
}
public static MyResult something() {
int number1 = 1;
int number2 = 2;
return new MyResult(number1, number2);
}
public static void main(String[] args) {
MyResult result = something();
System.out.println(result.number1() + result.number2());
}
First, it would be better if Java had tuples for returning multiple values.
Second, code the simplest possible Pair class, or use an array.
But, if you do need to return a pair, consider what concept it represents (starting with its field names, then class name) - and whether it plays a larger role than you thought, and if it would help your overall design to have an explicit abstraction for it. Maybe it's a code hint...
Please Note: I'm not dogmatically saying it will help, but just to look, to see if it does... or if it does not.