Java入门系列之集合入门(六)

前言

接下来我们进入集合学习,关于集合学习将分为基础学习、数据结构讲解、源码分析三部分来进行讲解。

集合入门

我们以一个例子来进行集合入门讲解,上学时我们上体育课,首先体育老师会叫我们多少多少同学站成一对来进行报名,然后自由解散休息,哈哈。体育老师要求我们站成一对,这个时候就好对数组进行数据存储,我们假设体育老师需要5个人站成一对,所以这也就对应数组初始化的容量,有了容量之后,接下来则是在所要求的地方站到指定位置,这就好比往数组里添加元素,好了接下来我们以Java语言来实现这一要求。首先我们定义一个排队类,依据面向对象封装思想,我们对外提供操作方法,所以在此类中定义私有的数组,然后定义集合中元素个数属性,如下:

public class QueueDemo {

    private int Size;

    private Integer[] Elements;

}

依据我们所分析,我们按照5人站成一对,所以当我们初始化排队类时就初始化数组容量,也就是说我们在构造函数中初始化容量,如下:

public class QueueDemo {

    // 数组大小
    private int Size;

    // 数组
    private Integer[] Elements;
    
    // 初始化数组容量
    public QueueDemo(int capacity) {
        Elements = new Integer[capacity];
    }

}

我们初始化容量后呢,接下来则是对应同学开始排队,此时也就是对应往数组里添加元素,所以我们封装一个添加方法,每添加一个元素则数组大小则递增1,如下:

    // 添加元素
    public void Add(Integer element) {
        Elements[Size] = element;
        Size++;
    }

对应每一步操作,我们都遍历打印出数组中元素,所以接下来我们重写toString方法,如下:

  // 重写toString打印元素
    @Override
    public String toString() {
        int length = Elements.length;
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        for (int i = 0; i < length; i++) {
            sb.append(Elements[i]);
            if (i != length - 1) {
                sb.append(",");
            }
        }
        return sb.toString();
    }

我们完成了同学排队第一步,接下来我们实例化上述排队类并添加元素(我们将元素看做时排队时同学们的姓名),最后打印元素,如下:

public class Main {

    public static void main(String[] args) {
        QueueDemo demo = new QueueDemo(5);
        demo.Add(1);
        demo.Add(2);
        demo.Add(3);
        demo.Add(4);
        demo.Add(5);
        System.out.println(demo);
    }
}

当同学们排成一队后,体育老师发现排队的同学高矮不一,然后将同学与同学之间按照高矮进行调换,这也就对应着我们需要封装插入元素的方法,因为我们初始化数组容量为5,当我们在指定索引插入一个元素时,再打印元素必然抛出数组异常,也就是涉及到数组容量扩容,我们暂且定义在指定索引插入元素的方法,如下:

public void Insert(int index, Integer element) {
        
}

当按照高矮排好队后,体育老师认为一列只需排4个人,剩余一个人到其他对去,这也就对应删除数组中的元素,同样我们定义删除方法,当我们删除数组中元素时,需要将删除的元素后的元素都往前移一位,同时将最后一位置为空,数组大小也减少一位,如下:

// 删除元素
public void Remove(Integer element) {
        int index = GetIndex(element);
        for (int i = index; i < Elements.length - 1; i++) {
            Elements[i] = Elements[i + 1];
        }
        Elements[Size - 1] = null;
        Size--;
}

接下来我们再来删除并打印元素,如下:

        //删除元素
        demo.Remove(3);
        System.out.println(demo);

 因为我们将数组最后一位元素置为空,所以在打印时应删除,我们继续改造重写的toString方法,如下:

    @Override
    public String toString() {
        int length = Elements.length;
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        for (int i = 0; i < length; i++) {
            if (Elements[i] == null) {
                continue;
            }
            sb.append(Elements[i]);
            if (i != length - 1) {
                sb.append(",");
            }
        }
        if (sb.charAt(sb.length() - 1) == ',') {
            sb.delete(sb.length() - 1, sb.length());
        }
        sb.append("]");
        return sb.toString();
    }

 接下来体育老师要求报数,比如根据某个同学的姓名即元素报出自己所在的第几位(也就对应数组中的索引),所以此时我们再封装一个获取指定元素的索引方法,如下:

