概述
与数组类似,用于存储多个数据的容器;
与数组的区别:
数组:类型相同,长度固定 集合:类型可以不同,长度不固定
数组可存基本类型或引用类型 集合只能存引用类型
数组不能调方法 集合是一个类,可调用方法操作元素
集合架构
集合是一个家族分为单列集合和双列集合,单列集合最大的根接口为Collection;下面所有的集合类或接口都是它的子接口或实现类
Collection有两个重要的子接口: List,Set
List接口下面有两个重要的实现类:ArrayList,LinkedList
Set接口下面的两个重要的实现类:HashSet, TreeSet
List接口存储特点:存储元素有下标,有序,可重复
Set接口存储特点:存储元素无下标,无序,唯一
单列集合
Collection
单列集合最大的根接口,特点是无下标
基本用法
Collection col = new ArrayList();
col.add(1); //添加元素 自动装箱--Integer
col.add(3.5); //Double
col.add(2);
System.out.println(col); //[1, 3.5, 2]
Collection col2 = new ArrayList();
col2.add(6);
col.addAll(col2); //添加集合
System.out.println(col); //[1, 3.5, 2, 6]
col2.clear(); //清空元素
System.out.println(col2); //[]
System.out.println(col.contains(3.5)); //true 判断集合中是否包含指定元素
System.out.println(col.equals(1)); //false 判断集合元素是否相等
System.out.println(col.isEmpty()); //false 判断集合是否为空
System.out.println(col.remove(2)); //true 移除指定元素
System.out.println(col);
System.out.println(col.size()); //获取集合元素个数
Object[] o = col.toArray(); //集合转数组
System.out.println(Arrays.toString(o));
循环遍历
//Colletion循环遍历:
Collection col = new ArrayList();
col.add(1);
col.add(3);
col.add(2);
//1.基本for--操作下标,不可以;无下标
for(int i=0;i<col.size();i++) {}
//2.增强for foreach
for (Object obj : col) { //本质是迭代器-查反编译工具
System.out.println(obj);
}
System.out.println("========");
//3.迭代器
Iterator it = col.iterator();
while(it.hasNext()) { //判断是否有下一个元素
System.out.println(it.next()); //有则取出,并移位
}
List
List集合是Collection的子接口,在Collection标准基础上,扩充了一些与下标操作相关的方法
存储元素特点:有序,有下标,允许重复
基本用法
//List的基本操作及存储特点:
List list = new ArrayList(); //ArrayList是List的实现类
list.add(1);
list.add(3);
list.add(2);
list.add(2); //允许重复
System.out.println(list); //1,3,2,2 有序
list.add(1, 666); //指定下标位置添加元素
System.out.println(list);
List list2 = new ArrayList();
list2.add(5);
list.addAll(1, list2); //指定位置存集合
System.out.println(list);
System.out.println(list.get(2)); //根据下标取元素
list.set(2, 999); //指定位置的修改
System.out.println(list);
List list3 = list.subList(1, 3); //提取子集合
System.out.println(list3); //根据开始和结束下标提取集合,不包括结束下标
//除以上方法外,Collection接口中提供的方法依然能用
list.remove(0); //根据下标移除
System.out.println(list);
循环遍历
//List循环遍历:
List list = new ArrayList();
list.add(1);
list.add(3);
list.add(2);
//1.基本for
for(int i=0;i<list.size();i++) {
System.out.println(list.get(i));
}
System.out.println("==========");
//2.增强for
for (Object o : list) {
System.out.println(o);
}
System.out.println("=====ListIterator(扩展)=====");
ListIterator li = list.listIterator(); //有迭代器功能和反向遍历
while(li.hasNext()) { //判断是否有下一个
System.out.println(li.next()); //有下一个则取出
}
System.out.println("-----------");
while(li.hasPrevious()) { //判断是否有上一个(在迭代器指向最后,才能用)
System.out.println(li.previous()); //取出后,指向上一个
}
ArrayList与LinkedList
List有两个重要的实现类,ArrayList和LinkedList,他们的存储特点都是一致的,调用方法也相同;但是存储原理不同
ArrayList
存储原理:通过数组扩容方式进行存储
细化原理:当存储第一个元素,内部开是个数组空间用于存元素;当没空间了,则进行扩容,并拷贝原数组值,每次扩当前元素的一半。
通过画图分析+原码分析,理解ArrayList的存储原理
说明:数组是连续的空间,本身有下标标记,很容易就找到下标的元素
LinkedList
存储原理:通过双向链表方式存储
细化原理:每次存值,都开空间节点,准备三个属性,pre,data,next;data存值,pre节点指向上一个节点,next指向下一个节点;当存储第二个值,开空间,并将旧节点的next执行新节点,新节点的pre指向上一个节点;这样就构成了双向链表。
通过画图分析+原码分析,理解LinkedList的存储原理
说明:链表的空间不连续,通过上一个或下一个节点的指向去定位元素
区别
在List接口中上面的两个实现类都可以完成存储数据的功能,除了存储功能,还有删除,查找,修改等功能,分析两个实现类的性能。
添加
向后追加:ArrayList和LinekdList都需要开空间,ArrayList稍快
指定位置追加:ArrayList考虑集体搬迁 PK LinkedList考虑定位(快)
删除(下标删)
与指定位置追加类似,ArrayList要集体搬迁 ,LinkedList要定位;LinkedList快
查找
查找就是定位;数组是连续内存空间,本身有下标标记,所以定位快;ArrayList快
修改
修改=查找+赋值;ArrayList快
结论:后续常用ArrayList,因为常用操作是查找和向后追加
Set
Set接口:Collection的子接口,Set接口没有提供抽象方法,全部继承Collection
Set的特点:无序、无下标,唯一
HashSet
存储特点:无序,无下标,唯一
HashSet存储方式:hash算法+数组+链表
细化原理:
存储的对象要得到hash值,hash值在hash表中(数组)得到一个下标位置;判断该下标位置是否有值,如果没有则直接存储;如果有,与该位置的链表元素一一比较;如果相等,则退出,确定唯一性;如果都不相等,则存储到该链表中。
分析规则: 画图分析+原码分析
结论:存储的元素要确定唯一性,则必须重写hashCode和equals
//基本使用:
Set<Integer> set = new HashSet<Integer>();
set.add(11);
set.add(33);
set.add(22);
set.add(22); //唯一
System.out.println(set); //HashSet特点:无序,唯一
//Set是Collection的子接口,所以Collection的常用方法,Set可完全继承
//循环遍历:1.基本for 无下标,不能使用
//for(int i=0;i<set.size();i++) {}
//增强for:有增强for,所以肯定有迭代器
for (Integer integer : set) {
System.out.println(integer);
}
TreeSet
存储特点:可排序,无下标,唯一
TreeSet的存储方式:二叉树
细化原理:存第一个元素时,作为根节点;再次存储,则于根比较;比根大则放右边,比根小放左边;查看左/右子树是否有节点,如果没有,则直接存;如果有节点,则继续比较;依次类推;如果找到相等的,则退出,确定唯一性;否则,比较到最后,存进来。
分析规则:画图分析+源码分析
结论:存自定义对象主要要看比较的规则:1.自然排序法(Comparable接口) 2.比较器法
//TreeSet基本应用
Set<Integer> set = new TreeSet<Integer>();
set.add(11);
set.add(33);
set.add(22);
set.add(22);
System.out.println(set); //[11, 22, 33] 可排序,唯一
//循环遍历: 1.基本for--没有基本for(无下标)
//for(int i=0;i<set.size();i++) {}
//2.增强for 可以有--迭代器一样也有
for (Integer integer : set) {
System.out.println(integer);
}
因为TreeSet具有可排序的特点,所以可以根据自定义的对象所具有的属性进行对数据的排序
//自定义类为一个属性的情况:
//说明:存储自定义对象,自定义类要么实现Comparable接口;要么使用比较器
class Student implements Comparable<Student>{
String name;
public Student(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student [name=" + name + "]";
}
@Override
public int compareTo(Student o) { //重写的比较方法中,只要返回结果于源码对应上即可
//将对象的比较,转成属性的比较
//String的compareTo:前面的小返回<0 前面的大则返回>0 相等则返回0
//return this.name.compareTo(o.name); //返回<0则存左子树;>0则存右子树;=0唯一性
return o.name.compareTo(this.name); //比较规则反过来则是降序排列
}
}
public class Test2 {
public static void main(String[] args) {
Set<Student> set = new TreeSet<Student>();
set.add(new Student("zs"));
//ClassCastException: Student cannot be cast to Comparable
set.add(new Student("ls"));
set.add(new Student("ww"));
set.add(new Student("zs"));
System.out.println(set);
}
}
双列集合
Map
概述:和Collection同级的根接口,Collection子孙类都是存单个对象;Map接口子孙类存的都是两个对象(键值对)
应用场景:往往key是已知的(基本上key是String类型),value是未知的; 后续我们需要去配置文件中取值,key就是已知字符串;值是我们可以随意更换
Map接口有两个重要的实现类: HashMap,TreeMap; 此外还有Hashtable,Properties
HashMap
描述:HashMap是Map接口的实现类,集合中存储键值对
存储特点:key无序,无下标,唯一; 确定唯一性后,后面的value会覆盖前面的。
HashMap的存储原理与HashSet是类似的,都是通过hash算法+数组+链表的方式
直接分析源码即可得出结论
结论:存储key的类要重写hashCode和equals才能确定唯一性
//HashMap的基本应用:
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("aa", 666);
map.put("cc", 999);
map.put("bb", 666);
map.put("bb", 999); //key无序,唯一;key相等后,value会覆盖前面的
System.out.println(map);
System.out.println(map.get("cc")); //根据key获取value值
Collection<Integer> col = map.values(); //将所有value放入Collection集合
System.out.println(col);
//循环遍历:1.基本for---不行,无下标
//for(int i=0;i<map.size();i++) {}
//增强for: 不行,没有统一的类型; 迭代器也不行
//for(String key :map) {}
//只能间接循环: keySet entrySet
//1.keySet:将所有key放入Set集合,然后循环根据key遍历出value
Set<String> set = map.keySet();
for (String key : set) {
System.out.println(key+"--"+map.get(key));
}
System.out.println("=======================");
//2.entrySet:将键值对作为实体存到Set;再循环Set取出实体拿出里面的键值对
Set<Entry<String, Integer>> entries = map.entrySet();
for(Entry<String, Integer> entry:entries) {
System.out.println(entry.getKey()+"=="+entry.getValue());
}
TreeMap
TreeMap的存储特点:key可排序,唯一; 确定唯一后,后面的值覆盖前面的
存储原理:通过二叉树存储
TreeMap的实现原理与TreeSet类似的,此处得出结论即可
结论:要有比较的规则:自然排序法(实现Comparable接口)
//TreeMap的基本应用:
Map<String, Integer> map = new TreeMap<String, Integer>();
map.put("aa", 11);
map.put("cc", 11);
map.put("bb", 11);
map.put("aa", 66);
System.out.println(map); //3
System.out.println(map.get("cc")); //根据key获取value
//循环遍历:keySet,entrySet
Set<String> set = map.keySet();
for(String key:set) {
System.out.println(key+"=="+map.get(key));
}
System.out.println("-----------------");
Set<Entry<String, Integer>> ens = map.entrySet();
for(Entry<String, Integer> entry:ens) {
System.out.println(entry.getKey()+"--"+entry.getValue());
}
Map的选择
HashMap与TreeMap的比较:
优先选择HashMap,因为性能更高
除非需要排序,才选择TreeMap; HashSet与TreeSet也类似
应用案例
案例:有一个字符串,求出每个字符串的个数: “ddcada”; 得出结果:d--3 a--2 c--1
分析:需要用到键值对,选择Map集合;没有排序的要求,则使用HashMap存储
//案例:有一个字符串,求出每个字符串的个数: “ddcada”; 得出结果:d--3 a--2 c--1
String a = "ddcada"; //将字符串转字符数组,再循环遍历
char[] cs = a.toCharArray();
Map<Character, Integer> map = new HashMap<>();//key为字符 值为次数
for(int i=0;i<cs.length;i++) {
Integer count = map.get(cs[i]); //根据key获取value; 没有则返回null
if(count==null) { //为空
map.put(cs[i], 1); //存储1个
}else { //不为null,次数+1
map.put(cs[i], count+1);
}
}
System.out.println(map);
集合汇总
Collection VS Map: 存单个对象 存键值对
Collections VS Collection: 工具类 集合接口
List VS Set: 有序,有下标,可重复 无序,无下标唯一
ArrayList VS LinkedList 数组扩容;查询修改快 双向链表; 增删快
Set VS Map 单个对象 存键值对
HashSet VS HashMap 对象无序,唯一 key无序唯一; 两者原理一致;具体实现Map完成
TreeSet VS TreeMap 对象可排序,唯一 key可排序唯一;两者原理一致;具体实现Map完成
HashSet VS TreeSet 对象无序,唯一 对象可排序唯一
HashMap VS TreeMap key无序,唯一 key可排序唯一