-3

My intention was to make a caching service for a database results, that I can paginate differently based on client's requests.

So, upon the (search) request I am making a key that is composed of parameters, which are in form of two Map<String, String[]> and a:

public class DocMaintainer {
    public Manipulator creator;
    public Manipulator lastChange;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        DocMaintainer that = (DocMaintainer) o;
        return Objects.equals(creator, that.creator) &&
                Objects.equals(lastChange, that.lastChange);
    }

    @Override
    public int hashCode() {

        return Objects.hash(creator, lastChange);
    }
}


public class Manipulator {
    public Date fromDate;
    public Date toDate;
    public String userId;
    public String system;

    public Manipulator() {
        this.userId = "";
        this.system = "";
        this._fromJoda = new DateTime(Long.MIN_VALUE);
        this._toJoda = new DateTime(Long.MAX_VALUE - DateTimeConstants.MILLIS_PER_WEEK);
    }

    private DateTime _fromJoda;
    private DateTime _toJoda;

    public DateTime get_fromJoda() {
        _fromJoda = fromDate != null ? new DateTime(fromDate) : _fromJoda;
        return _fromJoda;
    }

    public DateTime get_toJoda() {
        _toJoda = toDate != null ? new DateTime(toDate) : _toJoda;
        try {
            _toJoda = _toJoda.plusDays(1);
        } catch (Exception e) {
            System.out.println(e);
        }

        return _toJoda;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Manipulator that = (Manipulator) o;
        return Objects.equals(fromDate, that.fromDate) &&
                Objects.equals(toDate, that.toDate) &&
                Objects.equals(userId, that.userId) &&
                Objects.equals(system, that.system);
    }

    @Override
    public int hashCode() {

        return Objects.hash(fromDate, toDate, userId, system);
    }
}

As you can see I intended to use hashing to create a "key":

public class SearchKey {
    public int conjunctionHash;
    public int disjunctionHash;
    public int maintainerHash;

    public SearchKey(int conjunctionHash, int disjunctionHash, int maintainerHash) {
        this.conjunctionHash = conjunctionHash;
        this.disjunctionHash = disjunctionHash;
        this.maintainerHash = maintainerHash;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        SearchKey searchKey = (SearchKey) o;
        return conjunctionHash == searchKey.conjunctionHash &&
                disjunctionHash == searchKey.disjunctionHash &&
                maintainerHash == searchKey.maintainerHash;
    }

    @Override
    public int hashCode() {

        return Objects.hash(conjunctionHash, disjunctionHash, maintainerHash);
    }
}

a key-object is used directly as a caching key in a singleton service:

@Named
@Singleton
public class SearchCacheSrv {

    private Map<SearchKey, ValidMainteinersList<FindDTO>> cache = new HashMap<>();

    public ValidMainteinersList<FindDTO> getCached(SearchKey searchKey) {
        if (cache.containsKey(searchKey))
            return cache.get(searchKey);
        else
            return new ValidMainteinersList<FindDTO>();
    }

    public SearchKey makeAkey(Map<String, String[]> conjunction,
                              Map<String, String[]> disjunction,
                              DocMaintainer maintainer) {
        return new SearchKey(conjunction.hashCode(), disjunction.hashCode(), maintainer.hashCode());
    }

    public ValidMainteinersList<FindDTO> cache(SearchKey searchKey, ValidMainteinersList<FindDTO> findDTOS) {
        return cache.put(searchKey, findDTOS);
    }

    public void clearCache() {
        cache.clear();
    }
}

Unfortunately this is not behaving the way I expected and I'm getting different hashes/keys generated for the same parameters.

Naturally question is why?

greengold
  • 1,184
  • 3
  • 18
  • 43
  • "Unfortunately this is not behaving the way I expected" What were you expecting? How is it behaving? – Michael Mar 22 '18 at 08:51
  • Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. THIS INTEGER NEED NOT REMAIN CONSISTENT FROM ONE EXECUTION OF AN APPLICATION TO ANOTHER EXECUTION OF THE SAME APPLICATION. – Tino M Thomas Mar 22 '18 at 08:59
  • well, that's what happens, but how come is that? Is an object's instance playing some role in it somehow? any advice how can I make it serve my purpose? – greengold Mar 22 '18 at 09:03
  • Please provide a [mcve] which demonstrates how you use this code, provides example inputs, expected and actual outputs. – Andy Turner Mar 22 '18 at 09:08
  • you have each of my classes listed there. what you want me to do? to bootstrap enterprise java backend deployed to tomcat on the plunker? get some real life – greengold Mar 22 '18 at 09:13
  • @greengold Your (failing) Unit test? :-P – kutschkem Mar 22 '18 at 09:19

2 Answers2

0

The problem here is that the hashCode of an array does not depend on the contents, but on the reference. That means that if you have two conjunction / disjunction keys that are equal, but the contained arrays are not the same objects, then the hashcode of the keys will be different.

The solution that probably takes the least effort is replacing the arrays with ArrayLists, which do base their hashCode on the content.

kutschkem
  • 7,826
  • 3
  • 21
  • 56
0

I actually don't see the point of passing conjunction.hashCode(), ... to your SearchKey constructor; I never had to do it this way, but it could be my mistake. Try passing actual values to your SearchKey class, not hashCodes, so the hashCode method always returns a consistent value.

HBo
  • 635
  • 6
  • 16
  • yeah, it's cleaner that way and the reason I did that was to be more explicit and easier to read. certainly that was not a solution to my issue which got resolved by the accepted answer; – greengold Mar 23 '18 at 08:53