Return to lecture notes index
15-100 Lecture 30 (November 16, 2005)

Sets

Today's goal is to construct a Set using an array. We're using the word set just as you did in high school math. It is basically a collection of things. We talk about membership within a set, the union and intersection of sets, &c.

Specifically, we'll use an array of Comparables as the basis of the set. We're using Comparable instead of Object, because keeping the storage sorted will make some operations more efficient.

Below is a minimal set. It really is only minimally different than the OrderedCollection:


class Set {

  private Comparable[] members;
  private int nextSlot;
  
  
  private static final int DEFAULT_SIZE = 10;
  private static final double GROWTH_FACTOR = 2.0;
  private static final String NL = System.getProperty ("line_separator");


  public Set() {
  
    members = new Comparable[DEFAULT_SIZE];
    nextSlot = 0;
  }
  
  
  public Set (int size) {
    members = new Comparable[size];
    nextSlot = 0;
  }
  

  public void add (Comparable item) {
  
    if (members.length == nextSlot) 
      grow();
  
    int hole = nextSlot;
    for (      ; hole > 0; hole--) {
    
      if (members[hole-1].compareTo(item) <= 0)
        break;  
    }
    
    members[hole] = item;
    nextSlot++; 
  }
  
  
  public boolean isMember(Comparable item) {
  
    for (int index=0; index < nextSlot; index++) {
      if (members[index].equals(item) return true;
    }
    
    return false;
  
  }
  
  
  private void grow() {
  
    Comparable[] biggerList = new Comparable[(int)(GROWTH_FACTOR *list.length)];
    
    for (int index=0; index < nextSlot; index++) 
      biggerList[index] = list[index];
      
    
    list = biggerList;
  }
}

Union

Recall that the union of two sets consists of each and every object that appears in either, or both, of the two sets (and since they are sets their are no repeats of the objects -- just simple membership).

We could just traverse each of the sets, and as we did, add each item to the new set if, and only if, it wasn't present in the second of the original sets. And, there is certainly nothing wrong with this approach -- it is the best we could do, if the underlying array were unordered.

But, given that we've got ordered arrays, we can do better. If we use the method described above, we'll start our search at the beginning of the array each time. Because the list is necessary, because the list is ordered. We can simply remember where we left off and search from there next time. For example, if we have sets of numbers and encounter a 5 in the first of the original sets, the next number must be greater than 5. As a result, we don't need to search numbers less than 5 in the second set again. We can just pick up where we left off (the first item less than or equal to 5).


