Java TreeMap get min and max values - java

I have this kind of a TreeMap. How can i get minimum and maximum values for town temperature after i make some entries in towns? I do not copy the code where i fill in some values in towns because it works fine.
Map<String, Town> towns = new TreeMap<>();
The Town.class is like this.
public class Town {
private int temperature;
private int rainfall;
private int windPower;
private Downwind downwind;
public Town(int temperature, int rainfall, int windPower, Downwind downwind) {
this.temperature = temperature;
this.rainfall = rainfall;
this.windPower = windPower;
this.downwind = downwind;
}
public int getTemperature() {
return temperature;
}
public void setTemperature(int temperature) {
this.temperature = temperature;
}
public int getRainfall() {
return rainfall;
}
public void setRainfall(int rainfall) {
this.rainfall = rainfall;
}
public int getWindPower() {
return windPower;
}
public void setWindPower(int windPower) {
this.windPower = windPower;
}
public Downwind getDownwind() {
return downwind;
}
public void setDownwind(Downwind downwind) {
this.downwind = downwind;
}

The simplest:
Optional<Town> maybeMinTown = towns.values().stream()
.min(Comparator.comparing(Town::getTemperature));
Town minTown = maybeMinTown.get(); // throws NoSuchElementException when towns map is empty
the same for max - you only need to use max instead of min.
UPDATE
To get just temperature you can map Town to temperature and then call min/max:
final OptionalInt min = towns.values().stream()
.mapToInt(Town::getTemperature)
.min();
min.getAsInt(); // or you can call min.orElseGet(some_lowest_value)
OptionalInt is the same for int what Optional<Town> is for Town.

If you are not in Java 8, you have to order the Map by value to achieve that, maybe something like this
public static Map<String, Town> sortByValues(final Map<String, Town> map) {
Comparator<String> valueComparator = new Comparator<String>() {
public int compare(String k1, String k2) {
if (map.get(k1).getTemperature() < map.get(k2).getTemperature()) {
return 1;
} else if (map.get(k1).getTemperature() == map.get(k2).getTemperature()) {
return 0;
} else {
return -1;
}
}
};
Map<String, Town> sortedByValues = new TreeMap<String, Town>(valueComparator);
sortedByValues.putAll(map);
return sortedByValues;
}
When you create your TreeMap, pass an instance of this comparator class
towns = sortByValues(towns);
After that, you will have the maximun value at position 0 and the minimun in the last element.
UPDATE
Change the code in order to compile.

Related

TreeMap sort by value if value is an instance

I should to write a comparator that will let me sort a TreeMap by getScore in instance which is Value instead of the default natural ordering.
Earlier I have found one decision of my problem (TreeMap sort by value) but the problem has stayed. When I call e1.getValue they won't resolve methods of instance. How I can get them?
public class Trending {
Map<String, Topic> treeMap = new TreeMap<>();
void initialScore(int id, String topic, int score){
Topic object = new Topic(id, topic, score);
treeMap.put(topic, object);
}
static <String, Topic extends Comparable<Topic>>
SortedSet<Map.Entry<String,Topic>> entriesSortedByValues(Map<String,Topic> map) {
SortedSet<Map.Entry<String,Topic>> sortedEntries = new TreeSet<Map.Entry<String,Topic>>(
new Comparator<Map.Entry<String,Topic>>() {
#Override public int compare(Map.Entry<String,Topic> e1, Map.Entry<String,Topic> e2) {
int res = e1.getValue().compareTo(e2.getValue());
return res != 0 ? res : 1;
}
}
);
sortedEntries.addAll(map.entrySet());
return sortedEntries;
}
}
You could declare your entriesSortedByValues method as non generic (which would hide the Topic class, as stated at first comment of your question):
static SortedSet<Map.Entry<String, Topic>> entriesSortedByValues(Map<String, Topic> map) {
SortedSet<Map.Entry<String, Topic>> sortedEntries = new TreeSet<Map.Entry<String, Topic>>(
new Comparator<Map.Entry<String, Topic>>() {
#Override
public int compare(Map.Entry<String, Topic> e1, Map.Entry<String, Topic> e2) {
int res = e1.getValue().compareTo(e2.getValue());
return res != 0 ? res : 1;
}
}
);
sortedEntries.addAll(map.entrySet());
return sortedEntries;
}
and then make your Topic class implement Comparable:
public class Topic implements Comparable<Topic> {
private final int id;
private final String topic;
private final int score;
public Topic(int id, String topic, int score) {
this.id = id;
this.topic = topic;
this.score = score;
}
public int getScore() {
return score;
}
#Override
public int compareTo(Topic o) {
return Integer.compare(getScore(), o.getScore());
}
}

