return to lecture notes index

15-100 Lecture 31 (Friday, November 18, 2005)

Counted Collection -- Overview

One of the nine mastery Exam patterns is the Counted Collection. Now the way that they set this up is not the way that you would ever actually do this class. The reason that they did it this way is because they wanted you to have to do certain things, and not all the classes have used interfaces and they only wanted to have two classes.

If you just think about classes in java you will also see why Counted Collections aren't the way to do. Just think of a collection of cars. What if this collection of cars was a countable collection. In that case if you created a "Ford Focus" then that class would have to keep track of all the "Ford Focuses" that exist. This doesn't make much sense though. If we want a Ford Focus then we probably don't usually care how many exist in the world, and it may not even be possible to find how many exist in the world. What we really want to do is to create a Ford Focus, and then if an application cares how many there are then a count of Ford Focuses can be kept outside of the class.

Before we take a look at some code that follows the final exam's idiom, let's consider some that does it right. For a correct solution, we'll add an intermediate abstraction. This abstraction will contain both a reference to any Object and a count. Our collection will collect these new abstractions instead of collecting the items directly. This allows the collection to collect anything -- while keeping a count.

I like to think of the collection as an old-fashioned paper inventory. Each line contains an items's name (a reference) and a count of those items. The inventory is a collection of these "line items".

Consider the solution below:


  private class LineItem {
    private String item; //holds the name of the item

    //holds the number of this item that we have
    private int count;  

//if we're not provided with a count for this item 
//the default is 1
    private static final int DEFAULT_COUNT = 1;

  //Constructors
    public LineItem(String item, int count) {
      this.item = item;
      this.count = count;
    }

    public LineItem(Object item) {
      this.item = item;
      this.count = DEFAULT_COUNT;
    }

    //returns the count of this item
    public int getCount() {
      return count;
    }

   //increases count by the specified value
   public void changeCountBy (int change) {
     count += change;
   }
   
   //returns the name of the item
    public String  getName() {
      return item;
    }

   //returns the string representation of the item
    public String toString() {
      return "" + item + ": " + count;
    }

    //defines an equals methods for this object
    public boolean equals(Object o) {
      LineItem li = (LineItem) o;
      return item.equals(li.item);
    }
  }

 

So now we have created out CountableClass, so now lets create a collection of them. Let's make sure that it has some of the same types of methods that the mastery exam will have.


class CountedCollection { private LineItem[] items; //the collection private int count; //number of items in the collection private static final int DEFAULT_SIZE = 100; private static final double GROUWTH_AMOUNT = 1.0; //Constructors public CountedCollection() { items = new LineItem[DEFAULT_SIZE]; count = 0; } public CountedCollection(int size) { items = new LineItem[size]; count = 0; } //increases the list size by a specified percentage private void grow() { LineItem[] biggerArray = new LineItem[(int) ((1.0+GROWTH_AMOUNT) * items.length)]; for (int index=0; index < count; index++) biggerArray[index] = items[index]; items = biggerArray; } //inserts the item at the end private void insert (LineItem item) { if (count == items.length) grow(); items[count++] = item; // items[count] = item; count++; } public void add (Object item, int count) { for (int index=0; index < this.count; index++) { if (items[index].getItem().equals(item)) { items[index].changeCountBy(count); return; } } insert (new LineItem(item, count)); } //Removes the item at the given index private void removeItemAtIndex(int index) { for ( ; index < (count-1); index++) { items[index+1] = items[index]; } count--; items[count] = null; } //removes the first item equal to the item //passed in public void remove (Object item, int count) { for (int index=0; index < count; index++) { if (items[index].getItem().equals(item)) { items[index].changeCountBy(-1*count); if (items[index].getCount() == 0) removeItemAtIndex (index); return; } } } //returns the number of each item in the list public int getQuantity(Object item) { for (int index=0; index < count; index++) { if (items[index].getItem().equals(item)) { return items[index].getCount(); } } return 0; // thrown an ItemNotFoundException ? } }

Back to the Mastery

But, what to say? The final exam does it weird -- so we'll have to play along. Notice we have a collection of Items and that our collection only works with Items.

Imagine if you needed to solve this problem a few time: cut-and-paste, search-and-replace. Yuck!


class Item {
  private String name;
  private int count;

  private static final int DEFAULT_COUNT = 1;
  
  //the mastery's constructor will contain some type of logic (if statements)
  public Item (String name, int count) {
    this.name = name;
    this.count = count;
  }

  public Item (String name) {
    this.name = name;
    count = DEFAULT_COUNT;
  }

  //the accessors
	
  public int getCount(){
    return count;
  }

  public String getName() {
    return name;
  } 

  // the modifiers
	
  public void changeCountBy(int change) {
    count += change;
  }

  public String toString() {
    return name + ": " + count;
  }

  public boolean equals(Object o){
    Item item = (Item)o;

    if (!this.name.equals(item.name))
      return false;

    if (this.count != item.count)
      return false;

    return true;
  }
}

CountedCollection Class


class CountedCollection {
  private Item[] = items;
  private int count;

  public CountedCollection() {
    items = new Item[1];
    count = 0;
  }

  //not provided
  public CountedCollection(int size) {
    items = new Item[size];
    count = 0;
  }

  private void grow(){
    Item [] biggerArray = new Item[2*items.length];
    for (int index = 0; index < count; index++)
      biggerArray[index] = items[index];

    items = biggerArray;
  }

  private void insertAtEnd(Item item) {
    if (count == items.length)
      grow();

    items[count++] = item;
  }

  //we look for the item in the collection.  If it is there then we increase its
  //count by one.  Other wise we insert the newItem at the end. 
  private void insert (Item newItem){
    location = findLocation(newItem);
    if (location >= 0)
      item[location].changeCountBy(1);
    else
      insertAtEnd(newItem);
  }

  private int findLocation (Item item) {
    for (int i = o; i < count; i++)
      if (items[i].getName().equals(item.getName())
        return i;

    return -1;
  }

  public void remove(Item item, int count) {
    int location = findLocation(item);

    if(location < 0)
      return;

    items[location].changeCountBy(count);

    //if there are still one or more items of that type, we are done...exit the method
		if (items[location].getCount() > 0)
      return;

    //shift each item behind location forward by one (to cover up hole caused by
    //removing the item)
    for (int i = location; i < (count-1); index ++){
      items[index] = items[index+1];
      items[index+1] = null;
    }
    count--;
  }
}