目录
集合和数组的区别
Collection接口
ArrayList实现类
ArrayList的创建和使用
linkedList
Set接口及其实现类
Set接口特点
HashSet实现类
HashSet特点:
HashSet避免对象重复的规则:
迭代器Iterator
TreeSet
TreesSet特点:
TreeSet对元素进行排序的方式:
Map接口及其实现类
HashMap实现类
集合就是用于存储对象的容器。 只要是对象类型就可以存进集合框架中。集合的长度是可变的。 集合中不可以存储基本数据类型的值 。
集合和数组的区别
数组和集合相比,数组的长度是固定的,没有办法动态扩展。二集合存储数据时是没有长度限制的,可以动态扩展。集合容器因为内部的数据结构不同,有多种不同的容器对象。这些容器对象不断的向上抽取,就形成了集合框架。
Collection接口
Collection接口是单值集合的顶层接口,继承关系如下
返回类型 |
方法名称 |
描述 |
boolean |
add(Object o) |
在集合末尾添加元素 |
int |
size() |
返回集合列表中元素个数 |
boolean |
removeAll(Collection col) |
删除集合中的所有元素 |
boolean |
contains(Object o) |
判断集合中是否存在指定的元素 |
boolean |
remove(Object o) |
从集合中删除元素 |
void |
clear() |
清除集合中的所有元素 |
Iterator |
iterator() |
为Iterator接口实列化 |
List接口及其实现类
1、特点
- list集合是有序集合:数据的添加和存储次序一致
- list集合可以存储重复的数据
- list集合中的数据可以通过下标访问
ArrayList实现类
特点:
- 实现了list接口
- 可以动态扩容,底层自动扩容
- 通过下标可以快速访问数据
- 查找快,插入删除慢
- ArrayList底层是数组,对数组做了封装
- 可以存储任意类型的数据,包括null
- 数据按照存储次序排列
- 数据可以重复
- 多线程访问时不安全
ArrayList的创建和使用
//创建一个集合对象 若没有指定集合容器的长度,默认为10
List list = new ArrayList();
//添加元素
list.add("aas");
list.add("artrgh");
list.add("agfdg");
list.add("sdfd");
list.add("你好");
List list1 = new ArrayList();
//将集合list中的元素都添加到集合list1中
list1.addAll(list);
System.out.println(list1);
//修改元素
list.set(2,"你好");
System.out.println(list);
//删除元素
//元素内容删除,若有重复数据,只删除第一个
// list.remove("你好");
// System.out.println(list);
//下标删除
// list.remove(2);
// System.out.println(list);
//查找
//元素查找,找到元素的下标
int a = list.indexOf("你好");
System.out.println("你好的下标是:"+a);
//下标查找,得到元素
Object adss = list.get(1);
System.out.println(adss);
//获取集合中元素的个数
int as = list.size();
System.out.println(as);
//判断某元素是否在集合中
boolean fa = list.contains("你好");
System.out.println(fa);
//for循环
for (Object asd:list
) {
System.out.println(asd);
}
linkedList
LinkedList: 具有List的特征,底层以链表结构实现,可以进行头尾元素的添加删除
//添加元素
LinkedList list = new LinkedList();
list.add("aas");
list.add("artrgh");
list.add("agfdg");
list.add("sdfd");
//首尾添加
list.addFirst("你好啊");
list.addLast("你好");
System.out.println(list);
//首尾删除
list.removeFirst();
list.removeLast();
System.out.println(list);
//查询操作
int size = list.size();//集合长度
boolean empty = list.isEmpty();//是否为空
Object first = list.getFirst();//获取第一个元素
Object last = list.getLast();//获取最后一个元素
System.out.println(first);
System.out.println(last);
Set接口及其实现类
Set接口特点
- Set接口是无序的
- Set接口中的数据不允许重复
- Set接口无法通过下标访问数据
- 查找慢,插入删除快(底层数据结构是哈希表和红黑树)
- Set集合使用equals()和hashCode()方法实现元素去重
HashSet实现类
HashSet特点:
//如果没有指定容器大小,默认为16,负载因子为0.75
HashSet hashSet = new HashSet();
//添加元素
hashSet.add("dfsdf");
hashSet.add("sdfgsdg");
hashSet.add("sdfette");
System.out.println(hashSet);
//删除元素
hashSet.remove("dfsdf");
System.out.println(hashSet);
//清空集合元素
// hashSet.clear();
// System.out.println(hashSet);
boolean dfs= hashSet.isEmpty();//判断容器是否为空
boolean sdf = hashSet.contains("dfsdf");//判断是否包含某元素
System.out.println(dfs+" "+sdf);
//迭代器遍历
Iterator iterator = hashSet.iterator();//获取迭代对象,有序:由下标
while(iterator.hasNext()){//判断指针是否可以指定移动
System.out.println(iterator.next());
HashSet避免对象重复的规则:
1)如果对象的hashCode值不同,则不用判断equals方法,就直接存到HashSet中。
2)如果对象的hashCode值相同,需要用equals方法进行比较,如果结果为true,则视为相同元素,不存储。如果结果为false,视为不同元素,进行存储。
迭代器Iterator
迭代器(iterator)有时又称游标(cursor)是程序设计的软件设计模式,可在容器对象(container,例如链表或数组)上遍访的接口,设计人员无需关心容器对象的内存分配的实现细节。
HashSet类中没有提供根据集合索引获取索引对应的值的⽅法,因此遍历HashSet时需要使⽤Iterator迭代器。Iterator的主要⽅法如下
返回类型 |
方法 |
描述 |
boolean |
hasNext() |
如果有元素可迭代 |
Object |
next() |
返回迭代的下⼀个元素 |
TreeSet
TreeSet 基于TreeMap 实现。TreeSet可以实现有序集合,但是有序性需要通过比较器实现。
TreesSet特点:
- 有序
- 不重复
- 添加、删除、判断元素存在性效率比较高
- 线程不安全
TreeSet对元素进行排序的方式:
1) 如果是基本数据类型和String类型,无需其它操作,可以直接进行排序。
2) 对象类型元素排序,需要实现Comparable接口,并覆盖其compareTo方法。
3) 自己定义实现了Comparator接口的排序类,并将其传给TreeSet,实现自定义的排序规则。
Map接口及其实现类
Map接口特点:
- 以键值对方式存储数据(Collection是单值集合)
- 键不能重复,键重复时,后面的数据会覆盖前面的数据
- 可以存储null
- 键值对数据无序
返回类型 |
方法 |
描述 |
Object |
get(Object key) |
根据key取得value |
Object |
put(Obejct k,Object v) |
向集合中加入元素 |
void |
clear() |
清除Map集合 |
boolean |
isEmpty() |
判断集合是否为空 |
boolean |
containsKey(Object object) |
判断指定的key是否存在 |
boolean |
containsValue(Object value) |
判断指定的value是否存在 |
Set |
keySet() |
Map中所有的key的集合 |
Object |
remove(Object key) |
根据key删除对应的value |
Collection |
values() |
取出全部的value |
int |
size() |
获得集合的长度 |
HashMap实现类
HashMap实现了Map接口,拥有Map接口的基本特点。HashMap线程不安全,效率高。HashMap的底层是由哈希表、链表加红黑树构成的。
public class Test01 {
public static void main(String[] args) {
TreeSet t1 = new TreeSet();//TreeSet不允许重复元素
t1.add(new student("孙猴子",18));
t1.add(new student("猪八戒",52));
t1.add(new student("沙僧",52));
t1.add(new student("唐僧",12));
System.out.println(t1);
}
}
class student implements Comparable{
private String name;
private int age;
public student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//排序:返回结果如果大于0,表示当前元素比o大,如果返回-1,表示当前元素比o小,返回0,表示相同元素
@Override
public int compareTo(Object o) {
student s1 = (student)o;
System.out.println(this+"-----------------------"+o);
if (this.age>s1.age){
return 1;
}
if (this.age<s1.age){
return -1;
}
return 0;
}
@Override
public String toString() {
return "student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
HashMap底层原理
HashMap的put()和get()的实现
1) map.put(key,value)实现原理
第一步:首先将k,v封装到Node对象当中(节点)。
第二步:它的底层会调用K的hashCode()方法得出hash值。
第三步:通过哈希表函数/哈希算法,将hash值转换成数组的下标,下标位置上如果没有任何元素,就把Node添加到这个位置上。如果说下标对应的位置上有链表。此时,就会拿着k和链表上每个节点的k进行equals。如果所有的equals方法返回都是false,那么这个新的节点将被添加到链表的末尾。如其中有一个equals返回了true,那么这个节点的value将会被覆盖。
2) map.get(key) 实现原理
第一步:先调用k的hashCode()方法得出哈希值,并通过哈希算法转换成数组的下标。
第二步:通过上一步哈希算法转换成数组的下标之后,在通过数组下标快速定位到某个位置上。重点理解如果这个位置上什么都没有,则返回null。如果这个位置上有单向链表,那么它就会拿着参数K和单向链表上的每一个节点的K进行equals,如果所有equals方法都返回false,则get方法返回null。如果其中一个节点的K和参数K进行equals返回true,那么此时该节点的value就是我们要找的value了,get方法最终返回这个要找的value。
Hashtable
Hashtable也实现了Map接口,但是与HashMap相比有如下特点:
- 以键值对方式存储数据
- 键不能重复
- 数据无序
- 键和值都不能存储null
- 线程安全,效率低
Hashtable hashtable =new Hashtable();
hashtable.put("aaa",100);
//hashtable.put("aaa",null);
hashtable.put(null,100);
hashtable.put("xxx",100);
System.out.println(hashtable);
泛型
泛型:对要管理的数据进行类型限定,方便数据的处理。
为什么实用泛型:
在往集合中存储数据的时候缺乏类型检查,不安全
泛型:类型检查机制;
好处:省略了装箱和拆箱,效率高;类型安全。
-
泛型类:针对类中的属性限定类型。在具体使用的时候设置类
public static void main(String[] args) {
List<String> list = new ArrayList<>();//这里限制了集合元素的类型,ArrayList后面 的<>在jdk1.8后可以省略,也可不写限制类型
list.add("sadfg");
list.add("孙悟空");
String s1 = list.get(0);//在获取元素时,默认就是相应的数据类型,而无需进行强制转换
//<k,v>:K:表示键的泛型, v:表示值的泛型
HashMap<String,Integer> hashMap = new HashMap<>();
hashMap.put("年龄",18);
}
自定义链表
链表是最简单的动态数据结构,数据存储在节点(Node)中,其节点的数据结构如下:
class Node{
E e;//数据存储的地方
Node next;//也是一个节点,他指向当前节点的下一个节点
}
我们可以把链表理解成为一个火车,每个链表,其实就是一节车厢,数据存储在车厢中中,而每个火车节都有一个指针,连接着下一个火车节。
链表有一个优点:
真正的动态数据结构,无需关系创建的空间是否过大,不需要像数据一样担心容量的问题。
缺点:
不能像数组那样,给一个索引就能查找到指定的值。
1.单向链表
单向链表就是通过每个结点的指针指向下一个结点从而链接起来的结构,最后一个节点的next指向null。
2.单向循环链表
单向循环链表和单向列表的不同是,最后一个节点的next不是指向null,而是指向head节点,形成一个“环”。
.双向链表
从名字就可以看出,双向链表是包含两个指针的,pre指向前一个节点,next指向后一个节点,但是第一个节点head的pre指向null,最后一个节点的tail指向null。
.双向循环链表
双向循环链表和双向链表的不同在于,第一个节点的pre指向最后一个节点,最后一个节点的next指向第一个节点,也形成一个“环”。而LinkedList就是基于双向循环链表设计的。
集合使用总结:
1) 需要数据唯一时,使用Set集合
- 需要保证代码添加数据的次序,此时用LinkedHashSet
2) 不需要唯一时,使用List集合
- 不需要频繁增删操作,需要做大量查询操作时,使用ArrayList
3) 如果以键值对方式存储数据时,使用HashMap
4) 通过类名记住集合框架类的结构和所属体系
List: ArrayList LinkedList
Set : HashSet TreeSet
后缀名就是该集合所属的体系。 前缀名就是该集合所属的数据结构。
看到array:想到的就是数组,查询速度快。因为有角标是连续的内存地址。
看到link:想到的就是链表,增删动作的速度快,而且要想到
add get remove+first/last的 方法
看到hash:想到的就是哈希表,那么就具有唯一性,
而且要想到元素需要覆盖hashcode方法 和equals方法
看到tree:想到的就是二叉树,接下来想到的就是排序,然后就是两个接口
Comparable接口 实现compareTo(To)
Comparator接口 实现compare() 方法