Using range values as a key in a map

Let's say that I want my application to determine user's fitness level based on some criteria.
The criteria could be something like: age, currently taking medication?, 400m run
At first I though I could create a Map where the value is the fitness level and the key is an object that has all the criteria, but since the criteria are ranges this wouldn't work.
For example:
if age is between 18 and 22 and onMedication = false and run400m = [70, 80]
fitness level = GOOD
Now if only one of the parameters is in a different range the fitness level would be different. How could I achieve this?
You can use a TreeMap class for this. There are very useful methods to deal with ranges of key values. For example:
TreeMap<Integer, String> myTreeMap = new TreeMap<>();
myTreeMap.put(10, "A");
myTreeMap.put(20, "B");
myTreeMap.put(30, "C");
myTreeMap.put(40, "D");
System.out.println(myTreeMap.floorEntry(25));
Will be print the second option (20=B). I recommend that you check the TreeMap and all its methods for this case.
Maybe you could use OOP and do something like this:
public class FitnessApp {
public static void main(String[] args) {
Map<String, Object> params = new HashMap<>();
params.put("age", 17);
params.put("onMedication", false);
System.out.printf(new FitnessLevelCalculator().calculateFor(params).name());
}
}
class FitnessLevelCalculator {
private LinkedList<FitnessLevel> fitnessLevels = new LinkedList<>();
public FitnessLevelCalculator() {
fitnessLevels.add(new FitnessLevel(FitnessLevelEnum.ATHLETIC, Arrays.asList(new RangeCriteria("age", 18, 25), new BooleanCriteria("onMedication", false))));
fitnessLevels.add(new FitnessLevel(FitnessLevelEnum.GOOD, Arrays.asList(new RangeCriteria("age", 14, 17), new BooleanCriteria("onMedication", false))));
fitnessLevels.add(new FitnessLevel(FitnessLevelEnum.ILL, Arrays.asList(new RangeCriteria("age", 16, 17))));
}
public FitnessLevelEnum calculateFor(Map<String, Object> params) {
ListIterator<FitnessLevel> listIterator = fitnessLevels.listIterator();
while (listIterator.hasNext()) {
FitnessLevel fitnessLevel = listIterator.next();
if (fitnessLevel.accept(params)) {
return fitnessLevel.getLevel();
}
}
return FitnessLevelEnum.NOT_CLASSIFIED;
}
}
enum FitnessLevelEnum {
ILL, GOOD, ATHLETIC, NOT_CLASSIFIED
}
class FitnessLevel {
private List<Criteria> criteriaList = new ArrayList<>();
private FitnessLevelEnum level;
public FitnessLevel(FitnessLevelEnum level, List<Criteria> criteriaList) {
this.criteriaList = criteriaList;
this.level = level;
}
public boolean accept(Map<String, Object> params) {
for (Criteria criteria : criteriaList) {
if (!params.containsKey(criteria.getName())) {
return false;
}
if (!criteria.satisfies(params.get(criteria.getName()))) {
return false;
}
}
return true;
}
public FitnessLevelEnum getLevel() {
return level;
}
}
abstract class Criteria<T> {
private String name;
public Criteria(String name) {
this.name = name;
}
public abstract boolean satisfies(T param);
public String getName() {
return name;
}
}
class RangeCriteria extends Criteria<Integer> {
private int min;
private int max;
public RangeCriteria(String name, int min, int max) {
super(name);
this.min = min;
this.max = max;
}
#Override
public boolean satisfies(Integer param) {
return param >= min && param <= max;
}
}
class BooleanCriteria extends Criteria<Boolean> {
private Boolean expectedValue;
public BooleanCriteria(String name, Boolean expectedValue) {
super(name);
this.expectedValue = expectedValue;
}
#Override
public boolean satisfies(Boolean param) {
return param == expectedValue;
}
}
In your specific case, I don't think it's good to use as a key in Map. There maybe a way to put the Object with all conditions as your business but it's quite complex and not worth to do that. The Interpreter Pattern may help you on this.
Hope this help.

