Convert a HashMap<Integer, Long> ito HashMap<Integer, Integer> - java

Collectors.counting() returns long values for each key in this method:
private static Map<Integer, Long> countDuplicates(HashSet<Card> cards) {
return cards.stream().collect(Collectors.groupingBy(Card::getRankNumber, Collectors.counting()));
}
Is there a way to cast or convert the resulting Map from Map<Integer, Long> to Map<Integer, Integer>?
Direct casting gives this exception:
Type mismatch: cannot convert from Map<Integer,Integer> to Map<Integer,Long>
Note: The implementation of my class guarantees that cards has five objects in it, so there is no chance of overflow.

Try the following:
private static Map<Integer, Integer> countDuplicates(HashSet<Card> cards) {
return cards.stream()
.collect(Collectors.groupingBy(Card::getRankNumber, Collectors.summingInt(x -> 1)));
}
Instead of Collectors.counting() use Collectors.summingInt(x -> 1) so that you get immediately the value as an Integer.

Another way to get Map<Integer, Integer> is to use toMap collector along with Integer::sum method reference as a merge function:
private static Map<Integer, Integer> countDuplicates(HashSet<Card> cards) {
return cards.stream()
.collect(Collectors.toMap(Card::getRankNumber, x -> 1, Integer::sum));
}

Both the Answer by dreamcrash and the Answer by Alex Rudenko seem correct. Here is example code I wrote to try both approaches.
The Card class.
package work.basil.example;
import java.util.Objects;
public class Card
{
public enum Suit
{
CLUB, DIAMOND, HEART, SPADE
}
// -------------| Member fields |-----------------------
private Suit suit;
private String name;
private Integer rankNumber;
// -------------| Constructors |-----------------------
public Card ( Suit suit , String name , Integer rankNumber )
{
Objects.requireNonNull( suit );
Objects.requireNonNull( name );
Objects.requireNonNull( rankNumber );
this.suit = suit;
this.name = name;
this.rankNumber = rankNumber;
}
// -------------| Getters |-----------------------
public String getName ( ) { return this.name; }
public Integer getRankNumber ( ) { return this.rankNumber; }
// -------------| Object methods |-----------------------
#Override
public String toString ( )
{
return "Card{ " +
"suit=" + suit +
" | name='" + name + '\'' +
" | rankNumber=" + rankNumber +
" }";
}
#Override
public boolean equals ( Object o )
{
if ( this == o ) return true;
if ( o == null || getClass() != o.getClass() ) return false;
Card card = ( Card ) o;
return suit == card.suit && getName().equals( card.getName() ) && getRankNumber().equals( card.getRankNumber() );
}
#Override
public int hashCode ( )
{
return Objects.hash( suit , getName() , getRankNumber() );
}
}
In Java 16 and later, we could reduce that Card class to a brief record. And in the code further down we would change getRankNumber to the implicit getter method rankNumber. The Suit enum would move to its own .java file.
package work.basil.example.cardgame;
public record Card( Suit suit , String name , Integer rankNumber )
{
}
The CastingStream class to collect those cards.
package work.basil.example;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
public class CastingStream
{
public static void main ( String[] args )
{
CastingStream app = new CastingStream();
app.demo();
}
private void demo ( )
{
Set < Card > cards = Set.of(
new Card( Card.Suit.HEART , "Ten" , 10 ) ,
new Card( Card.Suit.SPADE , "Ten" , 10 ) ,
new Card( Card.Suit.HEART , "Queen" , 12 )
);
// Code taken from Question. https://stackoverflow.com/q/65910153/642706
Map < Integer, Long > countDuplicatesLong =
cards
.stream()
.collect(
Collectors.groupingBy(
Card :: getRankNumber ,
Collectors.counting()
)
);
// Code taken from Answer by dreamcrash. https://stackoverflow.com/a/65910215/642706
Map < Integer, Integer > countDuplicatesIntegerBySummingInt =
cards
.stream()
.collect(
Collectors.groupingBy(
Card :: getRankNumber ,
Collectors.summingInt( x -> 1 )
)
);
// Code taken from Answer by Alex Rudenko. https://stackoverflow.com/a/65910400/642706
Map < Integer, Integer > countDuplicatesIntegerBySumMethodReference =
cards
.stream()
.collect(
Collectors.toMap( Card :: getRankNumber ,
x -> 1 ,
Integer :: sum
)
);
System.out.println( "cards = " + cards );
System.out.println( "countDuplicatesLong = " + countDuplicatesLong );
System.out.println( "countDuplicatesIntegerBySummingInt = " + countDuplicatesIntegerBySummingInt );
System.out.println( "countDuplicatesIntegerBySumMethodReference = " + countDuplicatesIntegerBySumMethodReference );
}
}
When run.
cards = [Card{ suit=HEART | name='Queen' | rankNumber=12 }, Card{ suit=HEART | name='Ten' | rankNumber=10 }, Card{ suit=SPADE | name='Ten' | rankNumber=10 }]
countDuplicatesLong = {10=2, 12=1}
countDuplicatesIntegerBySummingInt = {10=2, 12=1}
countDuplicatesIntegerBySumMethodReference = {10=2, 12=1}

Related

Multiplying two objects

