一、集合和数组的区别
1. 数组:
(1) 长度不可变
(2) 可以是任意类型
2. 集合
(2) 长度可变
(3) 引用类型
二、集合的关系结构
列出了最常使用的集合接口和实现类的关系。
三、java.util.Collection:接口
单列集合的根接口,里面定义的方法,子接口和实现类都有。
常用方法
public boolean add(E e)
: 把给定的对象添加到当前集合中 。public boolean contains(Object obj)
: 判断当前集合中是否包含给定的对象。public boolean isEmpty()
: 判断当前集合是否为空。public int size()
: 返回集合中元素的个数。public boolean remove(E e)
: 把给定的对象在当前集合中删除。public Object[] toArray()
: 把集合中的元素,存储到数组中public void clear()
:清空集合中所有的元素。
- 注意事项:
- Collection中没有定义索引,因为List有索引,而Set是没有索引的,索引仅仅是List接口的特性。
- toArray方法返回的是Object数组,需要强制类型转换调用子类特有方法
解决:使用迭代的方法
-
并不是所有的集合都有索引,以前使用for+get方法索引,没了索引就不行了。jdk提供了一中遍历集合的通用方式那就是使用迭代器
java.util.Iterator<E>
tip: 详细方法使用和其他方法详见API文档学习。
四、java.util.List:接口
继承自java.util.Collection
接口
1、特点
- 保证存入元素一次进来的顺序
- 有索引,实现类可以通过成员方法
get(int index)
访问指定位置的元素 - 存入的元素可重复
2、常用方法
List继承了Collection集合的全部方法,同时还有自己的特有方法
- 特有方法:
public void add(int index, E element)
: 将指定的元素,添加到该集合中的指定位置上。public E get(int index)
:返回集合中指定位置的元素。public E remove(int index)
: 移除列表中指定位置的元素, 返回的是被移除的元素。public E set(int index, E element)
:用指定元素替换集合中指定位置的元素,返回值的更新前的元素
3、实现类
1、java.util.ArrayList
(1)底层原理
-
底层是数组实现
a.每次先开辟一个定长为10的数组空间
b.如果空间不够,在开辟一个1.5倍原本长度+1的数组空间,c.把当前数组拷贝进去,最后销毁原本的数组
d.并且让集合对象指向新的数组。 -
删除元素的原理:
a.查找元素是否存在
b.存在,记录索引
c.创建一个新数组
d.把剩余元素拷贝进入新数组
e.数组变量指向新的数组地址
f.销毁老数组所占的内存空间
(2)特点
- 查询速度快,增删快
- 底层是数组结构
- 查询快的原因:有索引,元素内存分布连续
- 线程不同步–不安全–效率高
(3)拓展了解
`java.util.Vector<E>:`(基本已经不用)
1.底层也是数组结构
2.线程同步--安全--但是效率低下
2、java.util.LinkedList
(1)特点
- 查询慢,增删快
- 底层是链表结构
- 线程不同步–不安全–效率高
(2)特有方法
-
void addFirst(E e)
在该列表开头插入指定的元素。 -
void addLast(E e)
将指定的元素追加到此列表的末尾。 -
E getFirst()
返回此列表中的第一个元素。 -
E getLast()
返回此列表中的最后一个元素。 -
E removeFirst()
从此列表中删除并返回第一个元素。 -
E removeLast()
从此列表中删除并返回最后一个元素。 -
LinkedList
集合中,具有栈结构特点(先进后出)的方法
public E pop()
从此列表表示的堆栈中弹出一个元素。
public void push(E e)
将元素推送到由此列表表示的堆栈上。
五、 java.util.Set:接口
继承自java.util.Collection
接口
1、特点
- 无索引
- 集合内元素唯一
2、常用实现类
(1)java.util.HashSet
特点
- 无索引
- 集合内元素唯一
- 不保证存入和取出的顺序是一致的
- 底层数据结构:哈希表——数组 + 链表(节点数>8–红黑树)
(2)java.util.LinkedHashSet
特点
- 无索引
- 集合内元素唯一
- 存入和取出是有序的
- 底层数据结构:哈希表 + 链表
哈希表:保证数据的唯一(同HashSet)
链表:保证数据是有序的
3、集合内部保证元素唯一性的原理
根本原因:java.lang.Object
类中带有hashCode
方法。
hashCode
方法
- 返回该对象的哈希码值(int类型)
- Object的
hashCode
方法,根据对象的地址值计算出一个int数字,叫做哈希码值(哈希地址值) - 所以只要new对象,就会产生新的地址,因此哈希值也就不同
注意事项:
- 子类不重写hashCode方法,调用Object类的hashCode方法,根据对象地址值计算哈希值。
- 子类根据地址值计算哈希值没有意义,需要根据内容计算哈希值。
- 由于new对象,就会产生新的地址,因此哈希值也就不同,自定义类要实现存入Set集合保证唯一就必须要重写
hashCode
方法和equals
方法。 - 根据String的代码测试,发现String覆盖重写hashCode方法,重写方式:根据String中每个字符的ASCII码值通过一个算法进行相加的出哈希值。只要是算法就会存在缺陷。
出现内容不同哈希值相同的情况
System.out.println("重地".hashCode()); // 1179395
System.out.println("通话".hashCode()); // 1179395
总结–根据内容计算哈希值:
- 哈希值不同内容一定不相同
- 哈希值相同,内容可能不相同,因此还需要调用equeals方法进行内容的判断。
为什么需要hashCode方法?
为了在使用哈希表的时候能减少equals方法的调用,提高效率。
4、hashSet存储元素的原理(重点!)
(1)哈希表结构图
(2)HashSet存储原理流程图
(3)实例流程图
注:
【1】加载因子:说明什么时候数组(table)进行扩容,0.75说明数组(table)的使用比例大于0.75就进行扩容,扩容为原来的2倍。
总结
Set
集合接口所有实现类保证元素唯一:依赖于hashCode
方法和equals
方法。
存入Set
集合的元素要保证唯一必须覆写hashCode
方法和equals
方法。
六、List与Set的转换
- List与Set接口的实现类都有方法:
boolean addAll(Collection<? extends E> c)
可以实现两种接口集合的转换。 - List与Set接口的实现类都有构方法:
ArrayList(Collection<? extends E> c)
、LinkedList(Collection<? extends E> c)
、HashSet(Collection<? extends E> c)和LinkedHashSet(Collection<? extends E> c)可以实现两种接口类型的转换。
使用场景:
需求一:
要求把List集合中元素去重
1.按顺序
2.不按顺序
解决方案:
- 四种方法:
1、新建一个列表,元素依次加入并使用contains()
方法判断是否已经存在,新数组中不存在就加入,存在就舍弃。
2、List
数据一个个加入到Set
中
3、Set直接用addAll(Collection< E> c)
4、Set构造方法Set(Collection< E> c)
需求二:
要求把Set集合按从小到大排序
分析与解决方案
我们知道Set集合是没有sort方法的,并且Collections工具类中的sort方法也是支持List接口的实现子类。
由于List的构造方法可以接受的是Collection c 参数,所以也可以把Set转换成List,之后使用collections工具类排序
七、集合工具类:java.util.Collections
- 特点
- 构造方法私有
- 全部是静态方法
- 常用方法:
public static void addAll(List<?> list,T ... elements)
:把元素集合加入到指定集合listpublic static void shuffle(List<?> list)
:打乱集合内元素pubiic static <T> void sort(List<T> list,comparator<? super T> comp)
:把指定集合进行排序(可自定义排序)