Finding the maximum among three integers and knowing which one is chosen

I am currently working on a code that has to find the best solution. First I check whether one of the three is larger than the other two. Hence, there is a maximum that occurs only once. If there are two numbers larger than the third one, but equal to each other, I will have to compare the distance of those two and then the one with the smallest distance is chosen.
The profit functions and distances are calculated outside this method and not that important.
What I have come up with so far is to use a lot of if statements. However, I was wondering whether there would be a more efficient method to do this.
public void bestSolution(List<ROUTE> LS, List<ROUTE> SA, List<ROUTE> RR)
{
int profitLS = profitRoutes(LS);
int profitSA = profitRoutes(SA);
int profitRR = profitRoutes(RR);
int distanceLS = totalDistance(LS);
int distanceSA = totalDistance(SA);
int distanceRR = totalDistance(RR);
if ((profitLS > profitSA) & (profitLS > profitRR))
{
}
}
In case of finding max between three integers -
int mostProfit = Math.max(profitLS, Math.max(profitSA, profitRR));
Considering case - "distance of those two and then the one with the smallest distance is chosen"
class DistanceProfit{
private int profit;
private int distance;
public DistanceProfit(int profit, int distance){
this.profit = profit;
this.distance = distance;
}
}
...
//create DistanceProfit objects add to list
Collections.sort(distenceProfitList, new Comparator<DistenceProfit>{
public int compare(DistenceProfit dp1, DistenceProfit dp2){
if(dp1.getProfit()==dp2.getProfit())
return dp1.getDistance() - dp2..getDistance();
return dp1.getProfit() - dp2.getProfit();
}
});
You could create a TreeSet with the comparison results and select the 'greatest' element.
The comparison result could be something like:
public class ProfitCounter implements Comparable<ProfitCounter>
{
public ProfitCounter(List<ROUTE> route)
{
this.route = route;
profit = profitRoutes(route);
distance = totalDistance(route);
}
#Override
public int compareTo(ProfitCounter other)
{
int result;
result = profit - other.profit;
if (result == 0)
result = other.distance - distance;
return (result);
}
private List<ROUTE> route;
private int profit;
private int distance;
} // class ProfitCounter
Id use something on these lines. Does not limit u to 3 parameters.
public class RouteCalc implements Comparable<RouteCalc> {
private final List routes;
public RouteCalc(List routes) {
this.routes = routes;
}
public static int calcProfit(List routes) {
//use the list to calculate profit
return 0;
}
public static int calcDistance(List routes) {
//use the list to calculate distance
return 0;
}
#Override
public int compareTo(#NonNull RouteCalc another) {
final int profitA = calcProfit(this.routes);
final int profitB = calcProfit(another.routes);
//swap parameters to change from ascending to descending and vice-versa
final int compare = Integer.compare(profitA, profitB);
//if same profit, compare distance
if (compare == 0) {
final int distanceA = calcDistance(this.routes);
final int distanceB = calcDistance(another.routes);
return Integer.compare(distanceA, distanceB);
} else
return compare;
}
//sample usage
public static void main(String args[]) {
final List<RouteCalc> allRoutes = new ArrayList<>();
//add routes
final RouteCalc bestRoute = Collections.max(allRoutes);
}
}
static class CalculatedRoute {
public static CalculatedRoute mostProfitableOf(List<CalculatedRoute> calculatedRoutes) {
return Collections.max(calculatedRoutes, BY_PROFIT_AND_DISTANCE);
}
public static final Comparator<CalculatedRoute> BY_PROFIT_AND_DISTANCE = new Comparator<CalculatedRoute>() {
#Override
public int compare(CalculatedRoute o1, CalculatedRoute o2) {
int cmp = o2.profit - o1.profit;
if (cmp == 0) {
cmp = o1.distance - o2.distance;
}
return cmp;
}
};
private final List<ROUTE> routeList;
private final int profit;
private final int distance;
public CalculatedRoute(List<ROUTE> routeList, int profit, int distance) {
this.profit = profit;
this.distance = distance;
}
public List<ROUTE> getRouteList() {
return routeList;
}
public int getProfit() {
return profit;
}
public int getDistance() {
return distance;
}
}
public List<ROUTE> mostProfitableOf(List<ROUTE> LS, List<ROUTE> SA, List<ROUTE> RR) {
return CalculatedRoute.mostProfitableOf(Arrays.asList(
new CalculatedRoute(LS, profitRoutes(LS), totalDistance(LS)),
new CalculatedRoute(SA, profitRoutes(SA), totalDistance(SA)),
new CalculatedRoute(RR, profitRoutes(RR), totalDistance(RR))
)).getRouteList();
}