Hey so I will do my best at explaining this (and will have to change some code sorry if it looks weird) so I currently have:
public Job(String name, int tickets, double wage) {
this.name = name;
this.tickets = tickets;
this.wage = wage;
& and getters and setters for these and also making a list here:
Job Peter = new Job (name: "Peter", tickets: 100, wage: 1.55);
System.out.println(Peter);
So my issue is that lets say the total wage is calculated by tickets * wage how do I go about doing this by automation.
I can do this, however want a simpler method where it is automated so I can just for example Peter.getTotalWage
System.out.println(Peter.getTickets() * (Peter.getWage()));
My issue is when it comes to multiplying int with double, I've tried something like this but can't seem to get it working as I know it is wrong (probably completely wrong to be honest):
}
public double totalWage(int tickets, double wage) {
int i = tickets;
Double d2=Double.valueOf(i);
double sum = d2 * wage;
return sum;
}
Right now I have two classes.
If you can help us identify how to go about doing this that'll be great!
tl;dr
Use BigDecimal for money.
Organize your classes properly. Apparently you have:
An employee with a wage rate
A job whose cost is the number of tickets multiplied by the assigned employee's wage rate.
So you need an Employee class with a member variable for wage rate. And you need a Job class with an assigned Employee object, and a getWageCost method with this logic:
BigDecimal cost =
this.employee // An `Employee` object is assigned to this `Job` object.
.getWageRate() // The `Job` object asks the `Employee` object for its current wage rate.
.multiply(
new BigDecimal( this.countOfTickets )
)
.setScale( 2 , RoundingMode.HALF_EVEN ) // Round to the penny using Banker’s Rounding.
;
Tip to students: Notice how working towards a simple clear problem description written down in plain English can lead naturally to a proper class design. Lesson learned: Prose before code.
BigDecimal
For money, never use double or Double, float or Float. The floating-point types trade away accuracy for speed of execution. Use BigDecimal for fractional money.
BigDecimal wageRate = new BigDecimal( "1.55" ) ;
Multiply.
BigDecimal cost = this.employee.getWageRate().multiply( new BigDecimal( this.countOfTickets ) ) ;
But that line above fails to address the fractional penny (assuming this is United States dollars). So round to the penny or to the tenth or hundredth of a penny as directed by your accounting staff.
Round by calling the setScale method. Use the rounding method as directed by your accounting staff. Often in business matters that would be Banker's Rounding. So to the line above we should add a call to setScale with the number of digits (2 for whole pennies of US Dollar) and the typing of rounding.
BigDecimal cost = this.employee.getWageRate().multiply( new BigDecimal( this.countOfTickets ) ).setScale( 2 , RoundingMode.HALF_EVEN ) ;
The code above is logic on the Job class. Notice how it reaches into the Employee object to obtain the current wage rate for that worker.
Example code
Here is complete example code.
Employee alice = new Employee( "Alice" , "EMP00013" , new BigDecimal( "1.55" ) );
Job whateverJob = new Job( alice , 100 , "whatever" , UUID.randomUUID() );
BigDecimal wagesCostForWhateverJob = whateverJob.getWageCost();
System.out.println( "whateverJob: " + whateverJob + " costs " + wagesCostForWhateverJob );
whateverJob: Job{ employee=Employee{ name='Alice' | employeeId='EMP00013' | wageRate=1.55 } | countOfTickets=100 | description='whatever' | id=b4e397b3-e02e-42be-b117-622cee96a192 } costs 155.00
Employee.java
package work.basil.example;
import java.math.BigDecimal;
import java.util.Objects;
public class Employee
{
// ---------| Member vars |----------------------------------
private String name, employeeId;
private BigDecimal wageRate;
// ---------| Constructors |----------------------------------
public Employee ( String name , String employeeId , BigDecimal wageRate )
{
this.name = Objects.requireNonNull( name );
this.employeeId = Objects.requireNonNull( employeeId );
this.wageRate = Objects.requireNonNull( wageRate );
}
// ---------| Accessors |----------------------------------
public String getName ( )
{
return name;
}
public String getEmployeeId ( )
{
return employeeId;
}
public BigDecimal getWageRate ( )
{
return wageRate;
}
// ---------| Object |----------------------------------
#Override
public boolean equals ( Object o )
{
if ( this == o ) return true;
if ( o == null || getClass() != o.getClass() ) return false;
Employee employee = ( Employee ) o;
return getEmployeeId().equals( employee.getEmployeeId() );
}
#Override
public int hashCode ( )
{
return Objects.hash( getEmployeeId() );
}
#Override
public String toString ( )
{
return "Employee{ " +
"name='" + name + '\'' +
" | employeeId='" + employeeId + '\'' +
" | wageRate=" + wageRate +
" }";
}
}
Job.java
package work.basil.example;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Objects;
import java.util.UUID;
public class Job
{
// ---------| Member vars |----------------------------------
private Employee employee;
private Integer countOfTickets;
private String description;
private UUID id;
// ---------| Constructors |----------------------------------
public Job ( Employee employee , Integer countOfTickets , String description , UUID id )
{
this.employee = employee;
this.countOfTickets = countOfTickets;
this.description = description;
this.id = id;
}
// ---------| Accessors |----------------------------------
public Employee getEmployee ( )
{
return employee;
}
public Integer getCountOfTickets ( )
{
return countOfTickets;
}
public String getDescription ( )
{
return description;
}
public UUID getId ( )
{
return id;
}
public BigDecimal getWageCost ( )
{
BigDecimal cost = this.employee.getWageRate().multiply( new BigDecimal( this.countOfTickets ) ).setScale( 2 , RoundingMode.HALF_EVEN );
return cost;
}
// ---------| Object |----------------------------------
#Override
public boolean equals ( Object o )
{
if ( this == o ) return true;
if ( o == null || getClass() != o.getClass() ) return false;
Job job = ( Job ) o;
return getId().equals( job.getId() );
}
#Override
public int hashCode ( )
{
return Objects.hash( getId() );
}
#Override
public String toString ( )
{
return "Job{ " +
"employee=" + employee +
" | countOfTickets=" + countOfTickets +
" | description='" + description + '\'' +
" | id=" + id +
" }";
}
}
In real work, if Job::getWageCost would be called very very often, we might consider caching the result. We could add a member variable to hold the result. But avoid falling into the trap of premature optimization.
I played around with the code a bit; maybe this helps?
public class Main {
public static void main(String[] args){
Job jobOne = new Job (100, 1.55);
Person p1 = new Person("Peter Williams");
double jobOnePay = jobOne.getTotal();
p1.setWage(jobOnePay);
System.out.println(p1.getName() + " gets paid: " + p1.getSetWage());
}
}
class Job{
private int tickets;
private double wage;
Job(int tickets, double wage) {
this.tickets = tickets;
this.wage = wage;
}
double getTotal(){
return tickets*wage;
}
}
class Person{
private String name;
private double setWage;
Person(String name){
this.name = name;
}
String getName() {
return name;
}
void setWage(double setWage) {
this.setWage = setWage;
}
double getSetWage() {
return setWage;
}
}
Output: "Peter Williams gets paid: 155.0"