// 获取指定元素索引
public int GetIndex(Integer element) {
        for (int i = 0; i < Elements.length - 1; i++) {
            if (!Elements[i].equals(element)) {
                continue;
            }
            return i;
        }
        throw new RuntimeException("未找到");
}

然后我们尝试获取4号同学所排队的位置是,如下:

System.out.println("4号同学所在的位置是 :" + demo.GetIndex(4));

到了这里我们完成了排队的基本要求,但是还远远不够,比如我们是自定义初始化容量,这里我们指定为5,经过删除操作后,最终数组中存在4个元素,要是我们再往数组中添加至少2个以上元素,此时打印数组元素将抛出异常,所以这里为了解决这个问题我们对数组进行自动扩容,也就是对添加方法进行改造,当添加元素时我们需要判断是否已经超过数组容量,若超过,我们将数组容量扩大到现有数组容量的2倍,那么我们应该怎么判断呢?我们通过数组大小和数组容量进行判断,如下: 

    public void Add(Integer element) {
        if (Size >= Elements.length) {
            Elements = Arrays.copyOf(Elements, 2 * Elements.length);
        }
        Elements[Size] = element;
        Size++;
    }
public static void main(String[] args) {
        QueueDemo demo = new QueueDemo(5);
        demo.Add(1);
        demo.Add(2);
        demo.Add(3);
        demo.Add(4);
        demo.Add(5);
        System.out.println(demo);
        //删除元素
        demo.Remove(3);
        System.out.println(demo);
        System.out.println("4号同学所在的位置是 :" + demo.GetIndex(4));

        demo.Add(6);
        demo.Add(7);
        System.out.println(demo);
}

在排队时我们给定人数为5,也就说数组初始化容量为5,这还不够灵活,如果体育老师已经明确规定一列必须站几个,我们直接就能接受到体育老师规定的信号,这就像明确指定了数组的初始化容量,这样一来既能保证不会抛出异常,同时也不会影响当添加和插入元素时扩容时所带来的性能开销,如果未明确规定一列站几个,我们也可以默认初始化容量,如此最灵活,一切都未变且性能最佳,如下我们定义一个默认初始化容量并改造排队列构造函数,如下:

    private int DEFAULT_CAPACITY = 10;

    public QueueDemo() {
        Elements = new Integer[DEFAULT_CAPACITY];
    }

    public QueueDemo(int capacity) {
        Elements = new Integer[capacity <= 0 ? DEFAULT_CAPACITY : capacity];
    }

到了这里我们还未实现在指定索引位置插入元素的Insert方法,既然要插入指定索引位置,首先我们必须检查指定索引位置是否超过数组大小,然后将指定索引后的元素向后移动一位,最后留出指定索引位置进行插入,如下:

public void Insert(int index, Integer element) {
        if (Size <= index || index < 0) {
            throw new RuntimeException("超出数组边界");
        }
        System.arraycopy(Elements, index, Elements, index + 1,
                Size - index);
        System.out.println(this);
        Elements[index] = element;
        Size++;
 }
        QueueDemo demo = new QueueDemo();
        demo.Add(1);
        demo.Add(2);
        demo.Add(3);
        demo.Add(4);
        demo.Add(5);
        System.out.println(demo);
        //删除元素
        demo.Remove(3);
        System.out.println(demo);
        System.out.println("4号同学所在的位置是 :" + demo.GetIndex(4));

        demo.Add(6);
        demo.Add(7);
        System.out.println(demo);

        //插入元素
        demo.Insert(2, 20);
        System.out.println(demo);

如上我们首先检查指定索引是否小于0或者是否超出数组大小,否则抛出异常,然后这里我们通过内置提供的方法,从指定索引位置后的元素进行复制即Index+1,最后复制元素的长度为Size-Index。此时指定索引位置数据仍为4,最后我们将指定索引位置的值通过我们要插入的值进行替换。

总结

如上则是我们实现比较完整的排队需求,当然还有一些参数检查的小问题,看到这里想必很多童鞋就已经清楚知道了,其实我们实现的就是Java中的集合,有了本节课的基础,下节课我们进行ArrayList源码分析将得心应手,感谢阅读,我们下节见。

猜你喜欢

转载自www.cnblogs.com/CreateMyself/p/11440876.html
今日推荐