前言
今天来介绍一下Android 里面常用的集合以及各自的特性
Collection
Collection是接口,List和Set都实现了Collection接口,基本需要操作的方法都定义在这里了
Set
常用TreeSet和HashSet
TreeSet
TreeSet是根据二叉树实现的,也就是TreeMap, 放入数据不能重复且不能为null,可以重写compareTo()方法来确定元素大小,从而进行升序排序
public class DataType {
public static void main(String[] args){
Set<Integer> treeSet = new TreeSet<>(new MyComparator());
treeSet.add(1);
treeSet.add(3);
treeSet.add(2);
for(Integer i : treeSet){
System.out.println(i);
}
}
static class MyComparator implements Comparator<Integer>{
@Override
public int compare(Integer o1, Integer o2) {
if(o1 < o2 ){
return -1;
}
if(o1 == o2 ){
return 0;
}
if(o1 > o2 ){
return 1;
}
return 0;
}
}
}
以上代码执行结果是:
1
2
3
通过传入MyComparator对象自定义的排序方法,来实现从大到小的排序。
HashSet
HashSet是根据hashCode来决定存储位置的,是通过HashMap实现的,所以对象必须实现hashCode()方法,存储的数据无序不能重复,可以存储null,但是只能存一个。
public class DataType {
public static void main(String[] args){
Set<String> set = new HashSet<>();
set.add("1");
set.add("2");
set.add(null);
set.add("1");
for(String s : set){
System.out.println(s);
}
}
}
以上代码运行的结果是:
null
1
2
List
List比较常用的有ArrayList和LinkedList,还有一个比较类似的Vector
ArrayList
是使用动态数组来实现的,对于数据的随机get和set或是少量数据的插入或删除,效率会比较高。ArrayList是线程不安全的,在不考虑线程安全的情况下速度也比较快的。ArrayList插入数据可以重复,也是有序的,按照插入的顺序来排序。
public class ListTest {
public static void main(String[] args){
List<String> arrayList = new ArrayList<>();
arrayList.add("张三");
arrayList.add("李四");
arrayList.add("王五");
arrayList.remove("张三");
for(String s : arrayList){
System.out.println(s);
}
}
}
内部使用动态数组实现
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
所以根据序号读取数据只需直接获取数组对应脚表的数据就可以了
LinkedList
内部是使用链表的形式来实现的,在插入大量数据的时候效率比较快。
链表实现的代码
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
往容器最后面添加元素
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first;
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last;
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
往容器最前面添加元素的代码
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
根据序号获取数据:
public E get(int index) {
//判断index序号是否是合法的
checkElementIndex(index);
return node(index).item;
}
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {//判断序号在总长度一半之前还是之后
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
LinkedList根据序号获取数据,是二分进行遍历,如果序号小于总长度的一半,就从链表头部开始往后遍历,直到找到对应的序号。如果序号大于总长度的一半,就从链表尾部往前进行遍历,直到找到对应的序号。拿到数据。
Vector
Vector的使用方法和内部实现基本和ArrayList相同,只不过它在add(), remove(), get()等方法中都加了同步。所以它是线程安全的。但是使用效率上就不如ArrayList了。
Map
常用HashMap和TreeMap
HashMap
HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体
// 存储时:
int hash = key.hashCode(); // 这个hashCode方法这里不详述,只要理解每个key的hash是一个固定的int值
int index = hash % Entry[].length;
Entry[index] = value;
// 取值时:
int hash = key.hashCode();
int index = hash % Entry[].length;
return Entry[index];
TreeMap
TreeMap的使用大致跟HashMap类似,但是内部实现是根据红黑树来实现的。红黑树是一种平衡有序的二叉树,TreeMap的插入删除查询都是依据红黑树的规则来进行的
HashTable
HashMap和TreeMap都是线程不安全的,多线程操作的时候可能会造成数据错误。Hashtable是线程安全的。HashMap单线程效率 非线程安全 数组链表,Hashtable 线程安全 效率低 双数组
总结
本章节主要介绍了在Android开发里经常用的一些集合类的底层实现,深刻理解数据结构可以帮助我们把现实问题转化为计算机语言的表示。它就好比一个武林高手的内功修为。也许你现在还只是停留在语言阶段,还在拼命的学习java语法,但是当你学完语法以后,想要提高或者以后要成为一个牛人的时候,你就会想起数据结构了。因为这时你已经不再满足于能写出程序了,你会关注程序的效率了。到了一定的境界,你自己都会意识到它的重要性