Refactoring Java code to make it reusable

I recently created a simple method that takes a HashMap and LinkedList as arguments. It iterates through the HashMap, find any two entries that follows these rules:
Sum of keys of this entries must be divisible by 100
Sum of keys must be less than 1000
If the entry with the same value appears more than once the case should be skipped
Pairs that follows these rules are added to the LinkedList. It looks like this:
private static void compare2(HashMap<Integer,String> values,List<String>results){
if (values.size()>1) {
for(HashMap.Entry<Integer,String> entry1:values.entrySet()){
for (HashMap.Entry<Integer,String> entry2:values.entrySet()){
if (entry1.getValue().equals(entry2.getValue()))continue;
if ((entry1.getKey() + entry2.getKey())%100 == 0 && (entry1.getKey() + entry2.getKey())<1000){
results.add(entry1.getKey() + "+" + entry2.getKey() + "=" + entry1.getKey() + entry2.getKey());
results.add(entry1.getValue());
results.add(entry2.getValue());
}
}
}
}
}
Now I wanted to create similar method that finds 3 entries that follows the same rules. Problem is that I would like to reuse existing code instead of copy/pasting this and modyfiyng, and I can't seem to find a way to do that. I don't mind if I need to change my method as long as the result is the same.
You could make the amount of numbers a parameter: N.
You could stay using for loops, but an alternative example approach could be to refactor your method with lambda's and streams as follows:
List<List<Map.Entry<Integer, String>>> compareN(HashMap<Integer, String> map, int n) {
return map.entrySet().stream()
.map(entry -> listOfNAccompanyingEntriesThatSatisfyTheConditions(entry, emptyList(), map, n - 1))
.filter(list -> !list.isEmpty())
.collect(toList());
}
Where the method listOfNAccompanyingEntriesThatSatisfyTheConditions is a recursive method:
private List<Map.Entry<Integer, String>>
listOfNAccompanyingEntriesThatSatisfyTheConditions(Map.Entry<Integer, String> newEntry,
List<Map.Entry<Integer, String>> selectedEntries,
HashMap<Integer, String> originalMap,
int n) {
List<Map.Entry<Integer, String>> newSelectedEntries = join(newEntry, selectedEntries);
if (n == 0) return satisifiesCondition(newSelectedEntries) ? selectedEntries : emptyList();
return originalMap.entrySet().stream()
.filter(entry -> !selectedEntries.contains(entry) && !entry.equals(newEntry))
.map(entry -> listOfNAccompanyingEntriesThatSatisfyTheConditions(entry, newSelectedEntries, originalMap, n - 1))
.flatMap(Collection::stream)
.collect(toList());
}
For every n, the method takes another shot at the original full list to accumulate a sublist that might fulfil the requirements. If the amount of numbers is reached (n==0), the recursion stops and the stop condition is verified:
private static boolean satisifiesCondition(List<Map.Entry<Integer, String>> entries) {
int sum = sumOfTheKeysFrom(entries);
return sum % 100 == 0 && sum < 1000;
}
However, this approach is really an exact translation of your implementation and has still the same couple of issues. For example, if you run it for
HashMap<Integer, String> map = new HashMap<Integer, String>() {{
put(1, "fred");
put(2, "anja");
put(24, "tom");
put(45, "eddy");
put(22, "lenny");
put(77, "tommy");
put(55, "henry");
put(43, "alfred");
}};
You will yield double results and results with the same entry twice, such as:
[1=fred, 22=lenny, 1=fred, 77=tommy] [2=anja, 55=henry, 2=anja,
43=alfred]
These are however easily solved by some small tweaks.
If you are not familiar with streams, lambda's or recursion, I'd suggest you implement the same with the imperative approach. Also be aware I did not care about performance in my example code, as I perceived this question as some kind of exercise.
Try something like this:
#SafeVarargs
private static void check( final List<String> results,
final Map.Entry<Integer, String>... vals )
{
int sum = 0;
for ( final Map.Entry<Integer, String> val : vals )
{
final Integer key = val.getKey();
sum += null == key ? 0 : key;
}
if ( sum < 1000 && 0 == ( sum % 100 ) )
{
final StringBuilder result = new StringBuilder( 200 );
for ( final Map.Entry<Integer, String> val : vals )
{
result.append( " + " ).append( val.getKey() );
}
results.add( result.append( " = " ).append( sum ).substring( 3 ) );
for ( final Map.Entry<Integer, String> val : vals )
{
results.add( val.getValue() );
}
}
}
private static void compare2( final HashMap<Integer, String> values,
final List<String> results )
{
if ( values.size() > 1 )
{
for ( final HashMap.Entry<Integer, String> entry1 : values.entrySet() )
{
for ( final HashMap.Entry<Integer, String> entry2 : values.entrySet() )
{
if ( entry1 == entry2 )
continue;
check( results, entry1, entry2 );
}
}
}
}
private static void compare3( final HashMap<Integer, String> values,
final List<String> results )
{
if ( values.size() > 2 )
{
for ( final HashMap.Entry<Integer, String> entry1 : values.entrySet() )
{
for ( final HashMap.Entry<Integer, String> entry2 : values.entrySet() )
{
for ( final HashMap.Entry<Integer, String> entry3 : values.entrySet() )
{
if ( entry1 == entry2 || entry1 == entry3 || entry2 == entry3 )
continue;
check( results, entry1, entry2, entry3 );
}
}
}
}
}