Counting Number of occurences of an element in a List

I am trying to make a list where I would add items in to my shopping cart, then at check out it would show how many occurrences of items there are by using a HashMap.
public class ShoppingCart
{
private ArrayList<Items> ShoppingCart;
public ShoppingCart()
{
ShoppingCart = new ArrayList<Items>();
}
public void addItems(Items newItems)
{
ShoppingCart.add(newItems);
}
public ArrayList<Items> getShoppingCart()
{
return ShoppingCart;
}
public void CheckOut()
{
for(int i = 0; i < ShoppingCart.size(); i++)
{
HashMap<String, Integer> itemsMap = new HashMap<String, Integer>();
int a = 1;
if (itemsMap.containsKey(ShoppingCart.get(i).getName()))
{
itemsMap.replace(ShoppingCart.get(i).getName(), a, a++);
}
else
{
itemsMap.put(ShoppingCart.get(i).getName(), a);
}
System.out.println(a +"x "+ShoppingCart.get(i));
}
}
and my items are created with
public class Items
{
private String name;
public Items (String name)
{
this.name = name;
}
public String toString()
{
return name;
}
public String getName()
{
return name;
}
}
In the Main, I would create Items, and Add them to the shopping cart, then ShoppingCart.CheckOut(); my items.
However, if I add 4 "White Bread" like so,
Items bread = new Items("White Bread");
ShoppingCart ShoppingCart = new ShoppingCart();
ShoppingCart.addItems(bread);
ShoppingCart.addItems(bread);
ShoppingCart.addItems(bread);
ShoppingCart.addItems(bread);
I get
1x White Bread
1x White Bread
1x White Bread
1x White Bread
instead of
4x White Bread
What am I doing wrong?
Change
itemsMap.replace(ShoppingCart.get(i).getName(), a, a++);
to
a = itemsMap.get(ShoppingCart.get(i).getName()); // in order to increment the
// current counter value
itemsMap.replace(ShoppingCart.get(i).getName(), a, ++a);
Using post decrement (a++) doesn't modify the value in the Map, since a++ returns the original value of a.
In addition, the HashMap should be initialized outside the loop. Otherwise it will always contain just one element.
Count them in advance and print the sum:
Map<String, Integer> itemsMap = new LinkedHashMap<String, Integer>();
for(int i = 0; i < ShoppingCart.size(); i++)
{
itemsMap.merge(ShoppingCart.get(i).getName(), 1, (old, set) -> old+set);
}
for (Map.Entry <String, Integer> e: itemsMap.entrySet ()) {
System.out.println(e.getValue ()+"x "+e.getKey ());
}
2 main problems:
- HashMap is created on each iteration, so you loose whatever you had in the map before.
- Your system out statement in within the FOR, so you'll get as many lines printed as elements you have in the loop.
Why not use A map to store the items like this?
public class ShoppingCart {
Map<Items, Integer> shoppingCart = new HashMap<>();
public ShoppingCart() {
}
public void addItems(Items newItem) {
int count = shoppingCart.containsKey(newItem) ? shoppingCart.get(newItem) : 0;
shoppingCart.put(newItem, ++count);
}
public List<Items> getShoppingCart() {
return shoppingCart.keySet()
.stream()
.flatMap(item -> Collections.nCopies(shoppingCart.get(item), item).stream())
.collect(Collectors.toList());
}
public void CheckOut() {
for (Map.Entry<Items, Integer> entry : shoppingCart.entrySet()) {
System.out.println(String.format("%dx %s", entry.getValue(), entry.getKey().getName()));
}
}
public static class Items
{
private String name;
public Items (String name)
{
this.name = name;
}
public String toString()
{
return name;
}
public String getName()
{
return name;
}
#Override
public int hashCode() {
return name.hashCode();
}
#Override
public boolean equals(Object obj) {
if(obj instanceof Items) {
return name.equals(((Items)obj).getName());
}
return false;
}
}
public static void main(String[] args) {
Items bread = new Items("White Bread");
ShoppingCart ShoppingCart = new ShoppingCart();
ShoppingCart.addItems(bread);
ShoppingCart.addItems(bread);
ShoppingCart.addItems(bread);
ShoppingCart.addItems(bread);
ShoppingCart.CheckOut();
System.out.println(ShoppingCart.getShoppingCart());
}
}
I believe your problem is that you are creating a new hashmap for every item in your shopping list, try this instead:
public void CheckOut()
{
HashMap<String, Integer> itemsMap = new HashMap<String, Integer>();
for(int i = 0; i < ShoppingCart.size(); i++)
{
if (itemsMap.containsKey(ShoppingCart.get(i).getName()))
{
itemsMap.put(ShoppingCart.get(i).getName(), itemsMap.get(ShoppingCart.get(i).getName())+1);
}
else
{
itemsMap.put(ShoppingCart.get(i).getName(), 1);
}
}
Iterator it = itemsMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pair = (Map.Entry)it.next();
System.out.println(pair.getValue()+"x " + pair.getKey() );
}
}

