1

I am required to create a class very similar to String however instead of storing an array of characters, the object must store an array of bytes because I will be dealing with binary data, not strings.

I am using HashMaps within my application. I am therefore keen to make my custom byteArray class immutable since immutable objects perform faster searches in hashmaps. (I would like a source for this fact please)

I'm pretty sure my class is immutable, but its still performing poorly vs string in hashmap searches. How can I be sure it is immutable?

Danny Rancher
  • 1,923
  • 3
  • 24
  • 43
  • 1
    Make its fields `final`. If any fields are of an array type, don't make them accessible (same for any other mutable reference type). – Sotirios Delimanolis Feb 01 '14 at 00:58
  • 4
    Why are immutable objects in hashmaps so effective? http://stackoverflow.com/questions/10342859/why-are-immutable-objects-in-hashmaps-so-effective – alex Feb 01 '14 at 01:00
  • The comment above is good. You should look at the code for the answer to the question. It answers WHY strings are faster. You should do the same thing in your code to improve performance. – Nick Humrich Feb 01 '14 at 01:05
  • So how can I "cache the hash after it had been calculated once" in my class? or is this feature unavailable to custom classes – Danny Rancher Feb 01 '14 at 01:07
  • 1
    It's not a Java feature, it's something you can write yourself, and something the authors of String wrote themselves. All classes are custom classes. – user253751 Feb 01 '14 at 01:08
  • There must be the class that I want already written though right? an encapsulated byte array instead of an encapsulated char array as in String – Danny Rancher Feb 01 '14 at 01:09
  • You need to write a custom hashCode() that caches the hash after you've computed it, see @alex's link – dfb Feb 01 '14 at 01:25
  • If there is no way from outside the class to modify it's internals, it's immutable (or as close as you can get in Java). – Hot Licks Feb 01 '14 at 02:26

3 Answers3

2

The most important thing is to copy the bytes into your array. If you have

this.bytes = passedInArray;

The caller can modify passedInArray and hence modify this.bytes. You must do

this.bytes = Arrays.copyOf(passedInArray, passedInArray.length);

(Or similar, clone is o.k. too). If this class will be mainly used as a key in Maps, I'd calculate the hashcode immediately (in the constructor), simpler than doing it lazily.

Implement the obvious equals() and I think you are done.

user949300
  • 15,364
  • 7
  • 35
  • 66
  • I have been using .clone() Is this the same? Ah, so your saying call myHashCode() method in constructor which updates final int hashCode field. then in hashCode() overridden method I can just return the hashCode field? – Danny Rancher Feb 01 '14 at 02:34
  • Yes, clone should be o.k. (have to admit, I avoid clone cause its harder/tricky to use in many cases). And yes, call myHashCode in the constructor and set a final field. – user949300 Feb 01 '14 at 02:44
2

Your question is "How can I be sure that my class is immutable?" I'm not sure that's what you mean to ask, but the way to make your class immutable is listed by Josh Bloch in Effective Java, 2nd Ed. in item 15 here, and which I'll summarize in this answer:

  1. Don't provide any mutator methods (methods that change the object's state, usually called "setters").
  2. Ensure the class can't be extended. Generally, make the class final. This keeps others from subclassing it and modifying protected fields.
  3. Make all fields final, so you can't change them.
  4. Make all fields private, so others can't change them.
  5. "Ensure exclusive access to mutable components." That is, if something else points to the data and therefore can alter it, make a defensive copy (as @user949300 pointed out).

Note that immutable objects don't automatically yield a the big performance boost. The boost from immutable objects would be from not having to lock or copy the object, and from reusing it instead of creating a new one. I believe the searches in HashMap use the class' hashCode() method, and the lookup should be O(c), or constant-time and fast. If you are having performance issues, you may need to look at if there's slowness in your hashCode() method (unlikely), or issues elsewhere.

One possibility is if you have implemented hashCode() poorly (or not at all) and this is causing a large number of collisions in your HashMap -- that is, calling that method with different instances of your class returns mostly similar or same values -- then the instances will be stored in a linked list at the location specified by hashCode(). Traversing this list will convert your efficiency from constant-time to linear-time, making performance much worse.

einnocent
  • 3,567
  • 4
  • 32
  • 42
  • #4 is unnecessary (if you do #3) but otherwise all good points. – user949300 Feb 01 '14 at 06:42
  • @user949300 True, and Bloch agrees with you, though he goes on to note that it is a best practice to make fields private so you can change their internal representation at a later date. – einnocent Feb 01 '14 at 16:02
0

since immutable objects perform faster searches in hashmaps. (I would like a source for this fact please)

No, this isn't true. Performance as a hashmap key will be determined by the runtime, and collision avoidance, of hashCode.

I'm pretty sure my class is immutable, but its still performing poorly vs string in hashmap searches. How can I be sure it is immutable?

Your problem is more likely to be a poor choice of hashCode implementation. Consider basing your implementation around Arrays.hashCode.

(Your question ArrayList<Byte> vs String in Java suggests you're trying to tune a specific implementation; the advice there to use byte[] is good.)

Community
  • 1
  • 1
Joe
  • 29,416
  • 12
  • 68
  • 88
  • Thanks for your answer and clarification. I have written a byte array wrapper. My hashcode method reads: hashCode() { return Arrays.hashCode(byteArray); } I can imagine this is causing more collisions than it should due to the fact that byte is signed. – Danny Rancher Feb 01 '14 at 03:41