如果你是一个初学者,相信看到Java Documentation 是懵逼的,说实话这点还是比较想吐槽的,如果接触的教材不好,有一些接口和包可能即使做了课程设计也还是懵的。
想来这也是博客存在的意义,教材本应因人而异,但这又办不到,博客虽不能当教材,但也权当补充,毕竟很多人就是想找一句点通问题的话。所以感谢《Data Structure and Algorithm Analysis in Java》这本书,紧接上次谈的泛型,继续看看常用的类和包及其对应的数据结构,或是相关的用途。
Java作为一门语言,说实话执行并不高效,但好在灵活,有很多现成的东西能用,互联网具有时效性,经不起数年的慢慢开发,灵活是要优先于效率的,这点取舍大家相比清楚。比如如果想写爬虫,优先选择肯定是Python而不是C,爬虫可能有想法了今晚就要用,经不起慢慢来。Java的灵活性靠的便是现有的类、包和一堆框架,可谓不学不行。
Collection 接口的常用程度应该是最高的,作为Java Collection Framework的主要部分,其所包含的Set和List类是最为常用的。
但是在谈Collection之前,还需要了解下Iterable<T>接口,这个接口又称为迭代器,它和Java的foreach遍历有关,实现这个接口的类才能使用foreach实现遍历输出,实现了Iterable<T>的类还需要在内部实现实现自己的迭代器,一般来讲实现Iterator<T>接口的都是集合,除非你自己写个数据结构,否则用到这个接口的概率还真不大。
public class Testclass<E> implements Iterable<E>{
@Override
public Iterator iterator() {
return new TestIterator<E>();
}
private class TestIterator<T> implements Iterator<T>{
@Override
public boolean hasNext() {
return false;
}
@Override
public T next() {
return null;
}
}
}
当然数组默认也支持foreach遍历,跟你往里面塞什么东西没关系,但是也别轻易用,经测试,foreach输出数组比for循环要慢。
public class testProgram {
class Boy{
}
public static void main(String[] args) {
//TODO
Boy[] a=new Boy[10];
for (Boy b : a) {//数组类型默认支持foreach
System.out.println(b);
}
}
}
而还有一点需要说明的是,关于Collection接口的重要方法——hashCode(),它的作用就是用来判断两个集合是否相等
The hash code of a set is defined to be the sum of the hash codes of the elements in the set, where the hash code of a nullelement is defined to be zero. This ensures that s1.equals(s2) implies that s1.hashCode()==s2.hashCode() for any two sets s1 and s2, as required by the general contract of Object.hashCode().
(一)Set<E>——集合
Set<E>,是不包含重复元素的集合,可以包含null值,但是只能有一个null值(视具体情况而定),这点和数学上的定义一致。一个Set禁止包含对自身的引用。集合的一些实现类可能禁止空值,可能限定了能装进去的元素。以下是最常用的
(1)HashSet<E>——哈希表相关
没有排序过就存起来的Set,无序。源码中可以看到,它基于HashMap类实现,也就是说当你创建一个HashSet实例,实际上创建了也个HashMap,不过别担心浪费空间,当你向其中添加值的时候,是添加了HashMap的key,而key所对应的value都是指向同一个Object类。
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable
{
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
...
public HashSet() {
map = new HashMap<>();
}
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
...
}
具体:
基于哈希表的 Map 接口的NavigableMap实现。此实现提供所有可选的映射操作,并允许使用 null值和 null 键。(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
此类为基本操作提供了稳定性能,这些基本操作包括 add、remove、contains 和 size
(2)TreeSet<E>——红黑树相关
排序过的集合,有序。顾名思义,此集合实现方式应该为树,学过数据结构可以知道,使用树可以实现查找的log(n)时间开销,而通过查看源代码可以发现,类似HashSet,TreeSet是利用TreeMap实现的,而TreeMap则使用了红黑树(source)。
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable
{
/**
* The backing map.
*/
private transient NavigableMap<E,Object> m;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}
public TreeSet() {
this(new TreeMap<E,Object>());
}
...
}
具体:
基于 TreeSet 的 NavigableSet实现。
此实现为基本操作(
add
、remove
和contains
)提供受保证的 log(n) 时间开销。
(二)List<E>——数据结构:表(有序集合)
List,又名列表,又称为有序集合,实现了这个接口的类都有一个特点,就是有序,这个有序指的是,你放进去的顺序,但和Set不同的是,列表通常允许重复元素。
(1)ArrayList——表
内部实现是数组,以及对数组的操作,所以经常需要访问它时,它的效率优于LinkedList,与之相对的,它对随机大规模删除、指定位置的插入等并不上心(因为要对数组整体进行移动,而不是单单修改一下引用)。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
...
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData; // non-private to simplify nested class access
private int size;
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
}
(2)LinkedList——双链表
内部实现是许多个Node,然后通过对下一个以及上一个Node的引用来链接起来,形如:Root-->Node2-->Node3,所以可以知道,LinkedList在具体取出一个项目时,要不断地寻址,所以访问速度不如ArrayList,但是优在灵活,add,remove操作较快。
Node<E>内部类,first和last分别指向上一个和下一个Node
插入时检查是否是新表,如果是,last必为null,则头(first)为新元素,size++。
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
transient int size = 0;
/**
* Pointer to first node.
*/
transient Node<E> first;
/**
* Pointer to last node.
*/
transient Node<E> last;
public LinkedList() {
}
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++;
}
public boolean add(E e) {
linkLast(e);
return true;
}
}