Skip to main content
Code Review

Return to Answer

replaced http://stackoverflow.com/ with https://stackoverflow.com/
Source Link
  • The method entriesSortedByValues creates an anonymous Comparator. Comparators are thread-safe and re-entran, and, as a result, it is just a waste of time to create one each time the method is called. Something like:

     private static final ASCENDING_VALUE_COMPARATOR = new Comparator<Map.Entry<K, V>>() {
     @Override
     public int compare(Map.Entry<K, V> e1, Map.Entry<K, V> e2) {
     int res;
     e1.getValue().compareTo(e2.getValue());
     else res = e2.getValue().compareTo(e1.getValue());
     return res != 0 ? -res : 1; // Special fix to preserve items with equal values
     }
     }
    
  • While in the process of doing that last one above, it appeared to me that the comparators are the wrong way around in there:

    if (descending) res = e1.getValue().compareTo(e2.getValue());
    else res = e2.getValue().compareTo(e1.getValue());
    
  • This line is highly suspicious... this makes the comparator non-transitive. With a comparator, compare(a, b) should be the same sign as -compare(b,a), but, in your case, if a.equals(b), then your code will return 1 in both cases.

    return res != 0 ? -res : 1; // Special fix to preserve items with equal values
    

    Your code will likely fail with "Comparison method violates its general contract!" "Comparison method violates its general contract!"

  • getRandom again makes it hard to move to mult-threaded. Consider using java.util.concurrent.ThreadLocalRandom

  • The method entriesSortedByValues creates an anonymous Comparator. Comparators are thread-safe and re-entran, and, as a result, it is just a waste of time to create one each time the method is called. Something like:

     private static final ASCENDING_VALUE_COMPARATOR = new Comparator<Map.Entry<K, V>>() {
     @Override
     public int compare(Map.Entry<K, V> e1, Map.Entry<K, V> e2) {
     int res;
     e1.getValue().compareTo(e2.getValue());
     else res = e2.getValue().compareTo(e1.getValue());
     return res != 0 ? -res : 1; // Special fix to preserve items with equal values
     }
     }
    
  • While in the process of doing that last one above, it appeared to me that the comparators are the wrong way around in there:

    if (descending) res = e1.getValue().compareTo(e2.getValue());
    else res = e2.getValue().compareTo(e1.getValue());
    
  • This line is highly suspicious... this makes the comparator non-transitive. With a comparator, compare(a, b) should be the same sign as -compare(b,a), but, in your case, if a.equals(b), then your code will return 1 in both cases.

    return res != 0 ? -res : 1; // Special fix to preserve items with equal values
    

    Your code will likely fail with "Comparison method violates its general contract!"

  • getRandom again makes it hard to move to mult-threaded. Consider using java.util.concurrent.ThreadLocalRandom

  • The method entriesSortedByValues creates an anonymous Comparator. Comparators are thread-safe and re-entran, and, as a result, it is just a waste of time to create one each time the method is called. Something like:

     private static final ASCENDING_VALUE_COMPARATOR = new Comparator<Map.Entry<K, V>>() {
     @Override
     public int compare(Map.Entry<K, V> e1, Map.Entry<K, V> e2) {
     int res;
     e1.getValue().compareTo(e2.getValue());
     else res = e2.getValue().compareTo(e1.getValue());
     return res != 0 ? -res : 1; // Special fix to preserve items with equal values
     }
     }
    
  • While in the process of doing that last one above, it appeared to me that the comparators are the wrong way around in there:

    if (descending) res = e1.getValue().compareTo(e2.getValue());
    else res = e2.getValue().compareTo(e1.getValue());
    
  • This line is highly suspicious... this makes the comparator non-transitive. With a comparator, compare(a, b) should be the same sign as -compare(b,a), but, in your case, if a.equals(b), then your code will return 1 in both cases.

    return res != 0 ? -res : 1; // Special fix to preserve items with equal values
    

    Your code will likely fail with "Comparison method violates its general contract!"

  • getRandom again makes it hard to move to mult-threaded. Consider using java.util.concurrent.ThreadLocalRandom

