Michael Bolin's 6.170 Recitation

This web site is for members of the recitation that I am teaching for 6.170 Spring 2004. The recitation meets Thursday mornings from 10-11am in 26-210. Here you will find recitation notes as well as links to software development tools that I consider important.
Recitation Notes
2/5 Recitation 1
2/12 Recitation 2
2/19 Recitation 3
2/26 Recitation 4
3/4 Recitation 5
Comments on Problem Sets
Problem Set 0
Problem Set 1
Problem Set 3
Things I Recommend
Software You Should Install
Books You Should Own
Web Sites You Should Use

Comments on Problem Set 1

I pointed out a number of problems on your Problem Set 1 that I did not take off for because you are new to Java and obviously cannot be expected to know everything. However, the list below details the major errors that I saw in Problem Set 1, so since they have now been pointed out to you explicitly, I expect you to be responsible for knowing how to avoid these errors in the future.
Documenting Unit Tests
Comment the module that passed the tests, NOT the test that the modules passed. The test should be an unchanging piece of code, so it should not get edited every time a module that passes the test is written. The module depends on the test, not the other way around. Thus, the module should contain the comment. (Further, sometimes you may not have access to the source code for the test, in which case this point is moot.)
Put the comment about which tests were passed at the top of the class that passes the test suite instead of buried within your code. This makes it easier for other developers to find this information.
The comment should specify which tests were passed (and ideally, when the test was run and by whom, if it is a team project). The comment "passed all tests" is not as helpful to other developers as the comment: "ps1.cards.Deck passed all tests in ps1.cards.DeckTest on 2/12/04."
Formatting Your Code
Java is not a whitespace-delimited language, so please do not treat it like one, even though such syntax may compile. Thus, instead of this:
if (valComp < 0)
  return true;
else if (valComp > 0)
  return false;
else
  return suitComp;
Do this:
if (valComp < 0) {
  return true;
} else if (valComp > 0) {
  return false;
} else {
  return suitComp;
}
In practice, the former is more error-prone, so please avoid it.
Some of you have indentation issues – just remember that every new block should be indented. Thus, instead of:
if (!cards.contains(c)) {
cards.add(c);
}
Do this:
if (!cards.contains(c)) {
  cards.add(c);
}
You can format your code in Eclipse by pressing Ctrl-Shift-F. Please make sure that your code is formatted before submitting it.
Restrict the width of every line to 80 characters. If you use Ctrl-Shift-F in Eclipse, it should take care of this for you. This is an old standard, dating back to times when terminals were only 80 characters wide; however, it is still honored today for various reasons. One of which is that it makes your code easier to read when I print it out!
Performance Issues
Use an Iterator to loop over the elements in a Collection instead of a for loop that uses get(i). When your Collection is a LinkedList, looping over your Collection using get(i) will be quadratic in performance whereas looping over it using an Iterator will be linear in performance.
Minimize the number of casts and calls to instanceof in your code. Both of these operations are relatively expensive, so try to avoid them as much as possible. Instead of this:
public int compareTo(Object o) {
  int valComp = this.getValue().compareTo(((Card)o).getValue());
  int suitComp = this.getSuit().compareTo(((Card)o).getSuit());
  // other stuff...
}
Do this:
public int compareTo(Object o) {
  Card c = (Card)o;
  int valComp = this.getValue().compareTo(c.getValue());
  int suitComp = this.getSuit().compareTo(c.getSuit());
  // other stuff...
}
This improves performance as well as readability.
Avoid redundant code. In the table below, verbose code that I saw in problem sets is on the left with its simplified equivalent on the right:

if (containsObj) {
  return true;
} else {
  return false;
}
return containsObj;
if (!(obj instanceof Card)) {
  throw new ClassCastException();
}
Card c = (Card)obj;
Card c = (Card)obj; // may throw ClassCastException
if (this.compareTo(c) == 0) return 0;
else if (this.compareTo(c) < 0) return -1;
else if (this.compareTo(c) > 0) return 1;
else throw new Exception();
return this.compareTo(c);

Writing an equals() Method for Card
Using compareTo to help implement equals is not the best design decision, although the reason is very subtle. The equals method is special in Java – its specification is explicit and somewhat tricky, but it is extremely important that it is met. Thus, think of implementing equals as being the first priority when implementing Card and implementing Comparable as being second priority. If Card no longer implements Comparable, it must still meet the contract for equals, so equals should be developed independently of compareTo. A good equals method for Card is shown below:
public void equals(Object obj) {
  if (!(obj instanceof Card)) return false;
  Card c = (Card)obj;
  return getValue().equals(c.getValue()) &&
         getSuit().equals(c.getSuit());
}
What the final Keyword Means
Declaring a class final does not mean that it is immutable; instead, it means that no class can subclass that class. Classes are often declared final for security reasons (so they are not overridden maliciously), such as String and Integer. There is no keyword in Java to declare a class as immutable – the best you can do is implement your class so that it is immutable and include a note in the class's Javadoc comment that says it is immutable.

If you declare a method final, it means that it cannot be overwritten by a subclass.

Also, you can declare fields of a class as final, but this does not mean that they are immutable. For example, if you have:

private final HashMap map = new HashMap();
That does not mean that map is immutable. Instead, it means that map cannot be reassigned to another HashMap. However, you can still mutate map with a call to map.put().
Avoiding Rep Exposure
Some of you tried to check if a Card was an ace by checking if its name was "Ace"; however, that would break if its name were changed to "ACE". Instead, test if a card is an ace by doing:
boolean isAce = (card.getValue() == CardValue.ACE);
How to Do listCardsAcesLow() Efficiently
Many of you agonized over how to implement listCardsAcesLow() efficiently. Here is what is likely the most succinct implementation:
public Iterator listCardsAcesLow() {
  CardSuit lowSuit = 
   (CardSuit)CardSuit.SUITS.get(CardSuit.SUITS.size() - 1);
  Card lowAce = new Card(CardValue.ACE, lowSuit);
  SortedSet aces = cards.tailSet(lowAce);
  SortedSet nonAces = cards.headSet(lowAce);
  List acesLow = new ArrayList(aces);
  acesLow.addAll(nonAces);
  return acesLow.iterator();
}
Note that lowSuit is created in a way that it will still work if the order of the suits changes.


©2004 Michael Bolin