文章目录
一.类图
collection
上图中的Iterator接口在1.5之后就由Iterable接口代替了,Iterable接口封装了一个方法叫做iterator,该方法返回一个iterator对象,而每个iterator接口的实现类都分别写在了各个集合类的实现类中,以内部类方式实现iterator接口
用ArrayList举个例子:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
//实现了iterator接口的内部类
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
}
//实现了Iterable接口中的iteartor方法
public Iterator<E> iterator() {
return new Itr();
}
}
map
二.Collection接口
1.概述
该接口是所有单元素存储的总接口(List接口和Set接口的父接口),那个Iterable可以理解为Collection的一个附属接口,虽然它是Collection的父接口,但是整个集合的主要功能定义都是在Collection中定义的,Iterable这个接口就提供了个iterator()方法供所有实现类获取迭代器.所以Collection是核心接口
接下来看看它提供的方法:
2.Collection接口中的方法
方法 | 解释 |
---|---|
boolean add(Object o) | 该方法用于向集合里添加一个元素,添加成功返回true |
void clear() | 清除集合里的所有元素,将集合长度变为0 |
boolean contains(Object o) | 返回集合里是否包含指定元素 |
boolean containsAll(Collection c) | 返回集合里是否包含集合c里的所有元素 |
int hashCode() | 返回此collection的哈希码值 |
boolean isEmpty() | 返回集合是否为空,当集合长度为0时,返回true |
Iterator iterator() | 返回一个Iterator对象,用于遍历集合里的元素 |
boolean remove(Object o) | 删除集合中指定元素o,当集合中包含了一个或多个元素o时,这些元素将被删除,该方法将返回true |
boolean removeAll(Collection c) | 从集合中删除集合c里包含的所有元素,如果删除了一个或一个以上的元素,返回true |
boolean retainAll(Collection c) | 从集合中删除不在集合c里包含的元素,如果删除了一个或一个以上的元素,返回true |
int size() | 返回集合中的元素个数 |
Object[] toArray() | 该方法把集合转换成一个数组,所有集合元素变成对应的数组元素 |
三.List接口及其实现类
1.概述
List是有序可重复的,底层的数据结构是线性结构
2.方法
List不单单继承了Collection的方法,还增加了一些新的方法。
方法 | 解释 |
---|---|
void add(int index, Object element) | 将元素element插入到List的index处 |
boolean addAll(int index, Collection c) | 将集合c所包含的所有元素都插入在List集合的index处 |
Object get(int index) | 返回集合index处的元素 |
int indexOf(Object o) | 返回对象o在List集合中出现的位置索引 |
int lastIndexOf(Object o) | 返回对象o在List集合中最后一次出现的位置索引 |
Object remove(int index) | 删除并返回index索引处的元素 |
Object set(int index, Object element) | 将index索引处的元素替换成element对象,返回新元素 |
3.实现类
ArrayList : 底层数据结构是数组,查询和修改效率高,删除添加效率低
LinkedList : 底层数据结构是双向链表,删除添加效率高,查询和修改效率低
Vector : 类似于ArrayList,它是线程安全的,但是现在一般线程安全问题都另外处理了,所以这个也就被弃用了
Stack: 继承Vector实现的栈,栈结构是先进后出,但已被LinkedList取代
4.LinkedList新增方法
方法 | 解释 |
---|---|
void addFirst(Object e) | 将指定元素插入该集合的开头 |
void addLast(Object e) | 将指定元素插入该集合结尾 |
boolean offerFirst(Object e) | 将指定元素插入该集合的开头,成功返回true |
boolean offerLast(Object e) | 将指定元素插入该集合结尾 |
boolean offer(Object e) | 将指定元素插入该集合结尾 |
Object getFirst() | 获取,但不删除集合第第一个元素 |
Object getLast() | 获取,但不删除集合最后一个元素 |
Object peekFirst() | 获取,但不删除该集合第一个元素,如果集合为空,则返回null |
Object peekLast() | 获取,但不删除该集合最后一个元素,如果集合为空,则返回null |
Object pollFirst() | 获取,并删除该集合第一个元素,如果集合为空,则返回null |
Object pollLast() | 获取,并删除该集合最后一个元素,如果集合为空,则返回null |
Object removeFirst() | 获取,并删除该集合的第一个元素 |
Object removeLast() | 获取,并删除该集合的最后一个元素 |
Object pop() | pop出该集合的第一个元素 |
void push(Object e) | 将一个元素push到集合 |
四.Set接口及其实现类
1.概述
Set是无序不可重复的,其中无序是指添加的顺序和取出来的顺序无序,存储的不是线性结构
2.方法
方法 | 解释 |
---|---|
boolean add(E e) | 如果此set中尚未包含指定元素,则添加指定元素 |
boolean isEmpty() | 如果此set不包含任何元素,则返回true |
boolean contains(Object o) | 如果此set包含指定元素,则返回 true |
boolean remove(Object o) | 如果指定元素存在于此set中,则将其移除 |
int size() | 返回此set中的元素的数量 |
void clear() | 从此set中移除所有元素 |
3.实现类
HashSet :
它的底层是由HashMap实现的,这个map是一个仅有key的map(value是一个常量对象,用来占位,没有什么意义)
当这个key重复的时候,add方法不会添加这个重复的元素
TreeSet
它的底层是由TreeMap实现的,可排序,默认自然升序.
五.Collections工具类和Comparable、Comparator比较器
1.Collections工具类
Collections是一个包装工具类。它包含有各种有关集合操作的静态多态方法,此类不能实例化,服务于Java的Collection接口。
常用方法:
sort、reverse、fill、copy、max、min、swap等
重点: sort排序
public static <T extends Comparable<? super T>> void sort(List<T> list)
这个方法的意思就是sort传入的是一个List接口下的实现类对象,而且这个泛型还要求要比较的元素必须是实现了Comparable这个接口或者是它的子类
- 根据元素的自然顺序 对指定列表按升序进行排序。列表中的所有元素都必须实现 Comparable 接口。
- 此外,列表中的所有元素都必须是可相互比较的(也就是说,对于列表中的任何 e1 和 e2 元素,e1.compareTo(e2) 不得抛出 ClassCastException)。
2.Comparable、Comparator比较器
对象排序,就是比较大小,要实现Comparable或Comparator比较器之一,才有资格做比较排序。
【介绍】
- Comparable:与对象紧相关的比较器,可以称“第一方比较器”。
- Comparator:此为与具体类无关的第三方比较器。
例子:
-
定义一个学生类,实现Comparable接口,使用学号进行Collections.sort排序
-
定义一个学生类,实现Comparator接口,使用学号进行Collections.sort排序
学生类
package test06;
public class Student implements Comparable<Student>{
private String name;
private String sex;
private int age;
private int stuNo;
@Override
public int compareTo(Student o) {
// TODO Auto-generated method stub
//按照学号升序排序
// return this.stuNo-o.stuNo;
//降序
// return o.stuNo-this.stuNo;
//多条件排序(首先按照年龄升序,然后按照学号降序)
// if(this.age-o.age==0){
// return o.stuNo-this.stuNo;
// }
// return this.age-o.age;
//按照姓名排序
return this.name.compareTo(o.name);
}
public Student() {
super();
}
public Student(String name, String sex, int age, int stuNo) {
super();
this.name = name;
this.sex = sex;
this.age = age;
this.stuNo = stuNo;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getStuNo() {
return stuNo;
}
public void setStuNo(int stuNo) {
this.stuNo = stuNo;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Student [name=" + name + ", sex=" + sex + ", age=" + age
+ ", stuNo=" + stuNo + "]";
}
}
测试类:
public class TestComparable {
public static void main(String[] args) {
//创建四个学生
Student s1 = new Student("tom","男",20,1807001);
Student s2 = new Student("jack","男",20,1807022);
Student s3 = new Student("jeny","女",22,1807011);
Student s4 = new Student("lady","女",21,1807031);
List<Student> stuList = new ArrayList<Student>();
stuList.add(s1);
stuList.add(s2);
stuList.add(s3);
stuList.add(s4);
System.out.println("排序前=====");
for(Student s:stuList){
System.out.println(s);
}
Collections.sort(stuList);
System.out.println("排序后=====");
for(Student s:stuList){
System.out.println(s);
}
}
}
输出结果:
排序前=====
Student [name=tom, sex=男, age=20, stuNo=1807001]
Student [name=jack, sex=男, age=20, stuNo=1807022]
Student [name=jeny, sex=女, age=22, stuNo=1807011]
Student [name=lady, sex=女, age=21, stuNo=1807031]
排序后=====
Student [name=jack, sex=男, age=20, stuNo=1807022]
Student [name=jeny, sex=女, age=22, stuNo=1807011]
Student [name=lady, sex=女, age=21, stuNo=1807031]
Student [name=tom, sex=男, age=20, stuNo=1807001]
六.Map接口
1.概述
Map用于保存具有映射关系的数据,因此Map集合里保存两组值。
-
一组值用于保存key,一组值用于保存value
-
key~value之间存在单向一对一关系,通过指定key可以找到唯一的value值
-
key和value都可以是任何引用类型对象
-
允许存在value为null,但是只允许存在一个key为null
2.常用方法
方法 | 解释 |
---|---|
V put(K key, V value) | 将指定的值与此映射中的指定键关联 |
boolean containsKey(Object key) | 如果此映射包含指定键的映射关系,则返回true |
boolean containsValue(Object value) | 如果此映射将一个或多个键映射到指定值,则返回true |
boolean isEmpty() | 如果此映射未包含键-值映射关系,则返回true |
V get(Object key) | 返回指定键所映射的值,如果此映射不包含该键的映射关系,则返回null |
Set keySet() | 返回此映射中包含的键的set集合 |
Collection values() | 返回此映射中包含的值的Collection集合 |
Set<Map.Entry<K,V>> entrySet() | 返回此映射中包含的映射关系的set集合 |
boolean equals(Object o) | 返回指定的对象与此映射是否相等 |
int hashCode() | 返回此映射的哈希码值 |
V remove(Object key) | 如果存在一个键的映射关系,则将其从此映射中移除 |
七.HashMap类
1.概述
特点:key无序不可重复,底层是哈希表
2.哈希表实现原理
-
HashMap实际上是一个"链表的数组"的数据结构,每个元素存放链表头结点的数组,即数组和链表的结合体。
-
当我们往HashMap中put元素的时候,先根据key的hashCode重新计算hash值,根据hash值得到这个元素在数组中的位置(即下标),如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。
-
只有相同的hash值的两个值才会被放到数组中的同一个位置上形成链表。
-
如果这两个Entry的key通过equals比较返回true,新添加Entry的value将覆盖集合中原有Entry的value,但key不会覆盖。
3.总结
- HashMap中key的hashCode值决定了<k,v>键值对组成的entry在哈希表中的存放位置。
- HashMap中key通过equals()比较,确定是覆盖key对应的value值还是在链表中添加新的entry。
- 综合前两条,需要重写hashCode()和equals()方法。
八.TreeMap类
1.概述
TreeMap是SortedMap接口的实现类,可以根据Key进行排序,HashMap没有这个功能。
【特点】
-
底层由可排序二叉树实现
-
不指定比较器默认按照key自然升序,指定比较器按照比较器排序
2.遍历TreeMap的两种方法
使用KeySet
//使用keySet方式输出
Set<Integer> keySet = treemap.keySet();
for(Integer i: keySet){
System.out.println("key--->"+i+" value--->"+treemap.get(i));
}
使用EntrySet
//使用entrySet方式输出
Set<Map.Entry<Integer, Student>> entryset = treemap.entrySet();
for(Map.Entry<Integer, Student> entry:entryset){
System.out.println("key--->"+entry.getKey()+" value--->"+entry.getValue());
}
区别:
使用KeySet获取的是TreeMap中所有的key
使用EntrySet获取的是TreeMap中所有的键值对
3.为什么keySet和entryset输出后就是排好序的
package day23;
import java.util.*;
/**
* 作业2:treemap的keyset方式或是entryset方式如何保证的有序输出。
* @author 付君华
* @Date 2021年6月29日
* @Description
*/
public class Hw02 {
/*
分析keySet
public Set<K> keySet() {
return navigableKeySet();
}
上面的调用了下面的方法
public NavigableSet<K> navigableKeySet() {
KeySet<K> nks = navigableKeySet;
return (nks != null) ? nks : (navigableKeySet = new KeySet<>(this));
}
返回了一个NavigableSet 这个是SortedSet下的一个子接口 而这个KeySet是NavigableSet的实现类
判断nks是否为空,非空的话说明创建过了,不用重复出创建
空的话创建一个KeySet
看看这个KeySet具体代码
static final class KeySet<E> extends AbstractSet<E> implements NavigableSet<E> {
private final NavigableMap<E, ?> m;
KeySet(NavigableMap<E,?> map) { m = map; }
public Iterator<E> iterator() {
if (m instanceof TreeMap)
return ((TreeMap<E,?>)m).keyIterator();
else
return ((TreeMap.NavigableSubMap<E,?>)m).keyIterator();
}
}
主要看这个构造方法
static final class KeySet<E> extends AbstractSet<E> implements NavigableSet<E> {
private final NavigableMap<E, ?> m;
KeySet(NavigableMap<E,?> map) { m = map; }
首先是传入了个map,这个map也就是treemap,也就是调用keySet方法的的treemap
但是也没有进行排序啊,就创建了个对象,咋就排序了呢?
百度查了一下资料,网上说是在遍历的时候才会进行排序
so 嘎
那如果用普通for循环的方式来遍历是否就不会进行排序了呢?
不行,这KeySet中就没get方法,只能用增强for循环,只要一用这个增强for,那就会调用KeySet中的迭代器
然后进行排序,返回迭代器咋就排序了呢?
两种可能,一是for循环给实现的,而是迭代器实现的
找一下增强for的源码:
for(Iterator iterator = list.iterator(); iterator.hasNext(); System.out.println(i)){
i = (Integer)iterator.next();
}
很明显不是for实现的
那只能是迭代器实现的了
那看看迭代器
上面for中调用的是无参的迭代器方法,所以应该是
public Iterator<E> iterator() {
if (m instanceof TreeMap)
return ((TreeMap<E,?>)m).keyIterator();
else
return ((TreeMap.NavigableSubMap<E,?>)m).keyIterator();
}
如果是个treemap 那么就返回一个treemap 的iterator 否则返回那个玩意的iterator
那再看看keyIterator
Iterator<K> keyIterator() {
return new KeyIterator(getFirstEntry());
}
final class KeyIterator extends PrivateEntryIterator<K> {
KeyIterator(Entry<K,V> first) {
super(first);
}
public K next() {
return nextEntry().key;
}
}
final Entry<K,V> getFirstEntry() {
Entry<K,V> p = root;
if (p != null)
while (p.left != null)
p = p.left;
return p;
}
也就是将第一个entry传入到了KeyIterator的构造方法中
这个构造方法将这个传到了父类的构造中
PrivateEntryIterator(Entry<K,V> first) {
expectedModCount = modCount;
lastReturned = null;
next = first;
}
应该快找到答案了
主要看看这个类中的实现
abstract class PrivateEntryIterator<T> implements Iterator<T> {
Entry<K,V> next;
Entry<K,V> lastReturned;
int expectedModCount;
PrivateEntryIterator(Entry<K,V> first) {
expectedModCount = modCount;
lastReturned = null;
next = first;
}
public final boolean hasNext() {
return next != null;
}
final Entry<K,V> nextEntry() {
Entry<K,V> e = next;
if (e == null)
throw new NoSuchElementException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
next = successor(e);
lastReturned = e;
return e;
}
final Entry<K,V> prevEntry() {
Entry<K,V> e = next;
if (e == null)
throw new NoSuchElementException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
next = predecessor(e);
lastReturned = e;
return e;
}
public void remove() {
if (lastReturned == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
// deleted entries are replaced by their successors
if (lastReturned.left != null && lastReturned.right != null)
next = lastReturned;
deleteEntry(lastReturned);
expectedModCount = modCount;
lastReturned = null;
}
}
主要看他的next方法,怎么就能排序输出呢?
next = successor(e);
这里调用了一个方法
static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) {
if (t == null)
return null;
else if (t.right != null) {
Entry<K,V> p = t.right;
while (p.left != null)
p = p.left;
return p;
} else {
Entry<K,V> p = t.parent;
Entry<K,V> ch = t;
while (p != null && ch == p.right) {
ch = p;
p = p.parent;
}
return p;
}
}
这个百度了一下是获取后继节点的意思,但是也没实现排序啊
..
恍然大悟
在创建treemap的时候就已经是排好序的了(红黑二叉树)
然后获取节点的时候就是有序获取的,所以这个keySet就是有序的
那entry也就是一样的道理了
总结一下:
keySet和entrySet方法返回了一个set集合
这个set集合中的元素在创建treemap的时候就已经排好序了
上面的方法在调用的时候用到了treemap,然后这俩方法就将这个map保存到了一个内部类中
在遍历的时候,调用迭代器,然后将这个treemap的一个一个值给输出
*/
public static void main(String[] args) {
//创建一个TreeMap
Map<Integer, Student> treemap = new TreeMap<Integer, Hw02.Student>();
//创建学生数据
Student s1 = new Student("zhagnsan", 20, 2021001);
Student s2 = new Student("lisi", 20, 2021002);
Student s3 = new Student("wangwu", 20, 2021003);
//将数据添加到Map中
treemap.put(s1.getStdNo(),s1);
treemap.put(s2.getStdNo(),s2);
treemap.put(s3.getStdNo(),s3);
//使用keySet方式输出
Set<Integer> keySet = treemap.keySet();
for(Integer i: keySet){
System.out.println("key--->"+i+" value--->"+treemap.get(i));
}
//使用entrySet方式输出
Set<Map.Entry<Integer, Student>> entryset = treemap.entrySet();
for(Map.Entry<Integer, Student> entry:entryset){
System.out.println("key--->"+entry.getKey()+" value--->"+entry.getValue());
}
}
public static class Student {
private String name;
private int age;
private int stdNo;
@Override
public int hashCode() {
return Integer.valueOf(this.stdNo).hashCode()+this.name.hashCode();
}
@Override
public boolean equals(Object obj) {
Student stu=(Student)obj;
return (this.stdNo==stu.stdNo)&&(this.name.equals(stu.name));
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", stdNo="
+ stdNo + "]";
}
public Student(String name, int age, int stdNo) {
super();
this.name = name;
this.age = age;
this.stdNo = stdNo;
}
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;
}
public int getStdNo() {
return stdNo;
}
public void setStdNo(int stdNo) {
this.stdNo = stdNo;
}
}
}
九.泛型
1.概述
泛型就是用来约束数据类型的一种语法,尤其是在集合与框架编写中常见
2.泛型的简单使用
List<String> list = new ArrayList<String>();//这个集合中就只能存储String类型
3.泛型的拓展使用
//在类上加泛型
class Stu<T>{
public void m1(T t){
System.out.println(t);
}
}
//测试
class Main{
public static void main(String args[]){
//创建一个Stu类,带泛型
Stu<String> s = new Stu<String>();
//调用Stu中的m1方法
s.m1("hello");//这只能传字符串
}
}
//在方法上加泛型
public class Test {
public static void main(String[] args) {
m1("123");//输出123,这里传入什么类型都可以,相当于Object
}
public static <T> void m1(T t){
System.out.println(t);
}
}
4.extends关键字和super关键字
<T extends 类型 > 就是T是这个类型或子类
<T super 类型> 就是这个这个类型或父类
5.?符号
? 是指所有没有限定类型,T是限定了某一种类型
<? extends 类型> 所有这个类型或者这个类型的子类都可以<? super 类型> 所有这个类型或者这个类型的父类