开发者

Java enum search by number range

Is it possible to do enum like below

enum {

 10  poor
 100  rich
 1000 very_rich


}

so that when i do search by input value let say 101. it will return "rich" ? how to do this in enum? can give example? i do not want to loop entire enum开发者_如何学运维 with forloop to get the string_value. possible?


Use an enum with values, as the others have already suggested.

Then, instead of performing a brute-force iterative search through the enum values, provide a static lookup(int) method that performs a binary search through an ordered list/array of all the values.

To perform the search, start with the median or middle value as 'root', and compare the value we're looking for with that.

If the value we're looking for is exactly that, then we're done. If it's less than that, then we start searching the lower half of the values, again starting from the middle. If greater, then compare with the value right after it just to see if it falls in range. If it's still larger, then search in the upper half, and so on.


EDIT: Code sample as requested.

public enum Wealth {

    BROKE(0),
    DESTITUTE(10),
    POOR(100),
    MIDDLE_CLASS(10000),
    RICH(100000),
    MILLIONAIRE(1000000),
    BILLIONAIRE(1000000000);

    private final int value;

    private Wealth(final int value) {
        this.value = value;
    }

    public final int getValue() {
        return value;
    }

    /**
     * @param v
     *        the value we're looking for
     * @return Wealth
     */
    public static Wealth lookup(final int v) {
        final Wealth[] a = Wealth.values();
        int min = 0;
        int max = a.length  - 1;
        int i;
        do {
            i = (min + max) / 2;
            final int av = a[i].value;
            if (v < av) {
                max = i;
            } else if (v > av) {
                if (i + 1 < a.length && v < a[i + 1].value) {
                    break;
                }
                min = i + 1;
            }
        } while (v != a[i].value && min < max);
        if (min == max) {
            return a[max];
        }
        return a[i];
    }

}

Several notes:

This assumes that the values for Wealth are already ordered. Otherwise, a quick sort (pun!) should do the trick.

This probably isn't the most efficient implementation, just a quick and dirty one adapted from the pseudo-code on Wikipedia.

If you have fewer than, say, a dozen values, then a linear search might still be more efficient (and the code definitely more self-explanatory) than a binary search. Binary search only really pays off when you have dozens or hundreds of values, and you perform lookups millions of times.

Given your original values, this is evil, premature optimization. I just wanted to bring it up as an option for those who're working with large sets of values.


"Standard" way

Create the enum as you would with an extra member variable (containing the values 10, 100 and 1000). Then just create a static method in the enum getWealth that finds the correct enum value depending on a money argument:

static enum Wealth {
    POOR(10), RICH(100), VERY_RICH(1000);

    private final int money;

    private Wealth(int money) {
        this.money = money;
    }

    public static Wealth getWealth(int money) {
        Wealth found = POOR;
        for (Wealth w : values())
            if (w.money <= money)
                found = w;

        return found;
    }
}

public static void main(String[] args) {
    System.out.println(Wealth.getWealth(101));
    System.out.println(Wealth.getWealth(9));
    System.out.println(Wealth.getWealth(10000));
}

Ouput:

RICH
POOR
VERY_RICH

Without looping:

I saw in one of your comments that you want to do it without looping. This can be done but with some tricks. First, your values cannot be changed in this solution (10, 100, 1000) because it uses the length of the string given by the money argument:

static enum Wealth {
    POOR, RICH, VERY_RICH; // 10, 100, 1000

    public static Wealth getWealth(int money) {
        int len = Integer.toString(money).length();
        int ordinal = Math.max(0, Math.min(len - 2, values().length - 1));
        return values()[ordinal];
    }
}


Here's a way that's neither elegant nor efficient, but you don't see any looping.

I stole the basics from Chip's answer, and added the Set to it:

public enum WealthLevel {
  POOR(10),
  RICH(100),
  VERY_RICH(1000);

private static final Map<Integer,Status> lookup 
      = new HashMap<Integer,Status>();
// contains all codes ordered - for headSet call.
private static final SortedSet<Integer> intValues = new TreeSet<Integer>(); 

 static {
      for(WealthLevel w : EnumSet.allOf(WealthLevel.class)) {
           lookup.put(w.getCode(), w);
           intValues.add( w.getCode() );
      }
 }

 private int code;

 private WealthLevel(int code) {
      this.code = code;
 }

 public int getCode() { return code; }

 public static WealthLevel get(int code) { 
      if (lookup.contains(code)) {
          return lookup.get(code); 
      }
      // No visible iteration through performance probably is not great
      SortedSet<Integer> lower = intValues.headSet(code);
      if (lower.size() > 0) {
          return lookup.get( lower.last() );
      }
      return null; // no possible value <= code
 }

}


Yes, and the tutorial from Oracle shows you how:

http://download.oracle.com/javase/tutorial/java/javaOO/enum.html


public enum MyEnum {
    POOR(10), RICH(100), VERY_RICH(1000);

    int money; 
    MyEnum(int money) {
        this.money = money;
    }
}


What you're looking for is a reverse lookup. The key needs to be accepted in a constructor and you need a lookup method.

An example borrowed from: http://www.ajaxonomy.com/2007/java/making-the-most-of-java-50-enum-tricks

public enum WealthLevel {
  POOR(10),
  RICH(100),
  VERY_RICH(1000);

private static final Map<Integer,Status> lookup 
      = new HashMap<Integer,Status>();

 static {
      for(WealthLevel w : EnumSet.allOf(WealthLevel.class))
           lookup.put(w.getCode(), w);
 }

 private int code;

 private WealthLevel(int code) {
      this.code = code;
 }

 public int getCode() { return code; }

 public static WealthLevel get(int code) { 
      return lookup.get(code); 
 }

}


In java, an enum Is a class. So you can just add any method you want to retrieve values. If the method values is not enough, you could had any other to meet your requirement.

Maybe your question was also about adding data members to enum. You can also do that.

enum A {

     A(1),
     B(2);

      private int a;

     A( int a) { this.a=a; }

      A retrieve( int a ) 
     {
        Your code here maybe using a hashmap 
      }
}


If it must be an enum, and lookup must be O(log n) with minimal overhead:

public enum WealthLevel {
    POOR(10), RICH(100), VERY_RICH(1000);

    private int lowerLimit;

    private WealthLevel(int lowerLimit) {
        this.lowerLimit = lowerLimit;
    }

    // cache for the sake of performance
    private static final WealthLevel[] values = values();

    private final static int[] lowerLimits; 
    static {
        lowerLimits = new int[values.length];
        for (int i = 0; i < values.length; i++) {
            lowerLimits[i] = values[i].lowerLimit;
        }

    }

    public static WealthLevel lookup(int wealth) {
        int i = Arrays.binarySearch(lowerLimits, wealth);
        if (i < 0) {
            i = -i - 2;
        }
        return values[i];
    }
}

If you can live with a little overhead during lookup, consider using a TreeMap instead. Also, if you just need a string (for instance to display it somewhere), an enum is unnecessary, too:

class WealthConverter {
    NavigableMap<Integer, String> levels = new TreeMap<Integer, String>();
    {
        levels.put(0, "pennyless");
        levels.put(10, "poor");
        levels.put(100, "rich");
        levels.put(1000, "very rich");
    }

    public String lookup(int wealth) {
        return levels.floorEntry(wealth).getValue();
    }
}
0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