ADS-Lecture 11 The End of Java

Lecture 11 The End of Java

Lists and Sets in Java

Lists in Real Java Code

We built a list from scratch, but Java provides a built-in List interface and several implementations, e.g. ArrayList.

java.util.List<Integer> L = new java.util.ArrayList<>();
L.add(5);
L.add(10);
L.add(15);
System.out.println(L);

By including import java.util.List and import java.util.ArrayList at the top of the file, we can make our code more compact.

import java.util.List;
import java.util.ArrayList;
 
public class SimpleBuiltInListExample {
    public static void main(String[] args) {
        List<Integer> L = new ArrayList<>();
        /* If we import, we can use the “simple name” (ArrayList) 
        * as opposed to the longer “canonical name” (java.util.ArrayList). */
        L.add(5);
        L.add(10);
        L.add(15);
        System.out.println(L);
  }
}
Sets and ArraySet

Another handy data structure is the set.

  • Stores a set of values with no duplicates. Has no sense of order.

Today we’re going to write our own Set called ArraySet.

  • Won’t be implementing any specific interface (for now).
ArraySet<String> S = new ArraySet<>();
S.add("Tokyo");
S.add("Beijing");	
S.add("Lagos");
S.add("São Paulo");
System.out.println(S.contains("Tokyo"));
System.out.println(S.size());
Goals

Goal 1: Create a class ArraySet with the following methods:

  • add(value): Add the value to the ArraySet if it is not already present.

  • contains(value): Checks to see if ArraySet contains the key.

  • size(): Returns number of values.

Ok to ignore resizing for this exercise.

  • In lecture, I’ll just give away the answer, but you might find implementing it useful.
ArraySet (Basic Implementation)

Array implementation of a Set:

  • Use an array as the core data structure.

  • contains(x): Checks to see if x is in the underlying array.

  • add(x): Checks to see if x is in the underlying array, and if not, adds it.

public class ArraySet<T> {
    private T[] items;
    private int size;
 
    public ArraySet() {
        items = (T[]) new Object[100];
        size = 0;
    }
  
    public boolean contains(T x) {
        for (int i = 0; i < size; i += 1) {
            if (items[i].equals(x)) {
                return true;
            }
        }
        return false;
    }
    
    public void add(T x) {
        if (!contains(x)) {
            items[size] = x;
            size += 1;
        }
    }
}

Exceptions

Basic idea:

  • When something goes really wrong, break the normal flow of control.

  • So far, we’ve only seen implicit exceptions, like the one below.

public static void main(String[] args) {
    ArraySet<String> s = new ArraySet<>();
    s.add(null);
    s.add("horse");
}
$ java ExceptionDemo
Exception in thread "main" java.lang.NullPointerException
	at ArraySet.contains(ArraySet.java:16)
	at ArraySet.add(ArraySet.java:26)
	at ArraySet.main(ArraySet.java:40)
Explicit Exceptions

We can also throw our own exceptions using the throw keyword.

  • Can provide more informative message to a user.

  • Can provide more information to code that “catches” the exception.

    More on “catching” at end of the course.

public void add(T x) {
    if (x == null) {
        throw new IllegalArgumentException("Cannot add null!");
    }
    ...
}
$ java ExceptionDemo
Exception in thread "main" 
java.lang.IllegalArgumentException: Cannot add null!
	at ArraySet.add(ArraySet.java:27)
	at ArraySet.main(ArraySet.java:42)

Arguably this is a bad exception.

  • Our code now crashes when someone tries to add a null.

  • Other fixes:

    • Ignore nulls.
    • Fix contains so that it doesn’t crash if items[i] is null.

Iteration

The Enhanced For Loop

Java allows us to iterate through Lists and Sets using a convenient shorthand syntax sometimes called the foreach or enhanced for loop.

Set<Integer> javaset = new HashSet<>();
javaset.add(5);
javaset.add(23);
javaset.add(42);
for (int i : javaset) {
    System.out.println(i);
}
  • This doesn’t work with our ArraySet.
  • Let’s strip away the magic so we can build our own classes that support this.