Get minimum and maximum from a list of objects

I have this class
class TimeSpentStats{
int manId;
String sessionId;
int userId;
Long timeStamp;
}
I have a List and I want to get the minimum timestamp and maximum
from the list for each (manId, sessionId, userId)
So for example, I have:
manId sessionId userId timeStamp
1 01F 5 1000
1 01F 5 1005
3 6Y 3 7
3 6Y 3 16
I need for (1 01F 5) -> min = 1000 , max = 1005
and for (3 6Y 3 ) - > min = 7 , max = 16
I need to add maybe 2 attributes in the same class?
Any ideas if I can do this? Thank you
If you have a List of TimeSpentStatus called list, the following algorithm should do what you want it to do.
HashMap<String, Pair> statsList = new HashMap<String, Pair>();
for(TimeSpentStats stats : list){
// Constructs the combination of IDs that is used as the key to a Pair object
String statsStr = stats.manId + " " + stats.sessionId + " " + stats.userId;
if(statsList.containsKey(statsStr)){
// Update min and/or max time for the current combination as necessary
statsList.get(statsStr).minTime = Math.min(statsList.get(statsStr).minTime, stats.timeStamp);
statsList.get(statsStr).maxTime = Math.max(statsList.get(statsStr).maxTime, stats.timeStamp);
}else{
// Construct a new Pair for the ID combination and add max and min times
Pair p = new Pair();
p.maxTime = stats.timeStamp;
p.minTime = stats.timeStamp;
// Adds the new combination to the HashMap, which can now be updated in the if-statement
statsList.put(statsStr, p);
}
}
statsList will now contain the max and min times for each combination with (userID + " " + manID + " " + sessionID) as the keys. You will then be able to get the Pair object for a specific combination by using statsList.get(userId + " " + manId + " " + sessionId) (as long as it exists of course).
Here is the Pair class
class Pair{
public long minTime;
public long maxTime;
}
public class Main
{
public static void main( String[] args )
{
Map< TimeSpentStats, MinMax > hashMap = new HashMap< TimeSpentStats, MinMax >();
addToMap( hashMap, new TimeSpentStats( 1, "01F", 5, 1000L ) );
addToMap( hashMap, new TimeSpentStats( 1, "01F", 5, 1005L ) );
addToMap( hashMap, new TimeSpentStats( 3, "6Y", 3, 7L ) );
addToMap( hashMap, new TimeSpentStats( 3, "6Y", 3, 16L ) );
for ( Map.Entry< TimeSpentStats, MinMax > entry : hashMap.entrySet() )
{
TimeSpentStats timeSpentStats = entry.getKey();
MinMax minMax = entry.getValue();
System.out.println( timeSpentStats.getManId() + "\t" + timeSpentStats.getSessionId() + "\t" + timeSpentStats.getUserId() + "\tMin Time Stamp :" + minMax.getMin() + "\tMax Time Stamp :" + minMax.getMax() );
}
}
private static void addToMap( Map< TimeSpentStats, MinMax > hashMap, TimeSpentStats timeSpentStats )
{
MinMax timeStampMinMax = hashMap.get( timeSpentStats );
if ( timeStampMinMax != null )
timeStampMinMax.updateValues( timeSpentStats.getTimeStamp() );
else
hashMap.put( timeSpentStats, new MinMax( timeSpentStats.getTimeStamp() ) );
}
}
class MinMax
{
private Long min;
private Long max;
MinMax( Long timeStamp )
{
this.min = timeStamp;
this.max = timeStamp;
}
public Long getMin()
{
return min;
}
public Long getMax()
{
return max;
}
public boolean updateValues( Long timeStamp )
{
if ( timeStamp < this.min )
{
this.min = timeStamp;
return true;
}
else if ( timeStamp > this.max )
{
this.max = timeStamp;
return true;
}
return false;
}
}
class TimeSpentStats
{
private final int manId;
private final String sessionId;
private final int userId;
private final Long timeStamp;
public TimeSpentStats( int manId, String sessionId, int userId, Long timeStamp )
{
this.manId = manId;
this.sessionId = sessionId;
this.userId = userId;
this.timeStamp = timeStamp;
}
public int getManId()
{
return manId;
}
public String getSessionId()
{
return sessionId;
}
public int getUserId()
{
return userId;
}
public Long getTimeStamp()
{
return timeStamp;
}
#Override
public boolean equals( Object obj )
{
if ( obj instanceof TimeSpentStats )
{
TimeSpentStats timeSpentStats = (TimeSpentStats)obj;
return this.manId == timeSpentStats.manId && this.sessionId.equals(timeSpentStats.sessionId) && this.userId == timeSpentStats.userId;
}
return false;
}
#Override
public int hashCode()
{
return sessionId.hashCode();
}
}
Edit : a small bug is fixed. Here I had forgot to use .equals(), as the sessionId you have mentioned is of String type.
If the elements of the triple are independent, then this is a problem in combinatorics as much as anything else: you need to find all of the triples. That's a pretty well-described problem, and there are recursive solutions that Java can handle, though of course you have to watch the stack if the problem gets large. If the elements of the triple are dependent, then life is easier.
In any case, the brute force approach is obvious: iterate over the comparable items and compare item to max and compare item to min. Record the max and the min.
If these are in an object, then you can make a nested HashMap, for example mapping manIds to (maps of sessionIds to (maps of Userids to (max,min))) where max, min might be the object which contains the max/min value or they might be the values themselves - that's up to you and what you need.
This map would be a static member of the class that these instantiate, essentially you'd be caching the maxen and the mins as the objects are created. Not too hard to see how this would go into the constructor. This adds some overhead as the problem gets large, but it spares you a lot of iteration, so it's probably a worthwhile tradeoff.

