集合总结
找到安装java JDK时的路径,在该路径,找到src.zip就行
1.集合分类
都在java.util的包下面
1.1 Collection&&Collections
Collections是一个集合类,该类专门由操作或返回集合的静态方法组成public class Collections {
}
Collection是一个接口,接口定义如下
public interface Collection<E> extends Iterable<E> {
int size();
boolean isEmpty();
boolean contains(Object o);
Iterator<E> iterator();
Object[] toArray();
boolean add(E e);
boolean remove(Object o);
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
......
void clear();
boolean equals(Object o);
int hashCode();
}
// List继承Collection,又重写了Collection接口中的那些方法
public interface List<E> extends Collection<E> {
}
// Set继承Collection
public interface Set<E> extends Collection<E> {
}
如图可以看出Collection是List与Set的根接口,而Map与Collection接口没关系,但是属于集合类的一部分2.集合的基本操作
2.1 List
2.1.1 ArrayList
private static final int DEFAULT_CAPACITY = 10; // 数组默认大小为10
private int size;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;
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 ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
public void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
/**
* 在ArrayList<E> extends AbstractList<E>其抽象类中定义的protected transient int modCount = 0;
*/
modCount++;
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); // 右移1位,除以2,容量增为原来的1.5倍
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0)
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
public void clear() {
modCount++;
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
说明:
1)ArrayList底层是使用数组存储的,当数组大小不足存放新增元素的时候,才会发生扩容。在add操作中,ArrayList首先会调用ensureCapacityInternal方法进行扩容检测的。如果数组大小不足,则会自动扩容;如果扩容后的大小超出数组最大的大小,则会抛出异常。
2)fail-fast机制的实现
fail-fast机制也叫作”快速失败”机制,是Java集合中的一种错误检测机制。在对集合进行迭代过程中,除了迭代器可以对集合进行数据结构上进行修改,其他的对集合的数据结构进行修改,都会抛出ConcurrentModificationException错误。这里,所谓的进行数据结构上进行修改,是指对存储的对象,进行add,set,remove操作,进而对数据发生改变。
ArrayList中,有个modCount的变量,每次进行add,set,remove等操作,都会执行modCount++。在获取ArrayList的迭代器时,会将ArrayList中的modCount保存在迭代中,每次执行add,set,remove等操作,都会执行一次检查,调用checkForComodification方法,对modCount进行比较。如果迭代器中的modCount和List中的modCount不同,则抛出ConcurrentModificationException。
3)transient关键字
当对象被序列化时(写入字节序列到目标文件)时,transient阻止实例中那些用此关键字声明的变量持久化;当对象被反序列化时(从源文件读取字节序列进行重构),这样的实例变量值不会被持久化和恢复。例如,当反序列化对象——数据流(例如,文件)可能不存在时,原因是你的对象中存在类型为java.io.InputStream的变量,序列化时这些变量引用的输入流无法被打开。其实和hibernate中的注解@Transient一样
2.2 Set
Set里存放的对象是无序,不能重复的,集合中的对象不按特定的方式排序,只是简单地把对象加入集合中。
看源码HashSet底层就是HashMap
而TreeSet底层就是TreeMap
2.2.1 HashSet
Set<Integer> set = new HashSet<Integer>();
set.add(1);set.add(2);set.add(3);
System.out.println(set.size()); //输出2
set.clear();
set.add(1);set.add(2);set.add(2);
System.out.println(set.size()); //输出3,说明set中不会出现重复元素
// 遍历set
set.clear();
set.add(1);set.add(3);set.add(2);
for(Integer i: set) {
System.out.println(i); //输出1 2 3 说明set是有序的(数字按123,字母按abc来)吗?XX大错特错XX
}
// 但是这个为什么无序呢
HashSet<Integer> set = new HashSet<Integer>();
set.add(2007111315);
set.add(2007111314);
set.add(2007111318);
set.add(2007111313);
System.out.println(set); // 输出[2007111314, 2007111315, 2007111313, 2007111318]无序
TreeSet ts = new TreeSet(set);
ts.comparator();
System.out.println(set); // 输出有序(把HashSet作为构造参数放到TreeSet中就可以排序)
上面是因为HashSet是根据哈希算法决定其在堆内存的位置,然后Integer(或String)其哈希值计算出的位置都是统一的,恰好是满足这种123,abc的顺序,所以输出set元素表面上看起来是有序。反例可以看下下面这个public class SetOrderTest {
public static void main(String[] args) {
Set<User> set = new HashSet<User>();
set.add(new User("c"));
set.add(new User("b"));
set.add(new User("a"));
for(User i: set) {
System.out.println(i.getName()); // 输出可能cba abc等各种顺序,证明了无序
}
}
}
class User{
String name;
public User(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2.2.2 TreeSet
TreeSet是用来排序的, 可以指定一个顺序, 对象存入之后会按照指定的顺序排列。如果是TreeSet<Person>在实体里面指定比较的规则,需要在自定义类(Person)中实现Comparable接口,并重写接口中的compareTo方法,这样输出就不会报错ClassCastException
Set<String> set = new TreeSet<String>();
set.add("abc");
set.add("xyz");
set.add("rst");
System.out.println(set);//输出 [abc, rst, xyz]
3.2 Map
Map集合使用键(key)值(value)来保存数据,其中值(value)可以重复,但键(key)必须是唯一,也可以为空,但最多只能有一个key为空,它的主要实现类有HashMap、TreeMap、Hashtable。
3.2.1 HashMap
3.2.2 TreeMap
3.2.3 Hashtable