1.为什么使用集合
例如:存储数据
之前可以用数组存储数据,数组有很大的缺点,长度一旦确定,就没法修改,所以如果删除或者增加元素,需要大量的移动元素的位置。
数组:只能放一种数据类型,可以是基本数据类型也可以是引用数据类型
总行所述,可以使用集合来解决这种问题:
集合的优点:增加删除元素效率高
集合的特点:一个集合可以有多种数据类型(一般使用泛型,只存放一种数据类型),但是他只能存放引用数据类型
2. 集合的结构图
3.集合的应用场景
当需要将相同结构的个体整合到一起的时候,就考虑到用集合。
4.ArrayList
- ArrayList的使用
public class Test {
public static void main(String[] args) {
Collection collection=new ArrayList();//接口new实现类
System.out.println("集合是否为空:"+collection.isEmpty());
//(1)添加元素
collection.add("hello");
collection.add(new Date());
collection.add(123);
collection.add("hello");
collection.add(new Person("lisli",29, Gender.男));
System.out.println("集合是否为空:"+collection.isEmpty());
System.out.println("集合中元素的个数:"+collection.size());
System.out.println(collection);
System.out.println(collection.remove("hello")); //将第一个hello删除
//collection.clear();
System.out.println(collection);
System.out.println(collection.contains(123));
Collection collection1=new ArrayList();
collection1.add("world1");
collection1.add("world2");
collection1.add("world3");
collection.addAll(collection1);
System.out.println(collection);
//遍历集合
System.out.println("---------------------------------------");
for(Object obj:collection){
System.out.println(obj);//默认调用toString()方法
}
System.out.println("------------------自己使用迭代器遍历元素 hasNext() ,next()---------------------------");
Iterator ite= collection.iterator();
while(ite.hasNext()){ //true就说明集合中有数据,为false,说明集合中没有数据,可以退出循环
Object obj= ite.next();
System.out.println(obj);
}
}
}
- ArrayList的底层结构
ArrayList:是对Object类型数组的封装
(1)当调用ArrayList的无参构造方法创建ArrayList对象
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //初始长度为0,在堆里开空间了
transient Object[] elementData; //声明一个Object类型的数组,并没有new对象
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; //指向 是长度为0的那个数组
}
(2)当第一次调用add()方法添加元素时,给Object类型的数组初始化容量为10
private int size; //集合中元素的个数 ,默认值为0
private static final int DEFAULT_CAPACITY = 10;
public boolean add(E e) {
ensureCapacityInternal(size + 1); //调用本类中的方法
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {//minCapacity 的值为1
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//第一次调用add方法时执行
return Math.max(DEFAULT_CAPACITY, minCapacity); //return Math.max(10,1);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) { //计算出来的最小容量为10
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0) //10-0>0?true 说明数组的长度已完全不够用
grow(minCapacity);
}
private void grow(int minCapacity) { //10
// overflow-conscious code
int oldCapacity = elementData.length; //oldCapacity= 0
int newCapacity = oldCapacity + (oldCapacity >> 1); //0+0 结果为 newCapacity=0
if (newCapacity - minCapacity < 0) //0-10<0 true
newCapacity = minCapacity; //newCapacity=10;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity); //数组拷贝
}
(3) 当第十一次调用add()方法时,数组扩容为15
int newCapacity = oldCapacity + (oldCapacity >> 1);
对ArrayList的增删改查就是对Object类型的数组的增删改查
ArrayList的优点:节省存储空间,按照索引查询效率高
ArrayList的缺点:删除、添加需要移动大量的元素,效率低,按照内容查找主要逐个比较判断,效率低下
- LinkedList的使用
public class Test2 {
public static void main(String[] args) {
Collection collection=new LinkedList();//接口new实现类
System.out.println("集合是否为空:"+collection.isEmpty());
//(1)添加元素
collection.add("hello");
collection.add(new Date());
collection.add(123);
collection.add("hello");
collection.add(new Person("lisli",29, Gender.男));
System.out.println("集合是否为空:"+collection.isEmpty());
System.out.println("集合中元素的个数:"+collection.size());
System.out.println(collection);
System.out.println(collection.remove("hello")); //将第一个hello删除
//collection.clear();
System.out.println(collection);
System.out.println(collection.contains(123));
Collection collection1=new LinkedList();
collection1.add("world1");
collection1.add("world2");
collection1.add("world3");
collection.addAll(collection1);
System.out.println(collection);
//遍历集合
System.out.println("---------------------------------------");
for(Object obj:collection){
System.out.println(obj);//默认调用toString()方法
}
System.out.println("------------------自己使用迭代器遍历元素 hasNext() ,next()---------------------------");
Iterator ite= collection.iterator();
while(ite.hasNext()){ //true就说明集合中有数据,为false,说明集合中没有数据,可以退出循环
Object obj= ite.next();
System.out.println(obj);
}
}
}
- LinkedList
LinkedList的底层数据结构是链表,使用的是双向链表
transient Node<E> first; //默认值均为null
transient Node<E> last;
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null); //当调用add方法时,new Node创建节点对象
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
//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;
}
}
优点:删除,添加需要移动元素,效率高(但是需要先定位到元素上)
缺点:每个元素节点中,专门增加了空间来存储下个元素的地址,占用更多空间,每个节点地址不连续,无规律,导致按照索引查询效率低下
- 使用迭代器对象遍历元素,添加元素
public class Test2 {
public static void main(String[] args) {
List list=new ArrayList();
list.add("java");
list.add("hello");
list.add("world");
list.add("sql");
//想在遍历集合时添加元素
// Iterator ite= list.iterator();
ListIterator ite=list.listIterator();
while(ite.hasNext()){
Object obj=ite.next();
if(obj.equals("hello")){
ite.add("html");
}
}
System.out.println(list);
}
}
5.泛型
用于创建集合对象时对集合中所存储的元素的数据类型进行限制
泛型所用符号<数据类型>
泛型起作用的时间点:javac之前
public class Test {
public static void main(String[] args) {
//创建集合对象时,规则了集合中存储的元素的数据类型只能是String类型
List<String> list=new ArrayList<String>();
list.add("hello");
list.add("java");
list.add("world");
// list.add(123);
}
}
泛型的分类
(1)泛型接口
public interface Collection<E>{ //E代表一种数据类型,这个类型什么时候才能知道
}
//在创建接口的实现类对象时,才能知道,在如下代码中,E所代表的数据类型是Integer
Collection<Integer> coll=new ArrayList<Integer>();
(2)泛型类
public class ArrayList<E>{ //E的数据类型什么时候知道,创建ArrayList对象时知道
}
ArrayList<String> al=new ArrayList<String>();
(3)泛型方法
public boolean add(E e) {}
//E也代表一种数据类型 ,这个E的数据类型与ArrayList<E> 完全相同的,所以E的类型是在创建ArrayList的对象时确定
可变参数的泛型方法 :
(1)可变参数是JDK1.5
(2)泛型也是JDK1.5
public class Test {
public static void main(String[] args) {
Client<String> client=new Client<String>();//T的类型是String
client.show("hello");
//泛型方法,数据类型是在调用该方法时明确的,解决了同一个类中参数个数相同,类型不同的方法重载问题
client.fun("hello");
client.fun(123);
client.fun(new Date());
//可变参数的泛型方法,解决了数据类型不同,个数不同的方法重载问题
client.method("hello");
client.method("world1","world2","world3");
client.method(123);
client.method(8.7,9.8,7.6,9.9);
//可变参数实际上就是一个数组
Client<Integer> client2=new Client<Integer>();//T的类型时Integer
client2.show(123);
}
}