Java N-Level Nested Generics

I'm wondering if there's anything that allows N-Level nested generics?
Suppose I have
class Example< A > {
...
}
Is there any trick to get
Example<Example<Example<Example<Example<Example<...A...>>>>>>>
when the program is running (i.e. not hardcoding it)?
The reason I'm wondering about this is because I parameterized the coefficients of a univariate Polynomial class - so I had Polynomial< C >, where C is the type of coefficient, and C could be Integer, Rational, Polynomial (this would solve multivariate Polynomial issues), or a lot of other things. Having
Polynomial< Polynomial< Integer > >
for two variable polynomials might be okay, but if it gets to an arbitrary number of variables, I'd be in trouble. I realize this is probably bad design. Is there any way to resolve this issue (aside from restart)?
Thanks for the help!
There actually is a way to approach this problem using parametric recursion. It doesn't give you exactly N nested parametrization, but it addresses the problem of having to type
Example< Example < Example< ... ... > > > >
I stumbled upon an article on this topic today: http://www.cs.ubbcluj.ro/~studia-i/2011-4/02-Niculescu.pdf
Skip down to section 4 for the part about Java Generics. Below is what I got from the article's discussion on parametric recursion, and a coding experiment I tried.
I never thought about trying something like this:
public interface RingElem< T extends RingElem< T > > {
public abstract RingElem< T > add( RingElem< T > e );
public abstract RingElem< T > multiply( RingElem< T > e );
}
I didn't realize you could parametrize an interface with something that extended "itself." Now, if you have a RingElem object, then it would be parametrized by T, which is of type RingElem. By recursion, you pretty much have RingElem< RingElem < RingElem < ... > > >, but it's "infinitely" nested.
And now, you can implement the RingElem interface to create your own Polynomial class:
public class Polynomial implements RingElem< Polynomial > {
///...
}
And if you have a Polynomial object, it is parametrized as Polynomial< Polynomial< ... ... > >
When I thought about this in my head, I was skeptical that you could possibly initialize such an object. But once I started coding, it seemed simple enough. Below is are 3 classes I "hacked" together that allow one to manipulate multivariate monomials. I'll rewrite with better design (I should really be using public interface Monomial< T extends Polynomial< T > > where T is the type of coefficient for the Monomial) and add comments if I get the time.
As the example demonstrates, you can get a multivariate monomial that uses every letter of the alphabet without having to type Monomial< Monomial< Monomial< Monomial< ... ... > > > 26 times.
Monomial class: (e.g. 5x^2, 4a^-5)
public class Monomial implements RingElem< Monomial > {
private Monomial m_coefficient;
private Monomial m_base;
private Monomial m_exponent;
public Monomial() {
}
public Monomial( Monomial coefficient , Monomial base , Monomial exponent ) {
if ( base.m_coefficient != null ) {
this.m_coefficient = coefficient.multiply( base.m_coefficient );
if ( base.m_base != null ) {
this.m_coefficient = coefficient.multiply( base.m_base );
}
}
else {
this.m_coefficient = coefficient;
}
this.m_exponent = exponent;
this.m_base = base;
}
#Override
public Monomial add( RingElem< Monomial > e ) {
if ( e instanceof Monomial ) {
Monomial addend = (Monomial) e;
if ( this.m_base.equals( addend.m_base ) && this.m_exponent.equals( addend.m_exponent ) ) {
Monomial rtn = new Monomial( this.m_coefficient.add( addend.m_coefficient ) , this.m_base , this.m_exponent );
return rtn;
}
else {
throw new RuntimeException( "Addition would produce a polynomial, which is not implemented yet!" );
}
}
else {
throw new RuntimeException( "Operation not defined" );
}
}
#Override
public Monomial multiply( RingElem< Monomial > e ) {
if ( e instanceof Constant ) {
Constant c = ( Constant ) e;
return new Monomial( this.m_coefficient.multiply( c ) , this.m_base , this.m_exponent );
}
else if ( e instanceof Monomial ) {
Monomial m = ( Monomial ) e;
return new Monomial( this.m_coefficient.multiply( m.m_coefficient ).multiply( m.m_base ) , this.m_base , this.m_exponent.add( m.m_exponent ));
}
else {
throw new RuntimeException( "Operation not defined" );
}
}
#Override
public String toString() {
if ( this.m_coefficient == null || this.m_base == null || this.m_exponent == null ) {
return "<error>";
}
return this.m_coefficient.toString() + this.m_base.toString() + "^" + this.m_exponent.toString();
}
final public static void main( String[] args ) {
String[] letters = { "a" , "b" , "c" , "d" , "e" , "f" , "g" , "h" , "i" , "j" , "k" , "l" , "m" , "n" , "o" , "p" , "q" , "r" , "s" , "t" , "u" , "v" , "w" , "x" , "y" , "z" };
Variable[] v = new Variable[ letters.length ];
for ( int i=0 ; i<letters.length ; i++ ) {
v[ i ] = new Variable( letters[ i ] );
}
Monomial coefficient = new Constant( 1.2 );
Monomial exponent = new Constant( 4.5 );
Monomial test = new Monomial( coefficient , v[ 0 ] , exponent );
for ( int i=1 ; i<letters.length ; i++ ) {
test = test.multiply( new Constant( 1.2 ) );
test = new Monomial( test , v[ i ] , new Constant( (int)(Math.random() * 25 )) );
}
System.out.println( test.toString() );
}
#Override
public boolean equals( Object o ) {
if ( o instanceof Monomial ) {
Monomial m = ( Monomial ) o;
return this.m_coefficient.equals( m.m_coefficient ) && this.m_base.equals( m.m_base ) && this.m_exponent.equals( m.m_exponent );
}
else {
return false;
}
}
}
Constant class: (e.g. 1, 2, 3.14159, -10000)
public class Constant extends Monomial {
private double m_val;
public Constant( double val ) {
super();
this.m_val = val;
}
#Override
public Constant add ( RingElem e ) {
if ( e instanceof Constant ) {
Constant c = ( Constant ) e;
return new Constant( this.m_val + c.m_val );
}
else if ( e instanceof Variable ) {
throw new RuntimeException( "Operation would create a polynomial, which is not defined yet!" );
}
else {
throw new RuntimeException( "Operation not defined" );
}
}
#Override
public Monomial multiply( RingElem e ) {
if ( e instanceof Constant ) {
Constant c = ( Constant ) e;
return new Constant( this.m_val * c.m_val );
}
else if ( e instanceof Variable ) {
return new Monomial( this.clone() , (Variable) e , new Constant( 1 ) );
}
else {
throw new RuntimeException( "Operation not defined" );
}
}
#Override
public String toString() {
return String.valueOf( this.m_val );
}
#Override
public boolean equals( Object o ) {
if ( o instanceof Constant ) {
Constant c = ( Constant ) o;
return this.m_val == c.m_val;
}
else {
return false;
}
}
#Override
public Constant clone() {
return new Constant( this.m_val );
}
}
Variable class: (e.g. x, y, z)
public class Variable extends Monomial {
final private String m_varName;
public Variable( String varName ) {
this.m_varName = varName;
}
#Override
public Monomial add( RingElem e ) {
if ( e instanceof Variable ) {
if ( e.equals( this ) ) {
return new Monomial( new Constant( 2 ) , this , new Constant( 1 ) );
}
else {
throw new RuntimeException( "Operation would create a polynomial, which is not defined yet!" );
}
}
else if ( e instanceof Monomial ) {
throw new RuntimeException( "Operation would create a polynomial, which is not defined yet!" );
}
else {
throw new RuntimeException( "operation not defined!" );
}
}
#Override
public Monomial multiply( RingElem e ) {
if ( e instanceof Constant ) {
Constant c = ( Constant ) e;
return c.multiply( this );
}
if ( e instanceof Variable ) {
return new Monomial( (Variable) e , this , new Constant( 1 ) );
}
else {
return this.multiply( e );
}
}
#Override
public String toString() {
return this.m_varName;
}
#Override
public boolean equals( Object o ) {
if ( o instanceof Variable ) {
Variable v = ( Variable ) o;
return this.m_varName.equals( v.m_varName );
}
else {
return false;
}
}
}