  public Set union (Set otherSet) {

    Set unionSet = new Set (nextSlot + otherSet.nextSlot);

    int thisIndex = 0;
    int otherIndex = 0;

    while ((thisIndex < nextSlot) && (otherIndex < otherSet.nextSlot)) {

      // special case -- eliminate duplicate
      if (members[thisIndex].compareTo(otherSet.members[otherIndex] == 0) {
        unionSet.members[unionSet.nextSlot++] = members[thisIndex];
        thisIndex++, otherIndex++;
        continue;
      }

      if (members[thisIndex].compareTo(otherSet.members[otherIndex] < 0)
        unionSet.members[unionSet.nextSlot++] = members[thisIndex++];
      else
        unionSet.members[unionSet.nextSlot++] = otherSet.members[otherIndex++];   
    }

    while (thisIndex < nextSlot) {
      unionSet.members[unionSet.nextSlot++] = members[thisIndex++];
    }

    while (otherIndex < otherSet.nextSlot) {
      unionSet.members[unionSet.nextSlot++] = otherSet.members[otherIndex++];
    }

    return unionSet;

  }

Intersection

The intersection of two sets should be familiar -- it contains only those items that overlap between the two sets. In other words, the intersection of two sets contains only those items that the two sets have in common.

If we were playing with unordered list, the approach would be very straight-forward. We'd walk through the first set, checking for each item we encountered within the second set. If and only if the item was also found in the second set, we'd add it to the new intersection set.

But, again, because the underlying array is ordered, we can do better by confining the search to items we haven't already considered. The approach should look very familiar. We are searching the second list instead of the result list. And, we add it if, and only if, we do find it (instead of the other way around). But, the traverse-and-search approach is basically the same.

  public Set intersection (Set otherSet) {

    Set intersectionSet = new
        Set (nextSlot < otherSet.nextSlot ? nextSlot : otherSet.nextSlot);


    int otherIndex = 0;
    for (int thisIndex = 0; thisIndex < nextSlot; thisIndex++) {

       for (    ;
            (otherIndex < otherSet.nextSlot) &&
               members[thisIndex].compareTo(otherSet.members[otherIndex]) <= 0);
            otherIndex++)
       ;

       if (otherIndex >= otherSet.nextSlot)
         break;

       if (members[thisIndex].compareTo(otherSet.members[otherIndex]) == 0) {
         intersectionSet.members[insersectionSet.nextSlot++] =
              members[thisIndex];
         otherIndex++;
       }

    }

    return intersectionSet;

  }

The Whole Thing

class Set {

  private Comparable[] members;
  private int nextSlot;
  
  
  private static final int DEFAULT_SIZE = 10;
  private static final double GROWTH_FACTOR = 2.0;
  private static final String NL = System.getProperty ("line_separator");


  public Set() {
  
    members = new Comparable[DEFAULT_SIZE];
    nextSlot = 0;
  }
  
  
  public Set (int size) {
    members = new Comparable[size];
    nextSlot = 0;
  }
  

  public void add (Comparable item) {
  
    if (members.length == nextSlot) 
      grow();
  
    int hole = nextSlot;
    for (      ; hole > 0; hole--) {
    
      if (members[hole-1].compareTo(item) <= 0)
        break;  
    }
    
    members[hole] = item;
    nextSlot++; 
  }
  
  
  public boolean isMember(Comparable item) {
  
    for (int index=0; index < nextSlot; index++) {
      if (members[index].equals(item) return true;
    }
    
    return false;
  
  }
  
  
  public Set union (Set otherSet) {
  
    Set unionSet = new Set (nextSlot + otherSet.nextSlot);
    
    int thisIndex = 0;
    int otherIndex = 0;
    
    while ((thisIndex < nextSlot) && (otherIndex < otherSet.nextSlot)) {
    
      // special case -- eliminate duplicate
      if (members[thisIndex].compareTo(otherSet.members[otherIndex] == 0) {
        unionSet.members[unionSet.nextSlot++] = members[thisIndex];
        thisIndex++, otherIndex++;
        continue;
      }
      
      if (members[thisIndex].compareTo(otherSet.members[otherIndex] < 0)
        unionSet.members[unionSet.nextSlot++] = members[thisIndex++];
      else
        unionSet.members[unionSet.nextSlot++] = otherSet.members[otherIndex++];    
    }
    
    while (thisIndex < nextSlot) {
      unionSet.members[unionSet.nextSlot++] = members[thisIndex++];
    }
    
    while (otherIndex < otherSet.nextSlot) {
      unionSet.members[unionSet.nextSlot++] = otherSet.members[otherIndex++];
    }
        
    return unionSet;
  
  }
  
  
  public Set intersection (Set otherSet) {
  
    Set intersectionSet = new 
        Set (nextSlot < otherSet.nextSlot ? nextSlot : otherSet.nextSlot);
  
  
    int otherIndex = 0; 
    for (int thisIndex = 0; thisIndex < nextSlot; thisIndex++) {
  
       for (    ; 
            (otherIndex < otherSet.nextSlot) &&
               members[thisIndex].compareTo(otherSet.members[otherIndex]) <= 0); 
            otherIndex++)
       ;     
                   
       if (otherIndex >= otherSet.nextSlot)
         break;
    
       if (members[thisIndex].compareTo(otherSet.members[otherIndex]) == 0) {
         intersectionSet.members[insersectionSet.nextSlot++] = 
              members[thisIndex];
         otherIndex++;
       }
    
    }
    
    return intersectionSet;
  
  }
  
  

  
  
  private void grow() {
  
    Comparable[] biggerList = new Comparable[(int)(GROWTH_FACTOR *list.length)];
    
    for (int index=0; index < nextSlot; index++) 
      biggerList[index] = list[index];
      
    
    list = biggerList;
  }
  
  



}