是集合的上层接口。本身是一个Interface,里面包含了一些集合的基本操作。
Collection接口是Set接口和List接口的父接口
Collections是一个集合框架的帮助类。里面包含了一些对集合的排序。
搜索以及序列化的操作,包含有各种有关集合的操作的静态多态方法,此类不能实例化(工具类)
Collections是一个包装类,Collection表示一组的对象,这些对象也称为collection元素。
一些collection允许有重复的元素(List)。而另一些则不允许。
Collections中常用的方法
//将集合中的元素反转
Collections.reverse(list);
//addAll方法可以往集合中添加元素,也可往集合中添加一个集合
Collections.addAll(list,9,20,56);``
//打乱集合中的元素
Collections.shuffle(list);
//按照字符串首字符的升序排列
Collections.sort(list2);
调用代码
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class TestCollections {
public static void main(String args[]) {
//注意List是实现Collection接口的
List list = new ArrayList();
double array[] = { 112, 111, 23, 456, 231 };
for (int i = 0; i < array.length; i++) {
list.add(new Double(array[i]));
}
Collections.sort(list);
for (int i = 0; i < array.length; i++) {
System.out.println(list.get(i));
}
// 结果:23.0 111.0 112.0 231.0 456.0
}
}
通过数组实现。允许对数组的随机访问。
当超过初始容量的时候,容量扩增为原来的1.5倍
数组的缺点是:每个元素不能有间隔。当数组大小不满足时,就要将数据复制到新的存储空间中(不适合插入和删除)
通过数组实现的。支持线程同步,即某一时刻只有一个线程能够写Vector,避免读写不一致
当超过初始容量的时候,容量扩增为原来的2倍,并且可以设置增长空间大小
实现同步花费很高,访问Vector比ArrayList慢很多。
ArrayList和Vector
他们的底层都是一样的,都是使用数组方式存储,数组的容量大于实际的存储状态。以便以后的删除和增加元素。
实现了Serializable接口允许按序号索引元素,但是涉及插入,删除此类的操作比较慢
Vector:此方法由于添加了syncchronized修饰。因此Vector是线程安全的容器。
但是性能上较ArrayList差。因此已经是Java遗留的容器了
LinkedList
使用双向列表实现存储(将内存中零散的内存单元通过附加的引用关联起来,形成一个可以按序号索引的线性结构,
这种链式存储方法与数组的连续存储方式相比,内存的利用率更高),按序号索引数据需要进行向前或向后遍历,
但是插入数据时只需要记录本项的前后项即可,所以插入速度较快
附:
Vector属于遗留容器,(Hashtable,Dictionary,BitSet,Stack,Properties都是遗留容器)
由于ArrayList和LinkedListed都是非线程安全的,如果遇到多个线程操作。
可以通过Collections中的synchronizedList方法将其转换成线程安全的容器后再使用
再用迭代器遍历一个集合对象,如果遍历过程中对象的内容进行了修改(增加,删除,修改)。则会抛出Concurrent Modification Exception。
原理:迭代器在遍历的过程中,会使用一个modCount变量。集合在遍历期间如果内容发生变化,
就会改变modCount的值。迭代器使用hasNext()/next()遍历下一个元素之前,
都会检测modCount变量是否为expectedmodCount值,否则抛出异常,终止遍历。
注意:
这里异常抛出条件是检测到modCount!=expectedmodCoun这个条件。如果集合发生变化时修改modCount值
刚好又被设置为expectedmodCount值,则异常不会抛出(不能依赖这个判断)
场景:java.util包下面的集合类都是快速失败的,不能在多线程并发修改(迭代过程中被修改)
采用安全失败机制的集合容器。在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容(在拷贝上遍历)
原理:由于是基于集合的拷贝进行遍历,所以在遍历过程中对原有集合所作的修改不能被迭代器检测。
缺点:迭代器不能访问到修改后的内容。即:迭代器开始那一刻遍历的都是集合的拷贝对象。
遍历的期间原集合发生的修改迭代器是不知道的。
场景:
java.util.concurrent包下的容器都是安全失败,可以在多线程下使用。并发修改
HashMap是我们使用非常多的collection。基于Map接口实现。
提供了三个构造函数:
数据结构
HashMap:使用数组+链表来实现(数组的每一项都是一条链,其中参数initialCapacity就代表了该数组的长度。
储存的实现
public V put(K key, V value) {
//当key为null,调用putForNullKey方法,保存null与table第一个位置中,这是HashMap允许为null的原因
if (key == null)
return putForNullKey(value);
//计算key的hash值
int hash = hash(key.hashCode()); ------(1)
//计算key hash 值在 table 数组中的位置
int i = indexFor(hash, table.length); ------(2)
//从i出开始迭代 e,找到 key 保存的位置
for (Entry<K, V> e = table[i]; e != null; e = e.next) {
Object k;
//判断该条链上是否有hash值相同的(key相同)
//若存在相同,则直接覆盖value,返回旧value
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value; //旧值 = 新值
e.value = value;
e.recordAccess(this);
return oldValue; //返回旧值
}
}
//修改次数增加1
modCount++;
//将key、value添加至i位置处
addEntry(hash, key, value, i);
return null;
}
插入步骤:
读取的时候通过key的hash值找到table数组中的索引处的Entry,如何返回key对应的value即可。
当HashMap中的元素越来越多的时候,就会进行扩容(碰撞几率越来越高),为了提高查询效率
那么具体在什么情况进行扩容呢?
->当HashMap中的元素超过数据大小*loadFactor时,就会进行扩容。而loadFactor默认值为0.75。
上面也有提到,在初始化HashMap的时候,可以指定HashMap的loadFactor的大小。
比如 HashMap默认大小为16,loadFactor默认大小为0.75 .在大小为16*0.75=12的时候就会进行扩容
List与Set都是单列元素的集合,它们有一个共同的父接口collection
Set
存元素:add方法有一个boolean的返回值。当集合中没有某个元素,此时成功假如元素后,返回true
当集合中含有某个元素equals相等的元素时。此时add方法无法加入该集合,返回false
取元素:不能随机取,只能通过Iterator接口取得所有的元素,再逐一遍历各个元素
List
存元素:多次调用add(Object)方法时,根据先进先出。也可以调用add(int index,Object)方法(插队
取元素:①Iterator接口取得所有,逐一遍历各个元素
②调用get(index,i)来说明取第几个
Map
存元素:put(Object key,Object value),每次存储一对key/value,不能存储重复key,按equals比较相等
取元素:get(Object key)根据key获得相应的value
可以获得key的集合,也可以获得value的集合
还可以获得key和value组合成的Map.Entry对象的集合。
是用equals来区别重复的,因为Set集合里面存放的是一个个对象的引用,而equals比较的是引用的大小(比较符合)
“==”:用来判断是否为同一个对象。
“equals”:用来判断是否引用了同一个对象的
堆栈是两种数据结构。堆栈是一种数据项按序排列的数据结构。只能在一端堆数据项进行删除和插入。(在单片机中应用)。堆是个特俗的存储区,主要功能是暂时存放数据和地址。通常用来保护端点和现场。
一、 堆栈空间分配的区别
二、堆栈缓存方式的区别
三、堆栈数据结构区别:
总共由两大接口:Collection和Map,一个元素集合,一个是键值对集合;其中List和Set接口继承了Collection接口,一个是有序元素集合,一个是无序元素集合;而ArrayList和LinkedList实现了List接口,HashSet实现了Set接口。这几个都比较常用。HashMap和HashTable实现了Map接口;并且HashTable是线程安全的,但是HashMap性能更好
HashSet
当想HashSet中插入一个对象时,HashSet会先调用对象的hashCode()方法来获取到该对象的hashCode值,如何根据hashCode值来决定对象在HashSet中的存储位置,如果相等,那就不插入,如果不等,会调用equals方法,如果equals返回true,说明已经存在,就不能再插入,如果为false,则可以插入。
TreeSet
底层采用红-黑树的数据结构。使用这种数据结构可以重Set中获取有序的序列。但是前提条件是:元素必须实现Comparable接口。当往Set中插入一个新的元素的时候,首先会遍历Set中已经存在的元素。根据返回的结果,决定插入位置。进而也就保证了元素的顺序。并调用compareTo ()方法。
先看一下HashSet的源码
对于HashSet而言,它是基于HashMap实现的。HashSet底层使用的是HashMap来保存的元素。因此,HashSet的实现比较简单。不保证Set的迭代顺序。特别是它不保证顺序恒久不变,此类允许使用null元素。
HashSet的插入与删除都涉及到HashMap的插入与删除,判断HashMap的key
输出与插入顺序是一致的。
原理:
克隆
实现Serializable序列化的作用
实现Serializable接口的作用就是可以把对象存到字节流,然后可以恢复。所以你想你的对象没有序列化,怎么才能在网络传输呢?要网络传输就得转为字节流,所以在分布式应用中,你就得实现序列化。如果你不需要分布式应用,那就没必要实现序列化
空间大小比较:
适用场景
如果想要保存一些在整个程序运行期间都会存在而且不变的数据,我们可以将它们放进一个全局数组里,但是如果我们单纯只是想要以数组的形式保存数据,而不对数据进行增加等操作,只是方便我们进行查找的话,那么,我们就选择ArrayList。而且还有一个地方是必须知道的,就是如果我们需要对元素进行频繁的移动或删除,或者是处理的是超大量的数据,那么,使用ArrayList就真的不是一个好的选择,因为它的效率很低,使用数组进行这样的动作就很麻烦,那么,我们可以考虑选择LinkedList。