Java JTable: Setting a primary column to always sort by first

I'm using a JTable with say 3 columns, displaying information about files and folders:
Col1: Either "file" or "folder" (String)
Col2: Name of the file or folder (String)
Col3: Creation date (Timstamp)
I can easily sort the table by any column. But I'm not able to achieve the following desired behavior:
No matter what column header the user clicks, folders should always display before files. So basically sorting by Col1 in any order other than descending should not be possible. Also, if sorted by Col3, any folders should still display before files, regardless of their timestamp.
Any hint to how to do this is greatly appreciated.
Yours
Andreas
Try to use next code it helps you:
public class Example extends JFrame {
public Example() {
DefaultTableModel defaultTableModel = new DefaultTableModel(new Object[][]{{"1"},{"3"},{"2"}},new Object[]{"text"});
JTable t = new JTable(defaultTableModel);
TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(defaultTableModel);
sorter.setComparator(0, new Comparator<Object>() {
#Override
public int compare(Object arg0, Object arg1) {
return arg0.toString().compareTo(arg1.toString());
}
});
t.setRowSorter(sorter);
add(new JScrollPane(t));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
public static void main(String...strings ){
Example e = new Example();
}
}
Here I set custom Comparator to TableRowSorter for column 0. You can compare your rows in different ways with your own Comparator implementation. Try it!
Comparator
RowSorter tutorial
Maybe this is a way to do it. There's probably a more elegant way to do it around.
The DefaultRowSorter Comparator only compares values for one column at a time, without additional information it alone can not make sure that one column in the set of columns (say column 1) must always be the first column to sort by.
If you mess with DefaultRowSorter.toggleSortOrder and add a new SortKey to keys at position 0 before setSortKeys gets called (this would be the primary column you always want to sort by first [column 1]), the ascending and descending arrows displayed in the column headers constantly mark column 1 (rightfully so), but this is not what the user expects if she clicks on column 2.
Another approach is to have Comparator know about column sort relationships. An easy way is to add information about sort precedences to the Objects for Comparator.compare. Simple precedences can be represented by a prefix String (for example: prefix "a" for column 1 and prefix "b" for all other columns).
PrefixedData can contain String, Boolean, Timestamp or null values and a prefix:
public class PrefixedData {
private String sData=null, prefix="";
private Timestamp tData=null;
private Boolean bData=null;
public void setData(String data) {
sData=data;
}
public void setData(Timestamp data) {
tData=data;
}
public void setData(boolean data) {
bData=data;
}
public void setPrefix(String prefix) {
this.prefix=prefix;
}
public String getPrefix() {
return prefix;
}
public Object getData() {
if(sData!=null) return sData;
if(tData!=null) return tData;
if(bData!=null) return bData;
return null;
}
}
JTable cells can be rendered like this for String, Boolean and Timestamp, if your table model returns PrefixedData for DefaultTableModel.getValueAt:
private static class PrefixedDataRenderer3 extends JLabel implements TableCellRenderer {
private static final SimpleDateFormat formatter = new SimpleDateFormat("MM.yyyy HH:mm:ss");
private static final JCheckBox box = new JCheckBox();
public PrefixedDataRenderer() {
setOpaque(true);
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
remove(box);
PrefixedData p=(PrefixedData) value;
if(p.getData()==null) return this;
if(p.getData().getClass().equals(String.class))
{
setText((String)p.getData());
return this;
}
if(p.getData().getClass().equals(Timestamp.class))
{
setText(formatter.format((Timestamp)p.getData()));
return this;
}
if(p.getData().getClass().equals(Boolean.class))
{
box.setSelected(((Boolean)p.getData()).booleanValue());
add(box);
}
return this;
}
}
And finally the Comparator can decide the order of things:
Comparator<Object> PrefixedDataComparator = new Comparator<Object>() {
private List<? extends SortKey> sortKeys;
#Override
public int compare(Object o1, Object o2) {
PrefixedData p1=(PrefixedData) o1;
PrefixedData p2=(PrefixedData) o2;
//First: compare prefixes (precedence data)
int prefixResult=p1.getPrefix().compareTo(p2.getPrefix());
sortKeys = tableOU.getRowSorter().getSortKeys();
//The prefixes are not the same, so return the result of the prefix comparision
//The result has to be inverted if we're sorting in descending order
//for the column the user has clicked
if(prefixResult!=0) return (sortKeys.get(0).getSortOrder().equals(SortOrder.ASCENDING) ? prefixResult : -prefixResult );
//Only if the prefixes are the same do we have to compare the payload data
//Try to impose an order for null
if(p1.getData()==null && p2.getData()!=null) return -1;
if(p1.getData()==null && p2.getData()==null) return 0;
if(p1.getData()!=null && p2.getData()==null) return 1;
//Objects compared are identical
if(p1.getData().equals(p2.getData())) return 0;
//Compare String
if(p1.getData().getClass().equals(String.class)) {
String s1=(String) p1.getData();
String s2=(String) p2.getData();
return s1.toLowerCase().compareTo(s2.toLowerCase());
}
//Compare Timestamp
if(p1.getData().getClass().equals(Timestamp.class)) {
Timestamp t1=(Timestamp) p1.getData();
Timestamp t2=(Timestamp) p2.getData();
if(t1.before(t2)) return -1;
if(t1.equals(t2)) return 0;
return 1;
}
//Compare Bool
if(p1.getData().getClass().equals(Boolean.class)) {
boolean b1=((Boolean)p1.getData()).booleanValue();
boolean b2=((Boolean)p2.getData()).booleanValue();
if(b1==false && b2==true) return -1;
if(b1==b2) return 0;
return 1;
}
return 0;
}
};
Any suggestions to this or different approaches a very welcome.
Well, the question is old but Google returned this page to me when I had a similar task. So, I'll post here my solution. Hope that it would help someone.
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.DefaultRowSorter;
import javax.swing.JTable;
import javax.swing.RowSorter.SortKey;
import javax.swing.SortOrder;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import org.apache.log4j.Logger;
public class PredefinedRowSorter extends TableRowSorter< TableModel > {
private static final Logger LOGGER = Logger.getLogger( PredefinedRowSorter.class );
private static final SortKey EMPTY_ARRAY[] = new SortKey[ 0 ];
private final JTable table;
private SortKey preColumns[] = EMPTY_ARRAY;
private SortKey postColumns[] = EMPTY_ARRAY;
public PredefinedRowSorter( JTable table ) {
super( table.getModel() );
this.table = table;
}
private void check( SortKey modelColumns[], SortKey crossCheckColumns[], boolean post ) {
TableModel tm = table.getModel();
int max = tm.getColumnCount();
Set< Integer > used = new HashSet< Integer >();
for ( SortKey key : modelColumns ) {
if ( key == null )
throw new IllegalArgumentException( "SortKey must be non-null" );
if ( key.getColumn() < 0 )
throw new IllegalArgumentException( "SortKey column must be non-negative" );
if ( key.getColumn() >= max )
throw new IllegalArgumentException( "SortKey column is too high (out of model scope)" );
if ( key.getSortOrder() == SortOrder.UNSORTED )
throw new IllegalArgumentException( "SortKey must be ordered (ascending or descending)" );
if ( !used.add( key.getColumn() ) )
throw new IllegalArgumentException( "SortKey column must be unique (column " + key.getColumn() + " is repeating)" );
}
for ( SortKey key : crossCheckColumns )
if ( used.contains( key.getColumn() ) )
throw new IllegalArgumentException( "SortKey column must be unique (column " + key.getColumn() + " is already contained in " + ( post ? "post" : "pre" ) + " columns list)" );
}
public PredefinedRowSorter withPreColumns( SortKey... modelColumns ) {
if ( modelColumns == null )
modelColumns = EMPTY_ARRAY;
if ( !Arrays.equals( preColumns, modelColumns ) ) {
check( modelColumns, postColumns, true );
preColumns = modelColumns;
setSortKeys( getSortKeys() );
}
return this;
}
public PredefinedRowSorter withPostColumns( SortKey... modelColumns ) {
if ( modelColumns == null )
modelColumns = EMPTY_ARRAY;
if ( !Arrays.equals( postColumns, modelColumns ) ) {
check( modelColumns, preColumns, false );
postColumns = modelColumns;
setSortKeys( getSortKeys() );
}
return this;
}
public JTable getTable() {
return table;
}
public SortKey[] getPreColumns() {
return preColumns.length == 0 ? preColumns : preColumns.clone();
}
public SortKey[] getPostColumns() {
return postColumns.length == 0 ? postColumns : postColumns.clone();
}
private void setSortKeysInternal( List< ? extends SortKey > sortKeys ) {
try {
Field field = DefaultRowSorter.class.getDeclaredField( "sortKeys" );
boolean accessible = field.isAccessible();
if ( !accessible )
field.setAccessible( true );
field.set( this, sortKeys );
if ( !accessible )
field.setAccessible( false );
} catch ( IllegalAccessException e ) {
LOGGER.error( null, e );
} catch ( IllegalArgumentException e ) {
LOGGER.error( null, e );
} catch ( NoSuchFieldException e ) {
LOGGER.error( null, e );
} catch ( SecurityException e ) {
LOGGER.error( null, e );
}
}
private Object getViewToModelInternal() {
try {
Field field = DefaultRowSorter.class.getDeclaredField( "viewToModel" );
boolean accessible = field.isAccessible();
if ( !accessible )
field.setAccessible( true );
Object ret = field.get( this );
if ( !accessible )
field.setAccessible( false );
return ret;
} catch ( IllegalAccessException e ) {
LOGGER.error( null, e );
} catch ( IllegalArgumentException e ) {
LOGGER.error( null, e );
} catch ( NoSuchFieldException e ) {
LOGGER.error( null, e );
} catch ( SecurityException e ) {
LOGGER.error( null, e );
}
return null;
}
private void sortExistingDataInternal() {
try {
Method method = DefaultRowSorter.class.getDeclaredMethod( "sortExistingData" );
boolean accessible = method.isAccessible();
if ( !accessible )
method.setAccessible( true );
method.invoke( this );
if ( !accessible )
method.setAccessible( false );
} catch ( IllegalAccessException e ) {
LOGGER.error( null, e );
} catch ( IllegalArgumentException e ) {
LOGGER.error( null, e );
} catch ( NoSuchMethodException e ) {
LOGGER.error( null, e );
} catch ( InvocationTargetException e ) {
LOGGER.error( null, e );
LOGGER.error( null, ( ( InvocationTargetException )e ).getCause() );
}
}
#Override
public void setSortKeys( List< ? extends SortKey > sortKeys ) {
List< ? extends SortKey > oldSortKeys = getSortKeys();
List< ? extends SortKey > newSortKeys;
if ( sortKeys != null && !sortKeys.isEmpty() ) {
int max = getModelWrapper().getColumnCount();
for ( SortKey key : sortKeys )
if ( key == null || key.getColumn() < 0 || key.getColumn() >= max )
throw new IllegalArgumentException( "Invalid SortKey" );
newSortKeys = Collections.unmodifiableList( new ArrayList< SortKey >( sortKeys ) );
} else
newSortKeys = Collections.emptyList();
setSortKeysInternal( newSortKeys );
if ( !newSortKeys.equals( oldSortKeys ) ) {
fireSortOrderChanged();
boolean wasChanged = false;
if ( preColumns.length > 0 || postColumns.length > 0 ) {
List< SortKey > editableSortKeys = new ArrayList< SortKey >( newSortKeys );
for ( int i = preColumns.length - 1; i >= 0; i-- ) {
int modelColumn = preColumns[ i ].getColumn();
int idx = indexOfColumn( editableSortKeys, preColumns.length - i - 1, editableSortKeys.size(), modelColumn );
SortOrder sortOrder = idx < 0 ? preColumns[ i ].getSortOrder() : editableSortKeys.remove( idx ).getSortOrder();
editableSortKeys.add( 0, new SortKey( modelColumn, sortOrder ) );
}
int to = editableSortKeys.size();
for ( SortKey postColumn : postColumns ) {
int modelColumn = postColumn.getColumn();
int idx = indexOfColumn( editableSortKeys, preColumns.length, to, modelColumn );
SortOrder sortOrder;
if ( idx < 0 )
sortOrder = postColumn.getSortOrder();
else {
sortOrder = editableSortKeys.remove( idx ).getSortOrder();
to--;
}
editableSortKeys.add( new SortKey( modelColumn, sortOrder ) );
}
if ( wasChanged = !editableSortKeys.equals( newSortKeys ) )
setSortKeysInternal( editableSortKeys );
}
if ( getViewToModelInternal() == null )
sort();
else
sortExistingDataInternal();
if ( wasChanged )
setSortKeysInternal( newSortKeys );
}
}
private int indexOfColumn( List< SortKey > sortKeys, int fromIncl, int toExcl, int column ) {
for ( int i = toExcl - 1; i >= fromIncl; i-- )
if ( sortKeys.get( i ).getColumn() == column )
return i;
return -1;
}
};
Usage:
table.setRowSorter( new PredefinedRowSorter( table )
.withPreColumns( new SortKey( 0, SortOrder.ASCENDING ),
new SortKey( 1, SortOrder.ASCENDING ) ) );
This would set a preceding sorting on first two columns.
Post-sorting is available.
An end-user may sort these two columns too, thus switching the order of sorting (ascending/descending).
Also, this class is available as a part of JBroTable library (source code).
The principle of action is so: predefined columns are added to a sorting columns list before sorting and removed from this list after sorting. Adding and removal is performed via reflection because DefaultRowSorter implementation doesn't provide an access to the list.

Categories