如果你是一个初学者,相信看到Java Documentation 是懵逼的,说实话这点还是比较想吐槽的,如果接触的教材不好,有一些接口和包可能即使做了课程设计也还是懵的。
想来这也是博客存在的意义,教材本应因人而异,但这又办不到,博客虽不能当教材,但也权当补充,毕竟很多人就是想找一句点通问题的话。所以感谢《Data Structure and Algorithm Analysis in Java》这本书,紧接上次谈的泛型,继续看看常用的类和包及其对应的数据结构,或是相关的用途。
Java作为一门语言,说实话执行并不高效,但好在灵活,有很多现成的东西能用,互联网具有时效性,经不起数年的慢慢开发,灵活是要优先于效率的,这点取舍大家相比清楚。比如如果想写爬虫,优先选择肯定是Python而不是C,爬虫可能有想法了今晚就要用,经不起慢慢来。Java的灵活性靠的便是现有的类、包和一堆框架,可谓不学不行。
Collection 接口的常用程度应该是最高的,作为Java Collection Framework的主要部分,其所包含的Set和List类是最为常用的。
但是在谈Collection之前,还需要了解下Iterable
public class Testclass implements Iterable{
@Override
public Iterator iterator() {
return new TestIterator();
}
private class TestIterator implements Iterator{
@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
(1)HashSet
没有排序过就存起来的Set,无序。源码中可以看到,它基于HashMap类实现,也就是说当你创建一个HashSet实例,实际上创建了也个HashMap,不过别担心浪费空间,当你向其中添加值的时候,是添加了HashMap的key,而key所对应的value都是指向同一个Object类。
public class TreeSet extends AbstractSet
implements NavigableSet, Cloneable, java.io.Serializable
{
private transient HashMap 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
排序过的集合,有序。顾名思义,此集合实现方式应该为树,学过数据结构可以知道,使用树可以实现查找的log(n)时间开销,而通过查看源代码可以发现,类似HashSet,TreeSet是利用TreeMap实现的,而TreeMap则使用了红黑树(source)。
public class TreeSet extends AbstractSet
implements NavigableSet, Cloneable, java.io.Serializable
{
/**
* The backing map.
*/
private transient NavigableMap m;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
TreeSet(NavigableMap m) {
this.m = m;
}
public TreeSet() {
this(new TreeMap());
}
...
}
具体:
基于 TreeSet 的 NavigableSet实现。
此实现为基本操作(
add
、remove
和contains
)提供受保证的 log(n) 时间开销。
List,又名列表,又称为有序集合,实现了这个接口的类都有一个特点,就是有序,这个有序指的是,你放进去的顺序,但和Set不同的是,列表通常允许重复元素。
(1)ArrayList——表
内部实现是数组,以及对数组的操作,所以经常需要访问它时,它的效率优于LinkedList,与之相对的,它对随机大规模删除、指定位置的插入等并不上心(因为要对数组整体进行移动,而不是单单修改一下引用)。
public class ArrayList extends AbstractList
implements List, 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
插入时检查是否是新表,如果是,last必为null,则头(first)为新元素,size++。
public class LinkedList
extends AbstractSequentialList
implements List, Deque, Cloneable, java.io.Serializable
{
transient int size = 0;
/**
* Pointer to first node.
*/
transient Node first;
/**
* Pointer to last node.
*/
transient Node last;
public LinkedList() {
}
void linkLast(E e) {
final Node l = last;
final Node 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;
}
}