deleted 17 characters in body
Source Link
Jamal
  • 35.2k
  • 13
  • 134
  • 238
  • This is an accumulator for the score attached to a field. It could potentially contain multiple scores. I am disappointed that it is not thread-safe. First impression is that it would be a useful optimization to have the fields all scored by different scorers at the same time. This would not be possible with the current FieldScoreFieldScore.
  • PostScorer is an abstract class, but PreScorer` is an interface. I would expect them to be mirrors of each other, but they are not.

  • The method onScoringComplete should be something like onPreScoringComplete, right?

  • analyzeAnalyze should be returning some generic type, not Object.

  • I would expect the methods on PreScorerPreScorer and PostScorerPostScorer to be similar to each other, but they are not... at all.

  • Again, I would expect a more complicated <P, F> signature on the PreScorerPreScorer... without it, is it doing something wrong?

  • inIn all other methods you have been careful to return copies of the stored structures, but in this class you return the basic values. Should the methods use Collections.unmodifiableMap(....) or new HashMap(....) instead?
  • whyWhy have a factory class and method if the constructors are public anyway?
  • sinceSince when does this make sense?

    public class ScoreSet<P, F> extends LinkedHashMap<AbstractScorer<P, F>, Double>
    

    ???? ;-)

  • The method entriesSortedByValues creates an anonymous Comparator. Comparators are thread-safe and re-entran, and, as a result, it is just a waste of time to create one each time the method is called. Something like:

    private static final ASCENDING_VALUE_COMPARATOR = new Comparator<Map.Entry<K, V>>() { @Override public int compare(Map.Entry<K, V> e1, Map.Entry<K, V> e2) { int res; e1.getValue().compareTo(e2.getValue()); else res = e2.getValue().compareTo(e1.getValue()); return res != 0 ? -res : 1; // Special fix to preserve items with equal values } }

     private static final ASCENDING_VALUE_COMPARATOR = new Comparator<Map.Entry<K, V>>() {
     @Override
     public int compare(Map.Entry<K, V> e1, Map.Entry<K, V> e2) {
     int res;
     e1.getValue().compareTo(e2.getValue());
     else res = e2.getValue().compareTo(e1.getValue());
     return res != 0 ? -res : 1; // Special fix to preserve items with equal values
     }
     }
    
  • While in the process of doing that last one above, it appeared to me that the comparators are the wrong way around in there:

    if (descending) res = e1.getValue().compareTo(e2.getValue());
    else res = e2.getValue().compareTo(e1.getValue());
    
  • This line is highly suspicious... this makes the comparator non-transitive. With a comparator, compare(a, b) should be the same sign as -compare(b,a), but, in your case, if a.equals(b), then your code will return 1 in both cases.

    return res != 0 ? -res : 1; // Special fix to preserve items with equal values

    return res != 0 ? -res : 1; // Special fix to preserve items with equal values
    

    Your code will likely fail with "Comparison method violates its general contract!"

  • getRandomgetRandom again makes it hard to move to mult-threaded. Consider using java.util.concurrent.ThreadLocalRandom

The Codecode is good, and hangs together 'quite well.

  • This is an accumulator for the score attached to a field. It could potentially contain multiple scores. I am disappointed that it is not thread-safe. First impression is that it would be a useful optimization to have the fields all scored by different scorers at the same time. This would not be possible with the current FieldScore.
  • PostScorer is an abstract class, but PreScorer` is an interface. I would expect them to be mirrors of each other, but they are not.

  • The method onScoringComplete should be something like onPreScoringComplete, right?

  • analyze should be returning some generic type, not Object.

  • I would expect the methods on PreScorer and PostScorer to be similar to each other, but they are not... at all.

  • Again, I would expect a more complicated <P, F> signature on the PreScorer... without it, is it doing something wrong?

  • in all other methods you have been careful to return copies of the stored structures, but in this class you return the basic values. Should the methods use Collections.unmodifiableMap(....) or new HashMap(....) instead?
  • why have a factory class and method if the constructors are public anyway?
  • since when does this make sense?

    public class ScoreSet<P, F> extends LinkedHashMap<AbstractScorer<P, F>, Double>
    

    ???? ;-)

  • The method entriesSortedByValues creates an anonymous Comparator. Comparators are thread-safe and re-entran, and, as a result, it is just a waste of time to create one each time the method is called. Something like:

    private static final ASCENDING_VALUE_COMPARATOR = new Comparator<Map.Entry<K, V>>() { @Override public int compare(Map.Entry<K, V> e1, Map.Entry<K, V> e2) { int res; e1.getValue().compareTo(e2.getValue()); else res = e2.getValue().compareTo(e1.getValue()); return res != 0 ? -res : 1; // Special fix to preserve items with equal values } }

  • While in the process of doing that last one above, it appeared to me that the comparators are the wrong way around in there:

    if (descending) res = e1.getValue().compareTo(e2.getValue());
    else res = e2.getValue().compareTo(e1.getValue());
    
  • This line is highly suspicious... this makes the comparator non-transitive. With a comparator, compare(a, b) should be the same sign as -compare(b,a), but, in your case, if a.equals(b), then your code will return 1 in both cases.

    return res != 0 ? -res : 1; // Special fix to preserve items with equal values

    Your code will likely fail with "Comparison method violates its general contract!"

  • getRandom again makes it hard to move to mult-threaded. Consider using java.util.concurrent.ThreadLocalRandom

The Code is good, and hangs together 'quite well.

  • This is an accumulator for the score attached to a field. It could potentially contain multiple scores. I am disappointed that it is not thread-safe. First impression is that it would be a useful optimization to have the fields all scored by different scorers at the same time. This would not be possible with the current FieldScore.
  • PostScorer is an abstract class, but PreScorer` is an interface. I would expect them to be mirrors of each other, but they are not.

  • The method onScoringComplete should be something like onPreScoringComplete, right?

  • Analyze should be returning some generic type, not Object.

  • I would expect the methods on PreScorer and PostScorer to be similar to each other, but they are not... at all.

  • Again, I would expect a more complicated <P, F> signature on the PreScorer... without it, is it doing something wrong?

  • In all other methods you have been careful to return copies of the stored structures, but in this class you return the basic values. Should the methods use Collections.unmodifiableMap(....) or new HashMap(....) instead?
  • Why have a factory class and method if the constructors are public anyway?
  • Since when does this make sense?

    public class ScoreSet<P, F> extends LinkedHashMap<AbstractScorer<P, F>, Double>
    

    ???? ;-)

  • The method entriesSortedByValues creates an anonymous Comparator. Comparators are thread-safe and re-entran, and, as a result, it is just a waste of time to create one each time the method is called. Something like:

     private static final ASCENDING_VALUE_COMPARATOR = new Comparator<Map.Entry<K, V>>() {
     @Override
     public int compare(Map.Entry<K, V> e1, Map.Entry<K, V> e2) {
     int res;
     e1.getValue().compareTo(e2.getValue());
     else res = e2.getValue().compareTo(e1.getValue());
     return res != 0 ? -res : 1; // Special fix to preserve items with equal values
     }
     }
    
  • While in the process of doing that last one above, it appeared to me that the comparators are the wrong way around in there:

    if (descending) res = e1.getValue().compareTo(e2.getValue());
    else res = e2.getValue().compareTo(e1.getValue());
    
  • This line is highly suspicious... this makes the comparator non-transitive. With a comparator, compare(a, b) should be the same sign as -compare(b,a), but, in your case, if a.equals(b), then your code will return 1 in both cases.

    return res != 0 ? -res : 1; // Special fix to preserve items with equal values
    

    Your code will likely fail with "Comparison method violates its general contract!"

  • getRandom again makes it hard to move to mult-threaded. Consider using java.util.concurrent.ThreadLocalRandom

The code is good, and hangs together 'quite well.

Source Link
rolfl
  • 98.1k
  • 17
  • 219
  • 419

###AbstractScorer

  • Are you sure you need the precision of double for scoring? I only ask because I recently had reason to down-convert a system to float because it had many millions of scorable items and the memory saving ended up being significant.

  • toString() It is best practice to put a toString on each and every class you implement. The toString method is there to communicate with yourself (when debugging) and other programmers who unfortunately may need to devour your stack traces. There is no benefit in having a toString on an abstract class. Your toString, giving just the class's simple name, is even worst than the default Object.toString()

###FieldScore

  • This is an accumulator for the score attached to a field. It could potentially contain multiple scores. I am disappointed that it is not thread-safe. First impression is that it would be a useful optimization to have the fields all scored by different scorers at the same time. This would not be possible with the current FieldScore.

###FieldScoreProducer

  • Some documentation here would be nice. I am not certain 'producer' is the way I would describe the class, but then I am not sure what I would recommend instead.

  • The analyze() method has Generics with Object type. This should be resolved with an additional Generic type on the class. Object is a poor declaration, and it indicates a lack of structure.

  • detailed is odd. I can't see why it is not a final field. The setDetailed should be replaced with a flag on the constructor.

###FieldScores

  • It is customary to put the Constructors before the methods of the class... (and after static declarations and methods). You have some regular methods before the Constructor.

  • private final Map<F, FieldScore<F>> scores is a problem. The key of the map is of type F, but there is no indication as to what that F class is. As a result, you have no idea of whether F implements hashCode() and equals() in a way that is compatible with the Map contract. I would recommend that you declare that class as an IdentityHashMap just in case.

-detailed I don't like that it is not final. Also, you have a setDetailed() method but no isDetailed(). It should be set as part of the constructor as a final.

  • you have the word analyzes in various forms throughout the code. I think the right word is analyzers.

  • getAnalyzes() should wrap the return value in the more lightweight Collections.unmodifiableMap(....) rather than creating a new HashMap

  • getScores() ditto

###PostScorer

  • again with the toString() on an abstract class.

###PreScorer

  • PostScorer is an abstract class, but PreScorer` is an interface. I would expect them to be mirrors of each other, but they are not.

  • The method onScoringComplete should be something like onPreScoringComplete, right?

  • analyze should be returning some generic type, not Object.

  • I would expect the methods on PreScorer and PostScorer to be similar to each other, but they are not... at all.

  • Again, I would expect a more complicated <P, F> signature on the PreScorer... without it, is it doing something wrong?

###ScoreConfig

  • in all other methods you have been careful to return copies of the stored structures, but in this class you return the basic values. Should the methods use Collections.unmodifiableMap(....) or new HashMap(....) instead?

###ScoreConfigFactory

  • why have a factory class and method if the constructors are public anyway?

###ScoreSet

  • since when does this make sense?

    public class ScoreSet<P, F> extends LinkedHashMap<AbstractScorer<P, F>, Double>
    

    ???? ;-)

###ScoreTools

  • The method entriesSortedByValues creates an anonymous Comparator. Comparators are thread-safe and re-entran, and, as a result, it is just a waste of time to create one each time the method is called. Something like:

    private static final ASCENDING_VALUE_COMPARATOR = new Comparator<Map.Entry<K, V>>() { @Override public int compare(Map.Entry<K, V> e1, Map.Entry<K, V> e2) { int res; e1.getValue().compareTo(e2.getValue()); else res = e2.getValue().compareTo(e1.getValue()); return res != 0 ? -res : 1; // Special fix to preserve items with equal values } }

  • While in the process of doing that last one above, it appeared to me that the comparators are the wrong way around in there:

    if (descending) res = e1.getValue().compareTo(e2.getValue());
    else res = e2.getValue().compareTo(e1.getValue());
    
  • This line is highly suspicious... this makes the comparator non-transitive. With a comparator, compare(a, b) should be the same sign as -compare(b,a), but, in your case, if a.equals(b), then your code will return 1 in both cases.

    return res != 0 ? -res : 1; // Special fix to preserve items with equal values

    Your code will likely fail with "Comparison method violates its general contract!"

  • getRandom again makes it hard to move to mult-threaded. Consider using java.util.concurrent.ThreadLocalRandom

##Conclusion

Currently it appears that is works, but there is some missing details when it comes to being a multi-threaded application. I see a number of benefits in that direction.

The Code is good, and hangs together 'quite well.

lang-java

AltStyle によって変換されたページ (->オリジナル) /