ArraySet<Integer> aset = new ArraySet<>();
aset.add(5);
aset.add(23);
aset.add(42);
for (int i : aset) {
    System.out.println(i);
}
$ javac IterationDemo
error: for-each not applicable to expression type
        for (int i : S) {
                     ^
  required: array or java.lang.Iterable
  found:    ArraySet<Integer>
How Iteration Really Works

An alternate, uglier way to iterate through a List is to use the iterator() method.

public Iterator<E> iterator();
//"Nice" iteration
Set<Integer> javaset = new HashSet<Integer>();
    ...
for (int x : javaset) {
    System.out.println(x);
}
//"Ugly" iteration.
Set<Integer> javaset = new HashSet<Integer>();
    ...
Iterator<Integer> seer = javaset.iterator();
while (seer.hasNext()) {
    System.out.println(seer.next());
}
Supporting Ugly Iteration in ArraySets

To support ugly iteration:

  • Add an iterator() method to ArraySet that returns an Iterator<T>.

  • The Iterator<T> that we return should have a useful hasNext() and next() method.

public interface Iterator<T> {
	boolean hasNext();
    T next();
}
Iterator<Integer> aseer = aset.iterator();
while (aseer.hasNext()) {
    System.out.println(aseer.next());
}
Completed ArraySet iterator Method
private class ArraySetIterator implements Iterator<T> {
	private int wizPos;
 	public ArraySetIterator() { 
        wizPos = 0; 
    }
	public boolean hasNext() { 
        return wizPos < size; 
    }
 	public T next() {
    	T returnItem = items[wizPos];
    	wizPos += 1;
    	return returnItem;
	}
}
public Iterator<T> iterator() {
	return new ArraySetIterator();
}
The Enhanced For Loop

Our code now supports “ugly” iteration, but enhanced for loop still doesn’t work.

The problem: Java isn’t smart enough to realize that our ArraySet has an iterator() method.

  • Luckily there’s an interface for that.

To support the enhanced for loop, we need to make ArraySet implement the Iterable interface.

  • There are also some default methods in Iterable, not shown.
public interface Iterable<T> {
    Iterator<T> iterator();
}
public class ArraySet<T> implements Iterable<T> {
    ...
    public Iterator<T> iterator() { ... }
}
Iteration Summary

To support the enhanced for loop:

  • Add an iterator() method to your class that returns an Iterator<T>.

  • The Iterator<T> returned should have a useful hasNext() and next() method.

  • Add implements Iterable<T> to the line defining your class.

Object Methods:Equals and toString()

Objects

All classes are hyponyms of Object.

  • String toString()

  • boolean equals(Object obj)

  • Class<?> getClass()

  • int hashCode()

    • Very important, but will disscuss later.
  • protected Object clone()

  • protected void finalize()

  • void notify()

  • void notifyAll()

  • void wait()

  • void wait(long timeout)

  • void wait(long timeout, int nanos)

toString()

The toString() method provides a string representation of an object.

  • System.out.println(Object x) calls x.toString()

    • If you’re curious: println calls String.valueOf which calls toString.
  • The implementation of toString() in Object is the the name of the class, then an @ sign, then the memory location of the object.

ArraySet toString

Let’s try implementing toString for ArraySet.

One approach is shown below.

  • Warning: This code is slow. Intuition: Adding even a single character to a string creates an entirely new string. It’s because Strings are “immutable”.
@Override
public String toString() {
    String returnString = "{";
    for (int i = 0; i < size; i += 1) {
        returnString += keys[i];
        returnString += ", ";
    }
    returnString += "}";
    return returnString;
}

Much faster approach is shown below.

  • Intuition: Append operation for a StringBuilder is fast.
@Override
public String toString() {
    StringBuilder returnSB = new StringBuilder("{");
    for (int i = 0; i < size; i += 1) {
        returnSB.append(items[i]);
        returnSB.append(", ");
    }
    returnSB.append("}");
    return returnSB.toString();
}
Equals vs. ==

As mentioned in an offhand manner previously, == and .equals() behave differently.

  • == compares the bits. For references, == means “referencing the same object.”

To test equality in the sense we usually mean it, use:

  • .equals for classes. Requires writing a .equals method for your classes.

    • Default implementation of .equals uses == (probably not what you want).
  • BTW: Use Arrays.equal or Arrays.deepEquals for arrays.

The Default Implementation of Equals
ArraySet<Integer> aset = new ArraySet<>();
aset.add(5);
aset.add(23);
aset.add(42);
 
System.out.println(aset);
 
ArraySet<Integer> aset2 = new ArraySet<>();
aset2.add(5);
aset2.add(23);
aset2.add(42);
 
System.out.println(aset.equals(aset2));
/* Returns false because the default implementation of equals just uses ==. */
ArraySet equals

The implementation below is a good start, but fails if o is null or another class.

@Override
public boolean equals(Object o) {
    ArraySet<T> other = (ArraySet<T>) o;
    if (this.size() != other.size()) { return false; }
    for (T item : this) {
        if (!other.contains(item)) {
            return false;
        }
    }
    return true;
}

The implementation below is much better, but we can speed things up.

@Override
public boolean equals(Object o) {
    if (o == null) { return false; }
    if (this.getClass() != o.getClass()) { return false; }
    ArraySet<T> other = (ArraySet<T>) o;
    if (this.size() != other.size()) { return false; }
    for (T item : this) {
        if (!other.contains(item)) {
            return false;
        }
    }
    return true;
}

The code below is pretty close to what a standard equals method looks like.

@Override
public boolean equals(Object o) {
    if (o == null) { return false; }
    if (this == o) { return true; } // optimization
    if (this.getClass() != o.getClass()) { return false; }
    ArraySet<T> other = (ArraySet<T>) o;
    if (this.size() != other.size()) { return false; }
    for (T item : this) {
       if (!other.contains(item)) {
            return false;
        }
    }
    return true;
}
Summary

We built our own Array based Set implementation.

To make it more industrial strength we:

  • Added an exception if a user tried to add null to the set.

    • There are other ways to deal with nulls. Our choice was arguably bad.
  • Added support for “ugly” then “nice” iteration.

    • Ugly iteration: Creating a subclass with next and hasNext methods.
    • Nice iteration: Declaring that ArraySet implements Iterable.
  • Added a toString() method.

    • Beware of String concatenation.
  • Added an equals(Object) method.

    • Make sure to deal with null and non-ArraySet arguments!
    • Used getClass to check the class of the passed object. Use sparingly.

Even Better toString and ArraySet.of

@Override
public String toString() {
    List<String> listOfItems = new ArrayList<>();
    for (T x : this) {
        listOfItems.add(x.toString());
    }
    return "{" + String.join(", ", listOfItems) + "}";
}
public static <Glerp> ArraySet<Glerp> of(Glerp... stuff) {
    ArraySet<Glerp> returnSet = new ArraySet<Glerp>();
    for (Glerp x: stuff) {
        returnSet.add(x);
    }
    return returnSet;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值