开发者

Java Collections Using money ranges

I would like to store information in a java collection that is cached in memory.

I have the followi开发者_JAVA技巧ng table

less than $1 - (store some data)

$1 to $115,000 - (Store some different data)

$115,001 to $345,000 - (Store some different data)

$345,001 to 580,000 - (Store some different data)

$580,001 or more - (Store some different data)

I would like to have an amount of money passed in to a function. Then I would need to determine what money range the passed in money falls into so I can retrieve its data. Ex an amount of $127,000 was passed in. I would need to use the third rows data in the above table, because 127,000 falls in between $115,000 and $345,000.

I really would appreciate any suggestions on how to best design this.

Thanks in advance Doug


Here's a basic working example. Of course you'd probably want to use something besides MAX_VALUE and MIN_VALUE as the outer bounds, and you probably want floating point numbers instead of integers. A full implementation would probably have CurrencyRange as an interface, where you could have different implementations for the "less than $1" and "more than $580,000" cases:

private static final List<CurrencyRange> ranges = Arrays.asList(
        new CurrencyRange(Integer.MIN_VALUE, 0, "some data"),
        new CurrencyRange(1, 115000, "some data"),
        new CurrencyRange(115001, 345000, "some data"),
        new CurrencyRange(345001, 580000, "some data"),
        new CurrencyRange(580001, Integer.MAX_VALUE, "some data")
);

public String determineDataByAmount(int amount) {
    for (CurrencyRange range : ranges) {
        if (range.contains(amount)) {
            return range.getData();
        }
    }
    throw new IllegalStateException(
        "No suitable range found for amount: " + amount);
}

class CurrencyRange {
    private int lowerLimit;
    private int upperLimit;
    private String data;

    public CurrencyRange(int lowerLimit, int upperLimit, String data) {
        this.lowerLimit = lowerLimit;
        this.upperLimit = upperLimit;
        this.data = data;
    }

    public boolean contains(int amount) {
        return amount >= lowerLimit && amount <= upperLimit;
    }

    public String getData() {
        return data;
    }
}


Conditionals would work best.

function processMoney(int amount) {
    if(amount > 580000) {
        //do stuff
    else if (amount > 345000) {
        // do other stuff
    }
}

etc.


Use binary search to improve performance when:

private List<Double> ranges = Arrays.asList(1.0, 115000.0, 345000.0, 580000.0);

private int findRange(double amount) {
    final int idx = Collections.binarySearch(ranges, amount);
    return idx >= 0 ? idx : -idx - 1;
}

And test:

public class RangeTest {

    @Test
    public void shouldReturnIndex() throws Exception {
        assertThat(findRange(0.5)).isEqualTo(0);
        assertThat(findRange(1.0)).isEqualTo(0);
        assertThat(findRange(1.01)).isEqualTo(1);

        assertThat(findRange(114000.0)).isEqualTo(1);
        assertThat(findRange(115000.0)).isEqualTo(1);
        assertThat(findRange(115001.0)).isEqualTo(2);

        assertThat(findRange(581000.0)).isEqualTo(4);
    }


}

Few notes:

  • binary search seems like an overkill in your scenario, however it is actually the easiest implementation without any explicit loops and any custom logic
  • findRange() returns range index you fall into (number of ranges is equal to number of steps + 1)
  • You should definitely use BigDecimal, I used double only to make an example simpler
  • Real tests should be shorter and more descriptive


One way you could handle this would be with a NavigableSet:

NavigableSet<Integer> cutoffs = new TreeSet<Integer>();
cutoffs.addAll(Arrays.asList(Integer.MIN_VALUE, 0, 150000, 345000, 580000));

...

public void something(int amount) {
  switch (cutoffs.lower(amount)) {
    case Integer.MIN_VALUE:
      // below 1
    case 0:
      // 1 to 150000
    case 150000:
      // etc
    case 345000:
      // etc
    default:
      // etc
  }
}

This would likely be a bit more efficient than just doing a flat if/else (provided you only create the cutoffs set once and this method is used a lot) but for such a simple example as yours, something simpler would be fine if not better.


public void function(int money)
{
    if(money <= 1) //store some data
    if(money >= 2 && money <= 115000) // Store some different data
    if(money >= 115001 && money <= 345000) //Store some different data
    if(money >= 345001 && money <= 580000) //Store some different data
    if(money >= 580001) // Store some different data

}


On the coding side, it's easy. Use polymorphisim to have different objects for each money amount.

What I gather is that you need to persist this to a database too. While you didn't exactly say that, the heavy use of words like "table" "data" and "rows" hints that your are concerned with database storage. To persist polymorphic data in a database you only have a few options:

  1. Single Table Inheritance - using one table to store all classes in a polymorphic hierarchy.
  2. Concrete Table Inheritance - using one table for each concrete subclass.
  3. Class Table Inheritance - Using one table for the common superclass and one table for each subclass (concrete or otherwise) storing fields as they are defined in the object oriented hierarchy.

The advantages to #1 is that you have only one table to search, the downside is that most of that table will be a big block of variant data. Often a field with the "variant" parts becomes an XML document stored in a row. In either case, if the invariant (always there) fields are to be searched, it is easy; but, if the variant fields are to be searched, it gets hard.

The advantages to #2 is that database queries are easy with standard SQL tools. The downside is that you have to take special precautions not to store the same invariant keys in the collection of tables twice, as this would be saying that account #5 is both a $5 account and a $50,000 account.

The advantages to #3 is that database structure mimics the class heirarchy, which can ease the key collision issues in #2 and yet still provide better SQL querying than #1. The downside is that to get any single object, you will have to perform a join across a foreign key (performance hit).

In short, no silver bullets. However, perhaps you didn't really mean database tables, in which case it's not an issue.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