C++模拟实现JDK中的ArrayList和LinkedList
Java实现ArrayList和LinkedList的方式采用的是数组和链表。以下是用C++代码的模拟:
声明Collection接口:
#ifndef COLLECTION_H_ #define COLLECTION_H_ template<class T> class Collection { public: virtual ~Collection() { } virtual bool add(T* e) = 0; virtual int contains(T* e) = 0; virtual bool isEmpty() = 0; virtual bool remove(T* e) = 0; virtual int size() = 0; }; #endif /* COLLECTION_H_ */
声明List接口
#ifndef LIST_H_ #define LIST_H_ #include "Collection.h" template<class T> class List: public Collection<T> { public: virtual ~List() { } virtual void add(int index, T* e) = 0; virtual T* get(int index) = 0; virtual T* remove(int index) = 0; }; #endif /* LIST_H_ */
接口声明完成以后就是具体实现。通过C++的模板来模拟Java中的泛型机制,通过嵌套Iterator类实现各自的迭代器。由于在Java中Iterator最常用的方法是hasNext()和next()。因此本文只做了这两个方法的实现。
ArrayList.h
#ifndef ARRAYLIST_H_ #define ARRAYLIST_H_ #include "List.h" template<class T, int bsz = 10> // bsz代表每次扩展数组的步长 class ArrayList: public List<T> { // 继承List和Collection int len; // 已使用量 int max; // 最大长度 T** items; void inflate(); // 扩充数组 ArrayList(const ArrayList&); // 处于使用习惯隐藏拷贝函数,用户只能通过引用或指针 public: ArrayList() : len(0), max(bsz), items(new T*[bsz]) { } virtual ~ArrayList(); bool add(T* e); // 增加元素 int contains(T* e); // 判断是否包含 bool isEmpty(); // 判断容器是否为空 bool remove(T* e); // 移除元素 int size(); // 获取容器容量 void add(int index, T* e); // 增加元素 T* get(int index); // 获取元素 T* remove(int index); // 删除元素 class Iterator; friend class Iterator; class Iterator { // 创建迭代器 ArrayList& al; int index; public: Iterator(ArrayList& list) : al(list), index(0) { } bool hasNext() { if (index < al.len) { return true; } return false; } T* next() { if (hasNext()) { return al.items[index++]; } return 0; } }; }; /** * 以下是实现,如果处于效率考虑。有些函数可以增加inline。 */ template<class T, int bsz> ArrayList<T, bsz>::~ArrayList() { for (int i = 0; i < len; i++) { delete items[i]; items[i] = 0; // 清理对象 } delete items; // 清空数组 } template<class T, int bsz> void ArrayList<T, bsz>::inflate() { if (len == max) { max += bsz; // 数组增加并且赋值保存的指针 T** newItems = new T*[max]; for (int i = 0; i < len; i++) newItems[i] = items[i]; delete[] items; // 清空原始数组:这里很重要!如果不清理会多出很多匿名数组占用内存。 items = newItems; } } template<class T, int bsz> bool ArrayList<T, bsz>::add(T* e) { inflate(); items[len++] = e; return true; } template<class T, int bsz> int ArrayList<T, bsz>::contains(T* e) { // 判断是否包含:判断是否相同的标准是两个对象指针是否指向同一块内存。 for (int i = 0; i < len; i++) { if (get(i) == e) { return i; // 返回下标 } } return -1; } template<class T, int bsz> int ArrayList<T, bsz>::size() { // 获取容器容量 return len; } template<class T, int bsz> bool ArrayList<T, bsz>::remove(T* e) { // 调用内部的两个函数组合作为实现 int index = contains(e); if (index != -1) { remove(index); return true; } return false; } template<class T, int bsz> bool ArrayList<T, bsz>::isEmpty() { // 判断容器是否为空 if (len == 0) { return true; } else { return false; } } template<class T, int bsz> void ArrayList<T, bsz>::add(int index, T* e) { // 增加元素:插入 inflate(); T* newItems[++len]; index--; // 下标 for (int i = 0; i < len; i++) { if (i < index) { newItems[i] = items[i]; } if (i == index) { newItems[i] = e; } if (i > index) { newItems[i] = items[i - 1]; } } items = newItems; } template<class T, int bsz> T* ArrayList<T, bsz>::get(int index) { // 获取元素 if (index < 0 || index >= len) { return 0; } return items[index]; } template<class T, int bsz> T* ArrayList<T, bsz>::remove(int index) { // 删除元素 if (index < 0 || index >= len) { return 0; } T* result = get(index); T* newItems[len - 1]; for (int i = 0; i < len; i++) { if (i < index) { newItems[i] = items[i]; } if (i > index) { newItems[i - 1] = items[i]; } }
delete items; items = newItems; len--; return result; } #endif /* ARRAYLIST_H_ */
很明显,和Java中的ArrayList实现一样。进行插入的代价是很高的。
LinkedList.h
#ifndef LINKEDLIST_H_ #define LINKEDLIST_H_ #include "List.h" #include <iostream> using namespace std; template<class T> class LinkedList: public List<T> { struct Item { // 链表结构 Item(T* d, Item* nxt = 0, Item* pre = 0) : // d:保存数据,nxt:指向前一个链表结构,pre:指向下一个链表结构。 data(d), next(nxt), prev(pre) { } T* data; Item* next; Item* prev; }; int len; // 长度 Item* head; // 链表的头指针 Item* tail; // 链表的尾指针 LinkedList(const LinkedList&); // 隐藏拷贝函数 public: LinkedList() : len(0), head(0), tail(0) { } virtual ~LinkedList() { if (len != 0) { while (head->next != 0) { Item* item = head; head = item->next; delete item; item = 0; } } } bool add(T* e); // 增加元素 int contains(T* e); // 判断是否包含 bool isEmpty(); // 判断容器是否为空 bool remove(T* e); // 删除元素 int size(); // 获取容器容量 void add(int index, T* e); // 增加元素 T* get(int index); // 获取元素 T* remove(int index); // 删除元素 class Iterator; friend class Iterator; class Iterator { LinkedList& ll; int index; public: Iterator(LinkedList& list) : ll(list), index(0) { } bool hasNext() { if (index < ll.len) { return true; } return false; } T* next() { if (hasNext()) { return ll.get(index++); } return 0; } }; }; /** * 以下为实现 */ template<class T> bool LinkedList<T>::add(T* e) { // 入栈操作 add(len, e); return true; } template<class T> int LinkedList<T>::contains(T* e) { Item* temp = head; for (int i = 0; i < len; i++) { if (temp->data == e) { return i; } temp = temp->next; } return -1; } template<class T> bool LinkedList<T>::isEmpty() { if (len == 0) { return true; } else { return false; } } template<class T> bool LinkedList<T>::remove(T* e) { int index = contains(e); if (index != -1) { remove(index); return true; } return false; } template<class T> int LinkedList<T>::size() { return len; } template<class T> void LinkedList<T>::add(int index, T* e) { // 插入 if (index > 0 || index <= len) { if (len == 0) { // 第一个对象加入的时候,head引用和tail引用指向同一个 Item* first = new Item(e); head = first; tail = first; } else if (index == 0) { // 如何插入第一个位置,则更新head引用 Item* temp = new Item(e, head, 0); head->prev = temp; head = temp; } else if (index == len) { // 如果插入最后一个位置,则更新tail引用 Item* temp = new Item(e, 0, tail); tail->next = temp; tail = temp; } else { // 更新中间元素 Item* itemPrev = head; for (int i = 1; i < index; i++) { itemPrev = itemPrev->next; } Item* itemNext = itemPrev->next; Item* newItem = new Item(e, itemNext, itemPrev); itemPrev->next = newItem; itemNext->prev = newItem; } len++; } } template<class T> T* LinkedList<T>::get(int index) { // if (index >= 0 || index < len) { Item* result = head; for (int i = 0; i < index; i++) { result = result->next; } return result->data; } return 0; } template<class T> T* LinkedList<T>::remove(int index) { if (index > 0 || index <= len) { if (index == 0) { // 删除head引用 Item* temp = head; head = temp->next; head->prev = 0; T* result = temp->data; // 建立指向返回值的指针。 delete item; item = 0; return result; } else if (index == len) { // 删除tail引用 Item* temp = tail; tail = temp->prev; tail->next = 0; T* result = temp->data; delete item; item = 0; return result; } else { Item* item = head; for (int i = 0; i < index; i++) { // 通过迭代获取目标指针 item = item->next; } Item* itemPrev = item->prev; Item* itemNext = item->next; itemPrev->next = itemNext; itemNext->prev = itemPrev; T* result = item->data; delete item; item = 0; return result; } len--; } return 0; } #endif /* LINKEDLIST_H_ */
LinkedList中所有的查询都必须依赖迭代完成,代价较高。但是插入的代价却比较低。这一点与JDK中的表现一致。
总结:
(1)C++的内存模型比Java复杂,因此可选择的余地也更多。本例程采用完全使用指针实现,基本等同于Java的实现方式。当然你也可以使用位拷贝的方式或&引用来做。有兴趣的可以自己摸索。
(2)C++中的垃圾清理需要手动完成,没有Java来的方便。虽然也提供了析构函数可以自行调用,但是通过观察例程很明显并非所有的工作都可以交给析构函数。更典型的做法是在函数内部随时清理。
如何使用:一个简单测试例程
#include "ArrayList.h" #include "LinkedList.h" #include <iostream> #include <fstream> using namespace std; int main() { // 测试ArrayList<int> ArrayList<int> ia; for (int i = 0; i < 15; i++) { int* v = new int(i); ia.add(v); } cout << ia.size() << endl; for (int i = 0; i < ia.size(); i++) cout << *ia.get(i) << endl; // 测试ArrayList<string> ArrayList<string> sa; fstream in; in.open("Main.cpp"); string line; while (getline(in, line)) { sa.add(new string(line)); } for (int i = 0; i < sa.size(); i++) { cout << *sa.get(i) << endl; } in.close(); // 测试LinkedList<int> LinkedList<int> il; for (int i = 0; i < 33; i++) { il.add(new int(i)); } for (int i = 0; i < il.size(); i++) { cout << *il.get(i) << endl; } // 测试LinkedList<string> LinkedList<string> sl; in.open("Main.cpp"); while (getline(in, line)) { sl.add(new string(line)); } for (int i = 0; i < sl.size(); i++) { cout << *sl.get(i) << endl; } in.close(); // 测试ArrayList<int>的迭代 ArrayList<int>::Iterator iat(ia); while (iat.hasNext()) { cout << *iat.next() << endl; } // 测试ArrayList<string>的迭代 ArrayList<string>::Iterator ist(sa); while (ist.hasNext()) { cout << *ist.next() << endl; } // 测试LinkedList<int>的迭代 LinkedList<int>::Iterator ilt(il); while (ilt.hasNext()) { cout << *ilt.next() << endl; } // 测试LinkedList<string>的迭代 LinkedList<string>::Iterator slt(sl); while (slt.hasNext()) { cout << *slt.next() << endl; } }