集合框架
https://www.bilibili.com/video/BV1zD4y1Q7Fw?p=43
一.什么是集合
-
概念:存储对象的容器,定义了对象的常用操作方法,类似数组的功能。
-
和数组的区别:
(1)数组长度固定,集合长度不固定
(2)数组可以存储基本类型和引用类型,集合只能存储引用类型(存储基本类型可使用装箱操作)
-
位置:java.util.*
二.Collection体系集合
Collection接口
特点:代表一组任意类型的对象,有部分有序,有部分无序,无下标,有部分可重复,有部分不可重复
有序(添加和遍历顺序一致),有下标(可以通过下标访问),元素可重复
方法:
boolean add(Object obj) //添加一个对象
boolean addAll(Collection c) //将一个集合中的所有对象添加到此集合中
void clear() //清空此集合中所有元素
boolean contains(Object o) //检查此集合是否包含o对象
boolean equals(Object o) //比较两个对象是否相等
boolean isEmpty() //判断集合是否为空
boolean remove(Object o) //在此集合中移除o对象
int size() //获取集合中元素个数
Object[] toArray() //将此集合转换成数组
boolean removeAll(Collection c) //删除与集合c重复的元素
boolean retainAll(Collection c) //保留与c的交集
Iterator itrrator() //返回在此Collection的元素上迭代的迭代器,是个接口
代码展示
public class Demo1 {
public static void main(String[] args) {
//创建集合
Collection collection = new ArrayList();
//(1)添加元素
collection.add("迪迦");
collection.add("戴拿");
collection.add("盖亚");
System.out.println("元素个数" + collection.size());
System.out.println(collection);
//(2)删除元素
collection.remove("迪迦");
System.out.println(collection);
// collection.clear();
//(3)遍历元素[重点]
//方法一:增强for
for (Object object : collection) {
System.out.println(object);
}
//方法二:迭代器,专门用来遍历集合的一个接口,有三个方法
//hasNext();有无下一个元素
//next();获取下一个元素
//remove();删除当前元素即iterator.next();的返回元素
Iterator iterator = collection.iterator();
while (iterator.hasNext()){
Object object = iterator.next();
System.out.println(object);
}
//(4)判断
System.out.println(collection.contains("阿古茹"));
}
}
注意
-
collection.add() 仅是把地址加入collection对象
-
collection.remove() 删除对象仅是把地址从collection对象中删除,但三个对象仍存在
-
遍历输出时,可将Object类型强转成Student类型,以便输出值
-
迭代过程中,不能使用collection的删除方法
List接口(Collection的子接口)
-
ArrayList实现了接口List
List heros = new ArrayList(); //List父类,ArrayList子类
-
特点:有序(添加和遍历顺序一致),有下标(可以通过下标访问),元素可重复
方法
void add(int index , Object o) //在index位置插入对象o
boolean addAll(int index , Collection o) //将一个集合中的元素添加到此集合中的index位置
Object get(int index) //返回集合中指定位置的元素
List subList(int fromIndex, int toIndex) //返回fromIndex和toIndex之间的集合元素
ListIterator listiterator() //返回迭代器,迭代器存储当前对象的一系列信息,功能强大,可前可后遍历,可返回下标
boolean remove(Object o) //首先判断位置删除,其次可指定Object 20 即删除值为20的元素
ArrayList(List实现类)
-
特点:数组结构实现,查询快,增删慢,需开辟连续空间
JDK1.2版本,运行效率快,线程不安全
源码分析
-
默认容量大小 10
private static final int DEFAULT_CAPACITY = 10;
-
存放元素的数组
transient Object[] elementData;
-
size实际元素个数
private int size;
-
无参构造方法(如果没有向集合添加任何元素,容量为0,添加任意1个元素后容量变为10,由add方法调用,填满后自动扩容原来的1.5倍 因为>>2 右移两位 )
public ArrayList()
常用方法
因为ArrayList实现了List接口,所以List接口的方法ArrayList都实现了
-
01 add
加对象
```jArrayList heros = new ArrayList();//定义容器
Hero h1 = new Hero(special Hero);//调用重写的构造
heros.add(h1);//最后位置加对象
heros.add(3,h1);//指定位置3
-
02 contains
判断是否存在该对象
ArrayList heros = new ArrayList();
heros.contains(new Hero("hero 1"));//返回true或flase,这里因为是新创建的hero 1 不是一个对象,所以flase
heros.contains(specialHero);//有speacialHero对象则返回true
-
03 get
获取指定位置对象
ArrayList heros = new ArrayList();//定义容器
sout(heros.get(5));//返回对象名
-
04 indexOF
获取对象所处位置
heros.indexOf(specialHero);//获取specialHero对象所处位置
-
05 remove
删除
heros.remove(2);//根据下标删除
heros.remove(specialHero);//根据对象删除
-
06 set
heros.set(5, new Hero("hero 5"));//替换下标为5的对象
-
07 size
获取ArrayList的大小
heros.size();//返回heros的对象个数
-
08 toArray
把一个ArrayList对象转换为数组
如果要转换为一个Hero数组,那么需要传递一个Hero数组类型的对象给toArray(),这样toArray方法才知道,你希望转换为哪种类型的数组,否则只能转换为Object数组
Hero hs[] = (Hero[])heros.toArray(new Hero[]{}); //()强转
-
09 addAll
把另一个容器所有对象都加进来
ArrayList heros = new ArrayList();
ArrayList anotherHeros = new ArrayList();
heros.addAll(anotherHeros);
-
10 clear
清空一个ArrayList
heros.clear();
遍历ArrayList
for循环遍历
for(int i=0; i<heros.size;i++){
Hero h = heros.get(i);
sout(h);
}
迭代器遍历1
使用迭代器Iterator遍历集合中的元素 ,迭代器就像指针,最开始指在空位置
List<Hero> heros = new ArrayList<Hero>();
//加入对象
。。。
Iterator<Hero> it = heros.iterator();
//从最开始的位置判断"下一个"位置是否有数据
//如果有就通过next取出来,并且把指针向下移动
//直到"下一个"位置没有数据
while(it.hasNext()){
Hero h = it.next();
System.out.println(h);
}
迭代器遍历2
for (Iterator<Hero> iterator = heros.iterator(); iterator.hasNext();) {
Hero hero = (Hero) iterator.next();
System.out.println(hero);
}
迭代器遍历3(列表迭代器)
ListIterator lit = arrayList.listIterator();
while(lit.hasNext()){
Stdent s = (Student)lit.next();
System.out.println(s);
}
增强for遍历
List<Hero> heros = new ArrayList<Hero>();
for (Hero h : heros) {
System.out.println(h);
}
Vector(List实现类)
数组结构实现,查询快,增删慢
JDK1.0版本,运行效率慢,线程安全
常用函数
add
remove
elements() //使用枚举器遍历
while(en.hasMoreElements()){
Object o = en.nextElement();
sout(o);
}
LinkedList(List实现类)
链表结构实现,增删快,查询慢,无需开辟连续空间
LinkedList linkedList = new LinkedList<>();
方法
LinkedList也实现了List接口,有add,remove,contains等等方法。
源码分析
-
transient int size = 0; 初始大小
-
transient Node<E> first; 头结点
-
transient Node<E> last; 尾结点
-
构造方法
public LinkedList() { }
-
add方法 指向linkLast()方法 有Node节点(E是实际数据,next后节点,prev 前节点)
双向链表Deque
LinkedList还实现了双向链表结构Deque接口。
-
声明
LinkedList<Hero> ll =``new` `LinkedList<Hero>();
-
后插
ll.addLast(newHero("hero1"));
-
前插
ll.addFirst(new Hero("heroX"));
前取,后取,前查,后查
队列 - Queue
LinkedList 除了实现了List和Deque外,还实现了Queue接口(队列)。
offer 在最后添加元素 poll 取出第一个元素 peek 查看第一个元素
List ll =new LinkedList<Hero>();
Queue<Hero> q= new LinkedList<Hero>();
q.offer(new Hero("Hero1"));
Hero h = q.poll();
h=q.peek();
泛型
-
泛型定义
JDK1.5引入,本质是参数化类型,把类型作为参数传递
-
泛型形式
泛型类,泛型接口,泛型方法
-
语法
类名<T>
<T,...>类型 T称为类型占用符,表示一种引用类型,如果写多个用逗号隔开
K键
E值
-
好处
1.提高代码重用性
2.防止转换异常
不指定泛型的容器,可以存放任何类型的元素 指定了泛型的容器,只能存放指定类型的元素以及其子类
在取出数据的时候,不需要再进行转型了,因为里面肯定是放的Hero或者其子类
List<Hero> genericheros = new ArrayList<Hero>();
genericheros.add(new Hero("盖伦"));
genericheros.add(new APHero());
Hero h = genericheros.get(0);
-
泛型简写
基本写法
List<Hero> genericheros = new ArrayList<Hero>();
简写
List<Hero> genericheros2 = new ArrayList<>();
-
泛两种类
定义一个接口,两个类实现接口
List<LOL> lolList = new ArrayList<>();
lolList.add( new Hero("盖伦")); //能放Hero
lolList.add( new Item("血瓶")); //也能放Item
泛型类
-
声明出的对象里的任何属性,变量,方法都只能用某种引用类型
package Generic;
public class MyGeneric<T> {
//1.泛型使用1创建变量
T t;
//2.作为方法的参数
public void show(T t){
System.out.println(t);
}
//3.作为方法的返回值
public T getT(){
return t;
}
//注意泛型类里不可以实例化,因为无法保证对应的构造方法是否一定存在
}
调用类
package Generic;
public class TestGeneric {
public static void main(String[] args) {
//使用泛型类创建对象
//注意:1泛型只能使用引用类型2.不同泛型类型对象之间不能相互赋值
MyGeneric<String> myGeneric = new MyGeneric<>();
myGeneric.t = "hello";
myGeneric.show("大家好");
String string = myGeneric.getT();
//使用泛型创建Integer
MyGeneric<Integer> myGeneric1 = new MyGeneric<>();
myGeneric1.t = 100;
myGeneric1.show(200);
Integer integer = myGeneric1.getT();
}
}
泛型接口
-
创建接口
public interface MyInterface<T> {
String name = "张三";
//不能使用泛型创建静态常量,因为不能实例化对象T t = new T();
//创建函数
T server(T t);
}
-
实现接口
1.提前告知泛型类型
public class MyInterfaceImpl implements MyInterface<String>{
public String server(String t){
System.out.println(t);
return t;
}
}
2.实现接口不告知泛型类,在调用的时候指明类型(实现类也变成一个泛型类)
public class MyInterfaceImp2<T> implements MyInterface<T> {
public T server(T t){
System.out.println(t);
return t;
}
}
main方法里
MyInterfaceImpl2<Integer> impl2 = new MyInterfaceImp2<>();
impl2.server(1000);
泛型方法
-
定义
public class MyGenericMethod {
//泛型方法
public <T> void show(T t){
System.out.println(t);
T t2;
}
}
-
调用
//泛型方法调用
MyGenericMethod myGenericMethod = new MyGenericMethod();
myGenericMethod.show("中国加油");//方法的类型由传入的类型决定
myGenericMethod.show(100);
泛型好处
1.提高代码重复性:泛型方法
2.防止类型转换异常,提高代码安全性
泛型集合
-
概念:参数化类型,类型安全的集合,强制集合类型的元素必须一致
-
特点:编译时即可检查,而非运行时抛出异常
访问时,不必类型转换
不同泛型间不能相互赋值,泛型不存在多态
-
使用
1.集合不调用泛型,自动Object,虽然所有类型都可以加入arrarylist但无法判断输入类型
public class Demo {
public static void main(String[] args) {
ArrayList <String> arrayList = new ArrayList<String>();
arrayList.add("111");
arrayList.add("222");
}
}
Set集合
-
特点:无序,无下标,元素不可重复(只要地址不同就可以添加)
方法
继承自Collection方法
HashSet
-
特点
元素不能重复
基于HashCode计算元素存放位置
当存入元素的哈希码相同时,会调用equals进行确认,如结果是true,则拒绝后者存入(hashcode和equals方法如果值一样,无法存入)
-
存储结构
数组+链表+红黑树
-
使用
public class Demo1 {
public static void main(String[] args) {
HashSet<String> hashSet = new HashSet<>();
//添加元素
hashSet.add("刘德华");
System.out.println(hashSet.toString());
}
}
HashSet<String> names =new HashSet<String>();
names.add("gareen");
System.out.println(names);
//第二次插入同样的数据,是插不进去的,容器中只会保留一个
names.add("gareen");
System.out.println(names);
-
Set中的元素顺序,是不确定的
-
Set不提供get()来获取指定位置的元素 ,所以遍历需要用到迭代器,或者增强型for循环
//遍历Set可以采用迭代器iterator
for (Iterator<Integer> iterator = numbers.iterator(); iterator.hasNext();) {
Integer i = (Integer) iterator.next();
System.out.println(i);
}
//或者采用增强型for循环
for (Integer i : numbers) {
System.out.println(i);
}
-
HashSet和HashMap的关系
HashSet里封装了一个HashMap<E,Object>,实际上就是用HashSet的构造方法初始化这个HashMap
向HashSet中增加元素,其实就是把该元素作为key,增加到Map中
value是PRESENT,静态,final的对象,所有的HashSet都使用这么同一个对象
TreeSet
-
特点
基于排列顺序实现元素不重复
实现了SorttedSet接口,对集合元素自动排序
元素对象的类型必须实现Comparable接口,指定排序规则
通过compareTo方法确定是否为重复元素(若出现String类型,无法比较可重写该compareTo方法),方法返回0,认为是重复
-
存储结构
红黑树
比较器
-
Comparator接口(定制比较器)
Comparator给定如何进行两个对象之间的大小比较(定义对象的比较属性)
重写compare接口方法
-
Comparable接口
compareTo方法
Map集合
-
特点
1.用于存储键值对
2.键:无序,无下标,不允许重复
3.值:无序,无下标,允许重复
Map父接口
-
特点
1.用于存储键值对
2.键:无序,无下标,不允许重复
3.值:无序,无下标,允许重复
-
方法
put()//添加
-
使用
Map<String,String> map = new HashMap<>();
-
遍历
1.keySet方法
2.entrySet方法
HashMap(哈希表)
-
特点
JDK1.2,线程不安全,运行效率快,运行效率快,允许用null作为key或value
HashMap储存数据的方式是—— 键值对
key 》value 一个key只能指向一个value(重复指向会覆盖),一个value可被多个key指向
HashMap<String,Hero> heroMap = new HashMap<String,Hero>();
heroMap.put("gareen", new Hero("gareen1"));
System.out.println(heroMap);//输出所有键值对
HashTable
-
特点
JDK1.0,线程安全,运行效率慢,不允许null作为key或者value
TreeMap(红黑树)
-
实现类SortedMap接口(Map子接口),可以对key排序,红黑树实现
Collections工具类
-
概念:集合工具类,定义了除了存取以外的集合常用方法
-
方法:
reverse() //翻转元素
shuffle() //重制元素顺序
sort()//升序元素
补充
栈 - Stack
push 压栈
pull 出栈
peek 查看最后一个
LinkedList<Hero> heros = new LinkedList<Hero>();
public void push(Hero h) {
heros.addLast(h);
}
public Hero pull() {
return heros.removeLast();
}
public Hero peek() {
return heros.getLast();
}
二叉树
排序
根据左边放小,右边放大的顺序,创建节点
递归二叉树排序,定义Node类产生对象,add方法给对象赋值或比较
每次都从根节点开始比较
package er_chashu;
public class Node {
// 左子节点
public Node leftNode;
// 右子节点
public Node rightNode;
// 值
public Object value;
// 插入 数据
public void add(Object v) {
// 如果当前节点没有值,就把数据放在当前节点上
if (null == value)
value = v;
// 如果当前节点有值,就进行判断,新增的值与当前值的大小关系
else {
// 新增的值,比当前值小或者相同
if ((Integer) v -((Integer)value) <= 0) {
if (null == leftNode)
leftNode = new Node();
leftNode.add(v);
}
// 新增的值,比当前值大
else {
if (null == rightNode)
rightNode = new Node();
rightNode.add(v);
}
}
}
public static void main(String[] args) {
int randoms[] = new int[] { 67, 7, 30, 73, 10, 0, 78, 81, 10, 74 };
Node roots = new Node();
for (int number : randoms) {
roots.add(number);
}
}
}
遍历
整体来看很好理解,最外层小的放在左边(递归新的根节点),自己在中间,最外层大的放在右边(递归新的根节点)
// 中序遍历所有的节点
public List<Object> values() {
List<Object> values = new ArrayList<>();
// 左节点的遍历结果,左节点执行values方法,递归调用
if (null != leftNode)
values.addAll(leftNode.values());//最外层全部加入最左边
// 当前节点加入values,无左节点把自己加进去
values.add(value);
// 右节点的遍历结果
if (null != rightNode)
values.addAll(rightNode.values());//最外层全部加入最右边
//左右均无节点把自己返回
return values;
}
Collection类
Collection是 Set List Queue和 Deque的接口
-
注:Collection和Map之间没有关系,Collection是放一个一个对象的,Map 是放键值对的
-
注:Deque 继承 Queue,间接的继承了 Collection
Collections
Collections是一个类,容器的工具类,就如同Arrays是数组的工具类
提供一系列静态的public方法
类似Math类中的方法,直接调用即可
-
反转 reverse
List<Integer> numbers = new ArrayList<>();
for (int i = 0; i < 10; i++) {
numbers.add(i);
}
Collections.reverse(numbers);
-
混淆 shuffle
Collections.shuffle(numbers);`
-
排序sort
Collections.sort(numbers);
-
交换swap
Collections.swap(numbers,0,5);
-
滚动rotate
Collections.rotate(numbers,2);
-
线程安全化
List<Integer> numbers = new ArrayList<>();
System.out.println("把非线程安全的List转换为线程安全的List");
List<Integer> synchronizedNumbers = (List<Integer>) Collections.synchronizedList(numbers);
关系与区别
ArrayList与HashSet的区别
-
元素有无顺序
ArrayList: 有顺序 HashSet: 无顺序
-
元素能否重复
List中的数据value可以重复 Set中的数据value不能够重复
ArrayList和LinkedList的区别
ArrayList是数组:插入数据慢,定位快
LinkedList是双向链表:插入数据快,定位慢
HashMap和HahTable的异同
-
同:HashMap和Hashtable都实现了Map接口,都是键值对保存数据的方式
-
异:
区别1: HashMap可以存放 null Hashtable不能存放null 区别2: HashMap不是线程安全的类 Hashtable是线程安全的类
几种Set的比较
HashSet: 无序 LinkedHashSet: 按照插入顺序 TreeSet: 从小到大排序
HashSet<Integer> numberSet1 =new HashSet<Integer>();
LinkedHashSet<Integer> numberSet2 =new LinkedHashSet<Integer>();
TreeSet<Integer> numberSet3 =new TreeSet<Integer>();
hashcode原理
数组+链表:空间换时间
List查找低效率
HashMap查找高效
HashMap高效原因
-
概念:所有的对象,都有一个对应的hashcode(散列值)
-
保存数据:某一hashcode已有值,在这个hashcode创建链表,链式存储多个元素
-
查找数据:首先根据hashcode下标,到数组中进行定位,再比较元素名称
HashSet判断是否重复
因为HashSet没有自身的实现,而是里面封装了一个HashMap,所以本质上就是判断HashMap的key是否重复。
如果hashcode不一样,就是在不同的坑里,一定是不重复的 如果hashcode一样,就是在同一个坑里,还需要进行equals比较 如果equals一样,则是重复数据 如果equals不一样,则是不同数据。