目录
集合主要是两组(单列集合,双列集合), Collection接口有两个重要的子接口List Set,他们的实现子类都是单列集合.Map接口的实现子类是双列集合,存放KV对.
一集合概述
1. 集合的理解和好处
2. 集合框架体系
集合主要是两组(单列集合,双列集合), Collection接口有两个重要的子接口List Set,他们的实现子类都是单列集合.Map接口的实现子类是双列集合,存放KV对.
2.1. 单列集合
2.2 双列集合
二 Collection接口和常用方法
1. Collection接口实现类的特点
2.Arraylist的常见使用方法
//add 添加单个元素
ArrayList arrayList = new ArrayList();
arrayList.add("jack");
arrayList.add(10);
arrayList.add(true);
System.out.println(arrayList);
//delete删除元素
arrayList.remove(0); //删除第一个元素
arrayList.remove("jack"); //指定删除某个元素
System.out.println(arrayList.contains("jack")); //contains查找元素是否存在
arrayList.size(); //size:获取元素个数
arrayList.isEmpty(); //判断是否为空
arrayList.clear(); //清空,慎重使用!
ArrayList<Object> list = new ArrayList<>();
arrayList.addAll(new ArrayList<>()); //addAll(Collection)可以添加新集合
arrayList.containsAll(list); //查找多个元素是否都存在
list.removeAll(arrayList); // 删除多个元素
3.Collection接口的遍历方式
3.1 使用Iterator(迭代器)
迭代器的执行原理:
1. Iterator iterator = coll.iterator();可以得到一个集合的迭代器
2. hasNext()方法可以判断是否还有下一个元素
3. next()方法将指针下移并且将下移后集合位置上的元素返回
4. remove()方法将移除返回的元素
Note: 调用next()方法前必须调用hasNext()检测是否还有元素,否则有可能抛出异3
Iterator iterator = col.iterator();
while (iterator.hasNext()) { //快捷建 itit 一键生成
Object obj = iterator.next();
System.out.println("obj=" + obj);
}
3.2 增强for循环
for(Object book : col){
System.out.println("book=" + book);
}
Note: 增强for底层仍然是迭代器,可以理解为简化版的迭代器遍历;
也可以直接在数组中使用;
快捷键 I
3.3 使用普通for
for(int i = 0; i < list.size(); i++){
Object obj = list.get(i);
System.out.println(obj);
}
4. List接口和常用方法
1.特点
(1) List集合类中元素有序,且可重复;
(2) List集合类中的每个元素都有其对应的顺序索引,即支持索引索引从0开始;
(3) List集合类的实现类有很多,常用的有ArrayList, LinkedList和Vector.
2.常用方法
3.练习题
ArrayList<Object> list = new ArrayList<>();
list.addAll(Arrays.asList(0,1,2,3,4,5,6,7,8,9));
list.add(1,"韩顺平教育");
list.get(4);
list.remove(list.get(5));
list.set(6,"修改元素7");
Iterator<Object> iterator = list.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
}
排序练习
5. ArrayList底层结构和源码分析
1.注意事项
2.底层操作机制源码分析(重难点)
2.1结论
transisent 表示瞬间,短暂的 ,如果被transisent修饰,表示该属性不会被序列化
2.2源码分析
debugCode(强烈建议自己debug一下!!!)
ArrayList list = new ArrayList();
// ArrayList list2 = new ArrayList(8);
for (int i = 1; i <=10; i++){
list.add(i);
}
for (int i = 11; i <= 15; i++){
list.add(i);
}
list.add(100);
list.add(200);
list.add(null);
sourcecode分析:
(1) 无参构造
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//创建了一个空的elementData数组
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
//执行list.add (1)先确定是否要扩容 (2) 然后再执行赋值操作
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
//该方法确定minCapacity (1)第一次扩容为10
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//(1)modCount记录集合被修改的次数,防止多线程操作 (2)如果elementData大小不够,就调用grow()扩容
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
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);
}
//(1)真的扩容 (2)使用扩容机制来确定要扩大到多大 (3)第一次newCapacity=10 (4)以后按1.5倍扩容
//(5)扩容使用Arrays.copyOf()
(2)指定大小构造器
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
//创建了一个指定大小的elementData数组 this.elementData = new Object[capacity]
//如果是有参构造器,第一次扩容,就按照elementData的1.5倍扩容
6.Vector底层结构和源码分析
6.1 Vector和ArrayList的比较
6.2源码分析
Vector vector = new Vector();
//Vector vector = new Vector(8);
for (int i = 0; i < 10;i++){
vector.add(i);
}
vector.add(100);
System.out.println(vector);
//1.new Vector()底层
// public Vector() {
// this(10);
// }
//补充:如果是有参构造 如new Vector(8) ,走的方法就是
// public Vector(int initialCapacity) {
// this(initialCapacity, 0);
// }
//2.vector.add(i)
//2.1下面这个方法就是添加数据到集合
// public synchronized boolean add(E e) {
// modCount++;
// ensureCapacityHelper(elementCount + 1);
// elementData[elementCount++] = e;
// return true;
// }
//2.2 确定是否需要扩容
// private void ensureCapacityHelper(int minCapacity) {
// // overflow-conscious code
// if (minCapacity - elementData.length > 0)
// grow(minCapacity);
// }
//2.3如果需要的数组大小不够用,就扩容,扩容的算法
// private void grow(int minCapacity) {
// // overflow-conscious code
// int oldCapacity = elementData.length;
// int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
// capacityIncrement : oldCapacity);
// if (newCapacity - minCapacity < 0)
// newCapacity = minCapacity;
// if (newCapacity - MAX_ARRAY_SIZE > 0)
// newCapacity = hugeCapacity(minCapacity);
// elementData = Arrays.copyOf(elementData, newCapacity);
// }
7.LinkedList底层结构
7.1 理解双向链表
//模拟一个双向链表(这里省略了Node类的定义)
Node jack = new Node("jack");
Node tom = new Node("tom");
Node hsp = new Node("老韩");
//连接三个结点,形成双向链表
jack.next = tom;
tom.next = hsp;
hsp.pre = tom;
tom.pre = jack;
Node first = jack;//让first引用指向jack,就是双向链表的头结点
Node last = hsp; //让last引用指向hsp,就是双向链表的尾结点
//演示链表的添加对象/数据 是多么方便
//要求,是在tom和hsp之间插入一个对象 zhangfei
Node zhangfei = new Node("张飞");
tom.next = zhangfei;
hsp.pre = zhangfei;
zhangfei.next = hsp;
zhangfei.pre = tom;
//演示,从头到尾进行遍历
while (true) {
if (first == null){
break;
}
//输出first 信息
System.out.println(first);
first = first.next;
}
7.2 LinkedList常用方法
LinkedList linkedList = new LinkedList();
linkedList.add(1);
linkedList.add(2);
linkedList.add(3);
System.out.println(linkedList);
linkedList.remove();//共三种方法可以remove: remove() 默认删除第一个;remove(Object)删除指定对象;remove(index)删除索引所在位置的对象
System.out.println(linkedList);
//修改某个结点对象
linkedList.set(1,99);
//得到某个结点对象
//get(1)是得到双向链表的第二个对象
Object o = linkedList.getFirst();
//因为linkedlist实现了list接口,遍历方式
Iterator iterator = linkedList.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
for(Object i : linkedList){
System.out.println(i);
}
for (int i = 0; i < linkedList.size(); i++) {
System.out.println(linkedList.get(i));
}
7.3 LinkedList底层结构与源码分析
//源码阅读:添加
/*
1. LinkedList linkedList = new LinkedList();
public LinkedList() {}
2. 这时linkedlist的属性first = null last = null
3. 执行
public boolean add(E e) {
linkLast(e);
return true;
}
4.将新的结点加入到双向链表的最后
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
*/
/*
源码阅读:remove()
1.执行 removeFirst(
public E remove() {
return removeFirst();
}
2.执行
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
3.执行 unlinkFirst(f),将f指向的双向链表的第一个结点拿掉
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
*/
8.List 集合选择
8.1 ArrayList和LinkedList比较
5.Set接口和常用方法
1. Set特点:
(1) Set下有两个实现类,分别为HashSet和TreeSet
(2) 无序(添加的顺序和取出的顺序不一致,但取出的顺序是固定的)
(3) 无索引,所以无法用索引方式遍历
(4) 不允许重复元素,所以最多包含一个null
(5) 常用方法和遍历方法和Collection集合一样(无法用普通索引方法遍历)
2. Set接口实现类-HashSet
(1)特点:
HashSet.add(Object)方法会返回一个boolean值,成功返回true,失败返回false
HashSet.remove(Object)可以指定删除的元素
思考: 这里为什么两个new String 只能加入一个?-->看HashSet底层机制
(2)HashSet底层机制
底层机制说明:
这里的12不仅指12个数组元素,也可以表示单条链表上的元素个数到达12
练习:
Note: 重写hashcode()和equals()
Note: 重写MyDate类的hashcode()和equals()以及Employee类name的hashcode()和equals()
3 Set接口实现类-LinkedHashSet
(1) LinkedHashSet是HashSet的子类;
(2) LinkedHashSet底层是一个LinkedHashMap,底层维护了一个数组+双向链表;
(3) LinkedHashSet根据元素的hashcode值来决定元素的存储位置,同时使用链表维护元素的次序,是元素看起来以插入顺序保存;
(4) LinkedHashSet不允许添加重复元素.
练习:
Note: 重写Car类的hashcode()和equals()即可
6. Map接口和常用方法
6.1 Map接口实现类的特点
Node<k,v> --> entry<k,v> --> entrySet<entry>
6.2 Map接口的常用方法
(1) put: 添加
(2) remove: 根据键删除映射关系
(3) get: 根据键获取值
(4) size: 获取元素个数
(5) isEmpty: 判断个数是否为0
(6) clear: 清除
(7) containsKey: 查找键是否存在
6.3 Map接口的六大遍历方式
//1.keySet
// (1)增强for
for (Object key : map.keySet()){
System.out.println(key + "-" + map.get(key));
}
// (2)迭代器
Iterator iterator = map.keySet().iterator();
while (iterator.hasNext()) {//快捷键itit
Object key = iterator.next();
System.out.println(key + "-" + map.get(key));
}
//2.把所有的values取出
Collection values = map.values();
//这里可以使用所有collections使用的遍历方法
//(1)增强for
for (Object value:
values) {
System.out.println(value);
}
//(2)迭代器
Iterator iterator1 = values.iterator();
while (iterator1.hasNext()) {
Object next = iterator1.next();
System.out.println(next);
}
//3.通过entrySet来获取k-v
//(1)增强for
Set entrySet = map.entrySet();
for (Object o:
entrySet) {
//将entry转成Map.Entry
Map.Entry entry = (Map.Entry) o;
System.out.println(entry.getKey() + "-" + entry.getValue());
}
//(2)迭代器
Iterator iterator2 = entrySet.iterator();
while (iterator2.hasNext()) {
Object next = iterator1.next();
Map.Entry entry = (Map.Entry) next;
System.out.println(entry.getKey() + "-" + entry.getValue());
}
6.4 Map接口课堂练习
HashMap<Object, Object> map = new HashMap<>();
Employee1 tom = new Employee1("TOM", 24000, 234);
Employee1 kate = new Employee1("KATE", 23000, 357);
Employee1 chris = new Employee1("CHRIS", 10000, 123);
map.put(tom.getId(),tom);
map.put(kate.getId(),kate);
map.put(chris.getId(),chris);
//1.keySet
for (Object o : map.keySet()) {
Employee1 e = (Employee1) map.get(o);
if (e.getSal() > 18000){
System.out.println(e.getName());
}
}
//2.entrySet
for(Object o : map.entrySet()){
Map.Entry entry = (Map.Entry) o;
Employee1 e1 = (Employee1) entry.getValue();
if (e1.getSal() > 18000){
System.out.println(e1.getName());
}
}
6.5 HashMap小结
6.6 HashMap底层机制及源码解读
6.7 Map接口实现类-Hashtable
(1)存放的元素是键值对;
(2)键和值都不能为null;
(3)使用方法基本上和HashMap一样;
(4)Hashtable线程安全,但hashMap是线程不安全的;
底层分析:
1.底层有数组 Hashtable$Entry[] 初始化大小为11
2.threshold 8 = 11 * 0.75
3.扩容:按照自己的扩容机制进行 ,执行addEntry(...)方法
4.当count >= threshold时,进行扩容, 新容量为原先容量*2 +1
6.8 Map接口实现类-Properties
结构图:
分析:
1.Properties继承Hashtable
2.可通过kv存放数据,k和v都不能为null
3.方法:
put(k,v) 增加
get(k) 通过k获取v
remove(k) 通过k删除对应v
put(k,v) 修改kv
Note:对于用Properties去读取配置的详细内容可参照IO流笔记
七 总结-开发中如何选择集合实现类
八 TreeSet源码解读
结构图:
构造器:
1.当我们使用无参构造器创建TreeSet时,仍然是无序的
2.如果想要添加的元素按照一定规则排序,可以使用有参构造,传入比较器(匿名内部类)
源码解读:
九 TreeMap源码解读
结构图:
源码大致同TreeSet
十 Collections工具类
ArrayList list = new ArrayList();
list.add("tom");
list.add("smith");
list.add("king");
list.add("milan");
//reverse(list) 反转
Collections.reverse(list);
System.out.println(list);
//shuffle(list)对list元素进行随机排序
Collections.shuffle(list);
System.out.println(list);
//sort(list)按照元素的自然顺序对list元素按升序排序
Collections.sort(list);
//sort(list,comparator)希望按照指定的Comparator对list的元素进行排序
Collections.sort(list, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
//可以加入校验代码
return ((String)o1).length() - ((String)o2).length();
}
});
//
System.out.println(list);
//swap(list,int,int) 将指定list集合中的i处元素和j处元素进行交换
Collections.swap(list,0,1);
System.out.println(list);
//Object max(Collection) 根据元素的自然顺序,返回给定集合中的最大元素
System.out.println("自然顺序最大元素:" + Collections.max(list));
//Object max(Collection,Comparator) 根据自定义的比较器,返回给定集合中的最大元素
System.out.println("指定比较器最大元素:" + Collections.max(list, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return ((String)o1).length() - ((String)o2).length();
}
}));
//Object min(Collection)
//Object min(Collection,Comparator) 这两个方法参照max即可
//int frequency(Collection,Object) 返回指定集合中指定元素出现的次数
System.out.println("tom出现的次数:" + Collections.frequency(list,"tom"));
//Void copy(list dest, list src)将src的内容复制到dest中 //为了完整拷贝,需要先给dest赋值,大小和list.size()一样
//boolean replaceAll(List list, Object oldVal, Object newVal) 使用新值替换list对象的所有旧值
System.out.println(Collections.replaceAll(list,"tom","汤姆"));
System.out.println(list);
十一 练习题
ArrayList list = new ArrayList();
News news1 = new News("新冠确诊病例超千万,数百万印度教信徒赴恒河\"圣浴\"引民众担忧");
News news2 = new News("男子突然想起2个月前钓的鱼还在网兜里,捞起一看赶紧放生");
list.add(news1);
list.add(news2);
//倒序遍历
for (int i = list.size() - 1; i >= 0; i--){
News news = (News) list.get(i);
news.setTitle(news.getTitle().substring(0, 15) + "...");
System.out.println(list.get(i));
}
HashMap m = new HashMap();
m.put("jack", 650);
m.put("tom", 1200);
m.put("smith", 2900);
m.put("jack", 2600);
for (Object obj : m.entrySet()){
Map.Entry entry = (Map.Entry) obj;
Object value = entry.getValue();
Integer value2 = (Integer)value + 100;
entry.setValue(value2);
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
(1)HashSet的去重机制: hashCode()+equals(),底层先通过存入对象,进行运算得到hash值,通过hash值得到对应的索引,如果发现table索引所在的位置没有数据,就直接存放,如果有数据,就进行equals比较[遍历比较],如果比较后不相同就加入,否则不加入
(2)TreeSet的去重机制: 如果传入了一个Comparator匿名对象,就使用实现的compare去重,如果方法返回0,就认为是相同的元素/数据,就不添加,如果没有传入Comparator,则以你添加的对象实现的Comparator对象的compareto方法实现去重
由于p1.name的值更改,所以无法定义索引位置去remove,则remove不成功,仍旧输出两个元素; 而因为索引不同,则(1001,"cc")可以添加成功,此时输出三个对象;(1001,"AA")会挂在原先索引对象的后面,形成链表