Find the lowest / highest key of a distributed map

I have a distributed map and I want to find the lowest or highest key (an object implementing compareable). What is the most efficient way to get those keys? I mean something like every node provides his lowest key and in the end the lowest key is the lowest of every node.
So I think:
MyObj max = Collections.max(map.keySet());
is not the most efficient way. And if I want to use
new DistributedTask<>(new Max(input), key);
I would need to now the key and therefore fetch all Keys over wire. I think in that case I could do Collections.max(map.keySet()); as well.
Hmm ... any ideas?
You could use EntryProcessor.executeOnEntries - with a stateful EntryProcessor - and then let the it do all the work for you; have each key map to a sentinel MIN and MAX enum if they are the min and max.
If you have some idea of the bounds, you could attach a filter Predicate as well to speed it up that way, too.
This map reduce solution seems to have a lot of overhead but it is the best way I could get the job done. Any better ideas are still welcome.
public static void main(String[] args) throws ExecutionException, InterruptedException {
IMap<String, Integer> map = instance.getMap("test");
JobTracker jobTracker = instance.getJobTracker( "default" );
KeyValueSource<String, Integer> source = KeyValueSource.fromMap( map );
Job<String, Integer> job = jobTracker.newJob(source);
JobCompletableFuture<Map<String, String>> future = job
.mapper(new MaxMapper())
.reducer(new MaxReducerFactory())
.submit();
System.out.println("mr max: " + future.get());
}
public static class MaxMapper implements Mapper<String, Integer, String, String> {
private volatile String max = null;
#Override
public void map(String s, Integer integer, Context<String, String> ctx) {
if (max == null || s.compareTo(max)>0) {
max = s;
ctx.emit("max", max);
}
}
}
public static class MaxReducerFactory implements ReducerFactory<String,String,String> {
#Override
public Reducer<String, String> newReducer(String s) {
return new MaxReducer();
}
private class MaxReducer extends Reducer<String, String> {
private volatile String max = null;
#Override
public void reduce(String s) {
if (max == null || s.compareTo(max)>0) max = s;
}
#Override
public String finalizeReduce() {
return max; // == null ? "" : max;
}
}
}
Mapper:
import com.hazelcast.mapreduce.Context;
import com.hazelcast.mapreduce.Mapper;
import stock.Stock;
public class MinMaxMapper implements Mapper<String, Stock, String, Double> {
static final String MIN = "min";
static final String MAX = "max";
#Override
public void map(String key, Stock value, Context<String, Double> context) {
context.emit(MIN, value.getPrice());
context.emit(MAX, value.getPrice());
}
}
Combiner:
import com.hazelcast.mapreduce.Combiner;
import com.hazelcast.mapreduce.CombinerFactory;
public class MinMaxCombinerFactory implements CombinerFactory<String, Double, Double> {
#Override
public Combiner<Double, Double> newCombiner(String key) {
return new MinMaxCombiner(MinMaxMapper.MAX.equals(key) ? true : false);
}
private static class MinMaxCombiner extends Combiner<Double, Double> {
private final boolean maxCombiner;
private double value;
private MinMaxCombiner(boolean maxCombiner) {
this.maxCombiner = maxCombiner;
this.value = maxCombiner ? -Double.MAX_VALUE : Double.MAX_VALUE;
}
#Override
public void combine(Double value) {
if (maxCombiner) {
this.value = Math.max(value, this.value);
} else {
this.value = Math.min(value, this.value);
}
}
#Override
public Double finalizeChunk() {
return value;
}
#Override
public void reset() {
this.value = maxCombiner ? -Double.MAX_VALUE : Double.MAX_VALUE;
}
}
}
Reducer:
import com.hazelcast.mapreduce.Reducer;
import com.hazelcast.mapreduce.ReducerFactory;
public class MinMaxReducerFactory implements ReducerFactory<String, Double, Double> {
#Override
public Reducer<Double, Double> newReducer(String key) {
return new MinMaxReducer(MinMaxMapper.MAX.equals(key) ? true : false);
}
private static class MinMaxReducer extends Reducer<Double, Double> {
private final boolean maxReducer;
private volatile double value;
private MinMaxReducer(boolean maxReducer) {
this.maxReducer = maxReducer;
this.value = maxReducer ? -Double.MAX_VALUE : Double.MAX_VALUE;
}
#Override
public void reduce(Double value) {
if (maxReducer) {
this.value = Math.max(value, this.value);
} else {
this.value = Math.min(value, this.value);
}
}
#Override
public Double finalizeReduce() {
return value;
}
}
}
Returns two elements map with min and max:
ICompletableFuture<Map<String, Double>> future =
job.mapper(new MinMaxMapper())
.combiner(new MinMaxCombinerFactory())
.reducer(new MinMaxReducerFactory())
.submit();
Map<String, Double> result = future.get();
Why don't you create an ordered index? Although I'm not quite sure if it currently is possible to find a maximum value using a predicate and once found, abort the evaluation of the predicate.

Categories

Resources