把玩算法 | 数组

数组是由相同类型的元素的集合所组成的数据结构,分配一块连续的内存来存储。数组是使用索引来访问里面的元素的。如果我们有n个值,那么数组索引的范围为0至n-1。对于0到n-1之间的任意的i,我们就能在Java代码中用arr[i]来访问第i个元素的值。

把玩算法 | 数组

下面的代码创建了一个人名的数组,然后打印数组中的第二个元素:

String[] names = {"张三", "李四", "王五"}; System.out.println(names[1]);

但是数组创建完成后,其大小也就不能改变了。如果我们想要动态的改变数组的大小,那么就需要先创建足够大的同类型的数组,然后再将原始的数组中的数据拷贝到新创建的数组中。例如原先创建了一个长度为3的数组存放了3个人的名字,后来需要再存放"赵六"和"钱七"这两个人名,那么就需要重新创建长度为5的数组来存放这5个人名。相关的代码如下(注:数组的拷贝可以使用System.arraycopy方法来进行拷贝):

String[] names = {"张三", "李四", "王五"}; // 原先的数组 // 现在需要存放"赵六"和"钱七"这两个人名 String[] newNames = new String[5]; // 拷贝之前的数据 for (int i = 0; i < names.length; i++) { newNames[i] = names[i]; } // 存放"赵六"和"钱七" newNames[3] = "赵六"; newNames[4] = "钱七";

每次都要自己手动的来对数组进行扩容确实挺麻烦的,我们可以将添加、删除、访问数组中的元素的方法进行封装,在需要用到时直接用就行了。

API

下面是我们为数组定义的相关API:

数组

pubic class Array<Element> implements Iterable<Element> Array() 创建一个空数组 Array(int capacity) 创建一个初始化容量的数组 void add(Element e) 添加一个元素 Element get(int index) 获取指定位置的元素 void set(int index, Element e) 设置指定位置的元素 Element remove(int index) 移除指定位置的元素 int size() 获取数组的大小

泛型:一种特别的Java的机制,也叫参数化类型。在API中,类名后面的<Element>将Element定义为一个类型参数,它是一个占位符,表示的是该类将会使用到的某个具体的数据类型。Array<Element>可以理解为某种元素的数组。我们这里的数组是能存放任意类型的数据的,例如,可以编写下面的代码来存放String类型的对象:

Array<String> arr = new Array<String>(); arr.add("张三"); ...

可迭代的集合:在很多情况下,只需要用某种方式来处理集合中的每个元素,这种模式一般叫做迭代器模式。有了它,我们就能写出很清晰的代码而不必依赖于具体的集合类型的实现。只要实现了Iterable,就能使用for-each来遍历集合中的每一个元素,例如下面的代码打印出所有的人的姓名:

Array<String> names = new Array<String>(); ... for (String name : names) { System.out.println(name); }

上面的for-each等同于下面的代码(很明显,使用for-each要简介方便很多):

for (Iterator<String> iterator = names.iterator(); iterator.hasNext();) { String name = iterator.next(); System.out.println(name); }

有了上面的API后,就能将上面的代码改为使用Array来编写。在这里只需要关注所需要实现的逻辑,而不必关注具体的内部实现细节:

public static void main(String[] args) { Array<String> names = new Array<>(3); names.add("张三"); names.add("李四"); names.add("王五"); System.out.println(names); // 存放"赵六"和"钱七" names.add("赵六"); names.add("钱七"); System.out.println(names); } 实现

更加详细的实现见github:Array.java

public class Array<Element> implements Iterable<Element> { private Object[] elements; // 元素 private int size; // 元素的个数 public Array() { this(0); } public Array(int capacity) { elements = new Object[capacity]; } public void add(Element e) { if (size == elements.length) { resize(size * 2 + 1); } elements[size++] = e; } public Element get(int index) { return (Element) elements[index]; } public void set(int index, Element e) { elements[index] = e; } public int size() { return size; } // 下面这几个方法的实现,请看接下来的实现说明 private void resize(int capacity) public void remove(int index) public Iterator<Element> iterator() }

默认的构造器创建了一个空白的数组(长度为0),也提供了一个能指定初始化容量的版本,这样就可以根据自己的使用场景提供一个恰当的容量,避免在添加元素的过程中,数组内部频繁的进行扩容,影响性能。内部维护了一个数组Object[]用来存放元素,使用实例变量size来记录数组的大小。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zypysd.html