回顾:
集合
Collection 定义集合能干什么
int size();
boolean empty();
boolean add(E e);
boolean remove(E e);
List:有序可重复的集合
有序:有下标(每个添加的元素都有一个序号,序号从0开始,每次自增1)
int size();
boolean empty();
boolean add(E e);
boolean remove(E e);
E get(int index);
int indexOf(Object obj);
- ArrayList
- 底层:数组
- 默认长度:10
- 数组什么扩容:长度不够的时候扩容
- 扩容到多少:原来的1.5倍
- LinkedList
- Vecto
Set:无序不可重复
一. List
List是有序可重复的集合
- 有序:有序号
- 可重复:值可以相等
1. ArrayList
- 底层:数组
- 默认长度:10
- 数组什么扩容:长度不够的时候扩容
- 扩容到多少:原来的1.5倍
2. LinkedList
- 底层:链表 (双向链表)
- 链表上的节点就是一个Node对象
- 值
- 上个节点
- 下一个节点
public class LinkedList<E> extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
//元素个数
transient int size = 0;
//第一元素 为查询获取元素值
transient Node<E> first;
//最后一个元素 为方便添加元素
transient Node<E> last;
public LinkedList() {
}
//添加元素
public boolean add(E e) {
linkLast(e);
return true;
}
//添加最后一个元素
void linkLast(E e) {
//获取最后一个节点
final Node<E> l = last;
/*
*创建一个节点
1. 把最后一个节点设置为当前节点的上一个节点
2. 把要添加的值赋值当前节点的item属性
3. 把后面一个节点设置为null
*/
final Node<E> newNode = new Node<>(l, e, null);
//把当前节点设置为最后一个节点
last = newNode;
//判断是否是第一次添加
if (l == null) {
first = newNode; // 如果是第一次添加,当前节点也是第一个节点
} else {
l.next = newNode; //把原来的最后一个节点的下一个节点设置为新添加的节点
}
size++; //元素个数添加1
modCount++;
}
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
Node<E> node(int 2) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < 2; i++) {
x = x.next;
}
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
/*
*私有静态内部类
1. private : 该类只能被所在类使用
2. static : 可以定义静态属性
*/
//Node 就是我们链表上一个的节点(环) 链表就是由一个一个Node对象组成
private static class Node<E> {
//该节点(环)上保存的值
E item;
//下一个节点(环)
Node<E> next;
//上一个节点(环)
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
}
3. Vector
Vector的底层和ArrayList一样都是数组
- 默认长度也为10
- 所有的方法都加了synchronized关键字(锁)
- 表示所有的方法都是线程安全的
4. 比较ArrayList,LinkedList,Vector
-
ArrayList底层是数组,查询速度快,插入和删除的速度的慢
-
LinkedList底层是链表,插入和删除的速度的快,查询慢点
-
Vector底层是数组,它是线程安全的,查询速度快,但是效率相对ArrayList还是差一点
二. Set
Set是无序不可重复的集合
-
是Collection的子接口
-
无序 : 没有序号
-
不可重复 : 可以通过重写equals和hashCode方法定义两个值的比较逻辑
public interface Set<E> extends Collection<E> {
int size();
boolean isEmpty();
boolean contains(Object o);
Iterator<E> iterator();
Object[] toArray();
boolean add(E e);
boolean remove(Object o);
void clear();
int hashCode();
}
package com.qf;
import java.util.HashSet;
import java.util.Set;
public class Demo02 {
public static void main(String[] args) {
Set<String> set01 = new HashSet<>();
set01.add("jackma");
set01.add("pony");
set01.add("tomlei");
set01.add("jackma");
int size = set01.size();
System.out.println(size);
}
}
1. HashSet
HashSet是Set的一个实现类
- 底层就是HashMap
- 保存值就是保存在一个键值对中的键
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
//HashSet的底层就是一个HashMap
private transient HashMap<E,Object> map;
//HashSet构造方法
public HashSet() {
//创建一个HashSet就是创建一个HashMap对象
map = new HashMap<>();
}
/*
* 当向HashSet中添加元素时,就是往map添加一个键值对,把值赋值给键
* HashMap的键是不可以重复的,且判断重复的规则和HashSet一致
*/
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
}
package com.qf;
import java.util.HashSet;
import java.util.Set;
public class Demo02 {
public static void main(String[] args) {
//创建保存学生的集合
Set<Student> set02 = new HashSet<Student>();
Student stu01 = new Student();
stu01.setName("jackma");
stu01.setCardId("666");
Student stu02 = new Student();
stu02.setName("pony");
stu02.setCardId("777");
Student stu03 = new Student();
stu03.setName("jackma2");
stu03.setCardId("666");
/*
* 如果想让两个对象重复
* 一定要重写equals和hashCode方法
*
* 如果添加了重复的数据,不是后面的覆盖前面的,而是后面的没有添加成功
*/
System.out.println(stu03.equals(stu01));
// System.out.println(stu03==stu01);
set02.add(stu01);
set02.add(stu02);
set02.add(stu03);
System.out.println(set02.size());
for(Student stu : set02) {
System.out.println(stu.toString()); //com.qf.Student@d576
}
/*
* 定义电脑
* 品牌 价格 编号
* 使用set集合保存多个电脑
* 如果两个电脑的编号相等,那么就是重复的数据
*/
}
}
//表示学生类
class Student {
private String name; //姓名
private String cardId; //身份证号码
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCardId() {
return cardId;
}
public void setCardId(String cardId) {
this.cardId = cardId;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((cardId == null) ? 0 : cardId.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (cardId == null) {
if (other.cardId != null)
return false;
} else if (!cardId.equals(other.cardId))
return false;
return true;
}
@Override
public String toString() {
return "Student [name=" + name + ", cardId=" + cardId + "]";
}
}
2. LinkedHashSet
LinkedHashSet是Set的一个实现类,会记录下保存数据的顺序
package com.qf;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
public class Demo02_2 {
public static void main(String[] args) {
/*
* Set集合
* 1. HashSet 不会记录顺序,但是顺序一旦确定了,也是不会改变
* 2. LinkedHashSet 记录下元素添加的顺序
*/
// Set<Student> set = new HashSet<Student>();
Set<Student> set = new LinkedHashSet<Student>();
set.add(new Student("tomlei","777"));
set.add(new Student("pony","888"));
set.add(new Student("jackma","666"));
for(Student stu : set) {
System.out.println(stu.toString());
}
}
}
3. TreeSet
TreeSet会自动为元素进行排序
package com.qf;
import java.util.Set;
import java.util.TreeSet;
public class Demo02_3 {
public static void main(String[] args) {
Set<Dog> set = new TreeSet<Dog>();
set.add(new Dog("阿黄", 3));
set.add(new Dog("旺财", 5)); // 3-4-5
set.add(new Dog("小强", 4));
set.add(new Dog("大强", 4));
for(Dog d : set) {
System.out.println(d);
}
System.out.println("-----------------------------------------");
/*
* 定义一个考生类
* 姓名,性别,成绩
* 第一个集合保存多个考生的信息(对象),按照成绩倒叙打印考生信息
*/
Set<Examinee> set02 = new TreeSet<Examinee>();
set02.add(new Examinee("jackma", "男", 100));// 100 99 98
set02.add(new Examinee("pony", "男", 100)); //98-99=-1
set02.add(new Examinee("tomlei", "男", 100));
/*
* Examinee [name=pony, gedner=男, score=98]
Examinee [name=tomlei, gedner=男, score=99]
Examinee [name=jackma, gedner=男, score=100]
*/
for(Examinee e : set02) {
System.out.println(e);
}
}
}
class Examinee implements Comparable<Examinee>{
private String name;
private String gedner;
private Integer score;
public Examinee() {
}
public Examinee(String name, String gedner, Integer score) {
super();
this.name = name;
this.gedner = gedner;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGedner() {
return gedner;
}
public void setGedner(String gedner) {
this.gedner = gedner;
}
public Integer getScore() {
return score;
}
public void setScore(Integer score) {
this.score = score;
}
@Override
public int compareTo(Examinee o) {
return o.getScore() - this.score;
}
@Override
public String toString() {
return "Examinee [name=" + name + ", gedner=" + gedner + ", score=" + score + "]";
}
}
/*
* Dog类实现Comparable接口
* 表示狗和狗对象之间是可以比较的
* 在compareTo方法中定义定义比较的规则
* - 0 : 表示两个对象相等
* - 正数: 前面的大于后面的
* - 负数: 前面的小于后面的
*
*/
class Dog implements Comparable<Dog>{
String name;
Integer code;
public Dog() {
}
public Dog(String name, Integer code) {
super();
this.name = name;
this.code = code;
}
@Override
public String toString() {
return "Dog [name=" + name + ", code=" + code + "]";
}
@Override
public int compareTo(Dog o) {
/*- 0 : 表示两个对象相等
*- 正数: 前面的大于后面的
*- 负数: 前面的小于后面的
*/
return this.code - o.code;
}
}
三. Map
Map也是一个集合
在java中用的最多就三种集合 List,Set,Map
- List,Set是Collection的子集合
- Map是独立的集合
Map是一种键值对的集合
- Key
- Value
public interface Map<K,V> {
int size();
boolean isEmpty();
boolean containsKey(Object key);
boolean containsValue(Object value);
//保存数据 value表示保存的值, key表示保存的值的名字
V put(K key, V value);
//通过key值获取value值
V get(Object key);
//根据key值删除键值对
V remove(Object key);
void putAll(Map<? extends K, ? extends V> m);
void clear();
//获取Map所有的Key值,key是不能重复的
Set<K> keySet();
//获取Map所有的Value值,Value是可以重复的
Collection<V> values();
Set<Map.Entry<K, V>> entrySet();
}
//键值对接口 我们保存在Map中的每一个键值对(k-v)
interface Entry<K,V> {
K getKey();
V getValue();
}
package com.qf;
import java.util.HashMap;
import java.util.Map;
public class Demo03 {
public static void main(String[] args) {
/*
* String : 键的泛型,表示取的名字的数据类型
* String : 值的泛型,表示存的值的数据类型
*/
Map<String, String> map = new HashMap<String, String>();
//put添加数据,为每个值,定义一个名字
map.put("jackma", "小云");
map.put("pony", "小腾");
map.put("tomlei", "小军");
//根据起的名字,获取值
String v1 = map.get("jackma");
System.out.println(v1);
/*
* 定义一个保存Student对象的Map集合,名字(key)为该学生对象的姓名
*
* 保存三个对象
*
* 根据名字获取对象值
*/
}
}
package com.qf;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class Demo03_2 {
public static void main(String[] args) {
/*
* 定义一个集合,保存班级学生的姓名,以及其成绩
*/
Map<String, Double> map = new HashMap<String, Double>();
//添加数据
map.put("jackma", 99.8);
map.put("pony", 100.0);
map.put("tomlei", 99.8);
/*
* 1. 在map中key值是不能重复的
* 1.1 和HashSet中判断值是否相等,是一致的
* - hashCode相等,
* - equals为true
* 1.2 当key值重复时,后面的值会覆盖前面的值
* 2. value值是可以相等
* 3. key,value值都可以为null
*/
map.put("tomlei", 99.0);
map.put(null, null);
//根据key值删除键值对
map.remove(null);
//获取map中有多少个键值对
int size = map.size();
System.out.println(size);
//通过key获取value值
double score = map.get("tomlei");
System.out.println(score);
//获取map中所有的键值
Set<String> keySet = map.keySet();
//遍历key,在通过key获取所有的value
for(String key : keySet) {
System.out.println(key+":"+map.get(key));
}
System.out.println("----------------------------------");
/*
* 获取map中所有键值对 对象
* Entry<String, Double> : 每一个键值对都是一个Entry的对象
*/
Set<Entry<String, Double>> entrySet = map.entrySet();
for(Entry<String, Double> e : entrySet) {
String key = e.getKey();
Double value = e.getValue();
System.out.println(key + ":"+ value);
}
/*
* 创建一个Map对象
* 1. Key值为考生
* 2. Value值为成绩
* 3. 如果两个考生姓名相同,那么他们就相等
*
* 测试
*/
}
}
1. HashMap
HashMap是Map的一个实现类
- 底层:数组加链表(Node,单向链表)
- Node中有五个属性 hash,key,value,next
- 数目默认长度为16
- 数组的扩容时机:当容量达到扩容因子定义的个数时,扩容
- 默认扩容因子0.75 16*0.75=12 数组中位置有12个已经被占用,就被扩容
- 数组扩容后的大小:扩容为原来的两倍
- 16 12
- 32 24
- 64 48
- Node对象的保存
- 当我们添加一个键值对时,会创建一个Node对象
- key
- value
- hash
- 首先通过key获取他的hash值
- hash值能够确定该对象保存在数组中位置
- 如果该位置没有值,就把该对象直接放在该位置
- 如果该位置有值,那么就该对象放在原对象后面(next属性)
- 数组中的一个元素存放节点的个数小于8时,存放的形式是二叉树
- 数组中的一个元素存放节点的个数达到8个时,二叉树转化为红黑树
- 当我们添加一个键值对时,会创建一个Node对象
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
//默认容量 0001 0000
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 16
//最大容量 2^30
static final int MAXIMUM_CAPACITY = 1 << 30;
//加载因子,定义扩容的时机
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//树化阈值(临界值)
static final int TREEIFY_THRESHOLD = 8;
//反向 树化阈值
static final int UNTREEIFY_THRESHOLD = 6;
//最小树化容量
static final int MIN_TREEIFY_CAPACITY = 64;
//保存数据的Node数组,我们保存的每一个键值对会保存在一个Node对象中,
//Node对象又会保存在一个数组中
//HashMap的底层是 数组+链表
transient Node<K,V>[] table;
//个数
transient int size;
//
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
/*
hash: key的hash值
key: key值
value:值
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; //node数组
Node<K,V> p; //node对象
int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
//设置数组的容量
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
}
//HashMap中保存数据的节点
static class Node<K,V> implements Map.Entry<K,V> {
//key的hash值
final int hash;
//k值
final K key;
//value值
V value;
//下一个节点
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
public final K getKey() { return key; }
public final V getValue() { return value; }
public final String toString() { return key + "=" + value; }
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
public final boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
}