Java coding -- reverse enum lookup and sorting objects with a comparator

This blog post covers three core Java concepts:

1) Writing value objects to store data.
2) Writing enum classes with reverse look-up.
3) Lastly, but most importantly  custom sorting your value objects with a comparator.


Step 1: Writing your value object class.

package com.myapp.model;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;

public class CashForecastSummary implements Serializable
{
 private static final long serialVersionUID = 2663449836220393299L;
 
 private String portfolioCode;
 private Date valuationDate;
 private String accountCd; 
 private BigDecimal totalAmount = BigDecimal.ZERO;
 private String currencyCode;
 private String transactionTypeDesc;
 private ForecastDay foreCastDay;
 private Date foreCastDate;  
 private RecordType recordType;
 
 public String getPortfolioCode() {
  return portfolioCode;
 }
 public void setPortfolioCode(String portfolioCode) {
  this.portfolioCode = portfolioCode;
 }
 public Date getValuationDate() {
  return valuationDate;
 }
 public void setValuationDate(Date valuationDate) {
  this.valuationDate = valuationDate;
 }
 public String getAccountCd() {
  return accountCd;
 }
 public void setAccountCd(String accountCd) {
  this.accountCd = accountCd;
 }
 public BigDecimal getTotalAmount() {
  return totalAmount;
 }
 public void setTotalAmount(BigDecimal totalAmount) {
  this.totalAmount = totalAmount;
 }
 public String getCurrencyCode() {
  return currencyCode;
 }
 public void setCurrencyCode(String currencyCode) {
  this.currencyCode = currencyCode;
 }
 public String getTransactionTypeDesc() {
  return transactionTypeDesc;
 }
 public void setTransactionTypeDesc(String transactionTypeDesc) {
  this.transactionTypeDesc = transactionTypeDesc;
 }
 public ForecastDay getForeCastDay() {
  return foreCastDay;
 }
 public void setForeCastDay(ForecastDay foreCastDay) {
  this.foreCastDay = foreCastDay;
 } 
 public Date getForeCastDate() {
  return foreCastDate;
 }
 public void setForeCastDate(Date foreCastDate) {
  this.foreCastDate = foreCastDate;
 }
 public RecordType getRecordType() {
  return recordType;
 }
 public void setRecordType(RecordType recordType) {
  this.recordType = recordType;
 }
 
 @Override
 public String toString() 
 {
  return "CashForecastSummary [portfolioCode=" + portfolioCode
    + ", valuationDate=" + valuationDate + ", accountCd="
    + accountCd + ", totalAmount=" + totalAmount
    + ", currencyCode=" + currencyCode + ", transactionTypeDesc="
    + transactionTypeDesc + ", foreCastDay=" + foreCastDay
    + ", recordType=" + recordType + "]";
 }
 
 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result
    + ((accountCd == null) ? 0 : accountCd.hashCode());
  result = prime * result
    + ((foreCastDay == null) ? 0 : foreCastDay.hashCode());
  result = prime
    * result
    + ((transactionTypeDesc == null) ? 0 : transactionTypeDesc
      .hashCode());
  return result;
 }
 
 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  CashForecastSummary other = (CashForecastSummary) obj;
  if (accountCd == null) {
   if (other.accountCd != null)
    return false;
  } else if (!accountCd.equals(other.accountCd))
   return false;
  if (foreCastDay != other.foreCastDay)
   return false;
  if (transactionTypeDesc == null) {
   if (other.transactionTypeDesc != null)
    return false;
  } else if (!transactionTypeDesc.equals(other.transactionTypeDesc))
   return false;
  return true;
 }

}



The hashCode( ), equals( Object obj), and toString( ) methods from the Java Object class are over written. It also implements the Serializable interface to make the object serializable (i.e. can be flattened to bytes).

Step 2a: Writing a simple enum class.

package com.myapp.model;

public enum RecordType 
{
 OPENING_BALANCE(1),
    DETAILS(2),
    CLOSING_BALANCE(3);

    private final int recordType;
    
    private RecordType(int recordType)
    {
     this.recordType = recordType;
    }
    
    public int getRecordType()
    {
        return recordType;
    }

}



Step 2b: Writing an enum class with reverse look-up using a Map.

package com.myapp.model;

import java.util.HashMap;
import java.util.Map;

public enum ForecastDay {
 T(0), T1(1), T2(2), T3(3), T4(4), T5(5), T6(6), T7(7), T8(8), T9(9), T10(10), T11(11), T12(12), T13(13), TN(14);

 // Reverse-lookup map for getting a day from an abbreviation
 private static final Map<Integer, ForecastDay> lookup = new HashMap<Integer, ForecastDay>();
 static {
  for (ForecastDay d : ForecastDay.values())
   lookup.put(d.getDaycount(), d);
 }

 private final int dayCount;

 private ForecastDay(int dayCount) {
  this.dayCount = dayCount;
 }

 public int getDaycount() {
  return dayCount;
 }

 public static ForecastDay getDayFromCount(int dayCount) {
  if (dayCount > 13) {
   dayCount = 14;
  }
  return lookup.get(dayCount);
 }

}


Step 3: Writing a comparator class to sort a collection of value objects.



package com.myapp.model.sort;

import java.util.Comparator;

import com.myapp.model.CashForecastSummary;

public class CashForecastSummaryComparator implements Comparator<CashForecastSummary> {

 @Override
 public int compare(CashForecastSummary o1, CashForecastSummary o2) {

  int result = 1;

  String accountCd1 = o1.getAccountCd();
  String accountCd2 = o2.getAccountCd();

  if (accountCd1 == null || accountCd2 == null) {
   throw new IllegalArgumentException("The account codes cannot be null " + " accountCd1=" + accountCd1 + ", accountCd2=" + accountCd2);
  }
  
  result = accountCd1.compareTo(accountCd2);
  if (result != 0) {
   return result;
  }
  
  Integer forecastDay1 = o1.getForeCastDay().getDaycount();
  Integer forecastDay2 = o2.getForeCastDay().getDaycount();

  if (forecastDay1 == null || forecastDay2 == null) {
   throw new IllegalArgumentException("The forecastDay cannot be null " + " forecastDay1=" + forecastDay1 + ", forecastDay2=" + forecastDay2);
  }

  result = forecastDay1.compareTo(forecastDay2);
  if (result != 0) {
   return result;
  }

  Integer recType1 = o1.getRecordType().getRecordType();
  Integer recType2 = o2.getRecordType().getRecordType();

  if (recType1 == null || recType2 == null) {
   throw new IllegalArgumentException("The recordType cannot be null " + " recType1=" + recType1 + ", recType2=" + recType2);
  }

  result = recType1.compareTo(recType2);
  if (result != 0) {
   return result;
  }
  
  result = o1.getTransactionTypeDesc().compareTo(o2.getTransactionTypeDesc());
  
  return result;
 }

}


As you could see, you need to implement the compare(Object obj1, Object obj2) method. This method has been written with fail fast in mind. As you can see, the input fields are validated, and exception is thrown if validation fails.


Step 4:  Finally, how to use the comparator.

    
 ....

    /**
 *  takes unsorted values as an argument and returns the sorted values
 */
 public List<CashForecastSummary> getSortedSummariesForBalanceUpdate(List<CashForecastSummary> values) {
  Collections.sort(values, new CashForecastSummaryComparator());
  return values;
 }
 
 
 ....
 


No comments:

Post a Comment