ADS-Lecture 06 Arrays and Lists

Lecture 06 Arrays and Lists

A Last Look at Linked Lists

Doubly Linked Lists

Behold. The state of the art as we arrived at in previous lecture. Through various improvements, we made all of the following operations fast:

  • addFirst, addLast

  • getFirst, getLast

  • removeFirst, removeLast

Arbitrary Retrieval

Suppose we added get(int i), which returns the ith item from the list.

Why would get be slow for long lists compared to getLast()? For what inputs?

  • Have to scan to desired position. Slow for any i not near the sentinel node.

  • How do we fix this?

    • For now: We’ll take a different tack: Using an array instead (no links!).

Naive Array Lists

Random Access in Arrays

Retrieval from any position of an array is very fast.

  • Independent of array size.

  • Ultra fast random access results from the fact that memory boxes are the same size (in bits), we won’t talk about this in detail in this course about Algorithm and Data Structure.

Our Goal: AList.java

Want to figure out how to build an array version of a list:

  • In lecture we’ll only do back operations. Front operations are left for your practice.

Naive AList Code

/** Array based list. */

public class Alist {
    private int[] items;
    private int size;

    /** Create an empty list. */
    public AList() {
        items = new int[100];
        size = 0;
    }

    /** Insert X into the back of the list. */
    public void addLast(int x) {
        items[size] = x;
        size += 1;
    }

    /** Returns the item from the back of the lists. */
    public int getLast() {
        return items[size - 1];
    }

    /** Gets the ith item in the list (0 is the front). */
    public int get(int i) {
        return items[i];
    }

    /** Returns the size of the list. */
    public int size() {
        return size;
    }

    /** Deletes item from back of the lisk and
     *  returns deleted item. */
    public int removeLast() {
        int x = getLast();
        //items[size - 1] = 0;
        /* Setting deleted item to zero is not necessary to
         * preserve invariants, and thus not necessary for
         * correctness. */
        size -= 1;
        return x;
    }
}

AList Invariants:

  • The position of the next item to be inserted is always size.

  • size is always the number of items in the AList.

  • The last item in the list is always in position size - 1.

Invariants: things that must be true.

Resizing Arrays

Array Resizing

When the array gets too full (size == items.length), e.g. addLast(11), just make a new array:

int[] a = new int[size+1];
System.arraycopy(...)
a[size] = 11;
items = a;  size +=1;
// We call this process "resizing"
Resizing Array Code
public void addLast(int x) {
  if (size == items.length) {
    int[] a = new int[size + 1];
    System.arraycopy(items, 0, a, 0, size);
    items = a;  	
  }
  items[size] = x;
  size += 1;
}
// Works.
private void resize(int capacity) {
  int[] a = new int[capacity];
  System.arraycopy(items, 0, a, 0, size);
  items = a;
}
 
public void addLast(int x) {
  if (size == items.length) {
	resize(size + 1);
  }
  items[size] = x;
  size += 1;
} 
// Much Better !
Resizing Slowness

Inserting 100,000 items requires roughly 5,000,000,000 new containers.

  • Computers operate at the speed of GHz (due billions of things per second).

  • No huge surprise that 100,000 items took seconds.

在这里插入图片描述
在这里插入图片描述

How do we fix the Resizing Performance Bug?

(Probably) Surprising Fact

Geometric resizing is much faster: Just how much better will have to wait.

public void addLast(int x) {
  if (size == items.length) {
	resize(size + RFACTOR);
  }
  items[size] = x;
  size += 1;
}
// Unsuably Bad!
public void addLast(int x) {
  if (size == items.length) {
	resize(size * RFACTOR);
  }
  items[size] = x;
  size += 1;
}
// Great performance!
/* This is how the Python list is implemented.
Memory Efficiency

An AList should not only be efficient in time, but also efficient in space.

  • Define the “usage ratio” R = size / items.length;

  • Typical solution: Half array size when R < 0.25.

  • More details later.

Later we will consider tradeoffs between time and space efficiency for a variety of algorithms and data structures.

Generic ALists

Similar to generic SLists.

public class AList<Glorp> {
    private Glorp[] items;    
    private int size;
 
    public AList() {
        items = (Glorp []) new Object[8];  
        size = 0;
    }
 
    private void resize(int cap) {
        Glorp[] a = (Glorp []) new Object[cap];
        System.arraycopy(items, 0, a, 0, size);
        items = a;
    }

    public Glorp get(int i) {
        return items[i];
    }
...

When creating an array of references to Glorps:

  • (Glorp []) new Object[cap];

  • Causes a compiler warning, which you should ignore.

Why not just new Glorp[cap];

  • Will cause a “generic array creation” error.
Nulling Out Deleted Items

Unlike integer based ALists, we actually want to null out deleted items.

  • Java only destroys unwanted objects when the last reference has been lost.

  • Keeping references to unneeded objects is sometimes called loitering.

  • Save memory. Don’t loiter.

public Glorp deleteBack() {
    Glorp returnItem = getBack();
    items[size - 1] = null;
    size -= 1;  	
    return returnItem;
}
Loitering Example

Changing size to size - 1 yields a correct AList.

  • But memory is wasted storing a reference to the unwanted object.
    在这里插入图片描述

By nulling out items[size], Java is free to destroy the unneeded object from memory, which could be potentially megabytes in size.

Obscurantism in Java

We talk of “layers of abstraction” often in computer science.

  • Related concept: obscurantism. The user of a class does not and should not know how it works.

    • The Java language allows you to enforce this with ideas like private!
  • A good programmer obscures details from themselves, even within a class.

    • Example: addFirst and resize should be written totally independently. You should not be thinking about the details of one method while writing the other. Simply trust that the other works.
    • Breaking programming tasks down into small pieces (especially functions) helps with this greatly!
    • Through judicious use of testing, we can build confidence in these small pieces, as we’ll see later.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值