Collection接口,还有一个父级接口Iterable可迭代的
Set 集合
Set的底层是用Map实现(存储在key中,value中是空的Object对象)
有序:取出的顺序和添加的顺序是一样的。
List是有序的,Set是无序的(LinkedHashSet是有序集合,同时维护了一个链表结构)
实例化Set集合:HashSet
HashSet set = new HashSet();
set.add("12");
set.remove("123");
set.size();
set.add("12");
int size = set.size();
System.out.println(size);//两次添加12算同一个元素
添加元素:Set不能存储相同的数据(使用equals比较是不是同一个元素)
//Set中不能存储相同的数据
set.add(456);//456存储的是Integer类型
set.add(456);
size = set.size();//用equals比较是否是同一个元素
System.out.println(size);
HashSet可以存储null值
set.add(null);//可以存储null
size = set.size();
System.out.println(size);
for(Object item:set){
System.out.println(item);
}
TreeSet:以红黑树的形式存储
TreeSet内部使用二叉树,内部节点是可比较的,默认情况下不能存储不同的类型
左子树比根节点小,右子树比根节点大
存入数据:
TreeSet treeSet = new TreeSet<>();
//存入数据
treeSet.add(2);
treeSet.add(200);
treeSet.add(3);
treeSet.add(88);
treeSet.add(45);
treeSet.add(72);
treeSet.add(1);
treeSet.add(99);
不能存入null,null值无法比较
遍历:按数值大小的顺序输出(TreeSet的遍历顺序是中序遍历)
for(Object item:treeSet){
System.out.print(item + ",");
}
System.out.println();
树的遍历:按访问根节点的顺序
先序遍历:先访问根节点,再访问左子树,再访问右子树
中序遍历:先访问左子树,再访问根节点,再访问右子树
后序遍历:先访问左子树,再访问右子树,最后访问根节点
对于自定义的类型,想要用TreeSet存储需要实现Comparable接口
class Student implements Comparable{
int score;
@Override
public String toString() {
return "学生的成绩是" + score;
}
@Override
public int compareTo(Object o){
if(o instanceof Student){
Student item = (Student)o;
if(this.score== item.score){
return 0;
}
return this.score> item.score?1:-1;
}else {
//如果o对象不是Student就无法比较 这是程序运行时出现的特殊情况
//这种特殊情况叫做异常 我们的方法处理不了这种业务,就要抛出一个异常对象
//告知调用此方法的代码
throw new RuntimeException("传入对象不可比较");
}
}
}
或者传入一个比较器Comparator
//比较器
Comparator<Student> com=(a,b)->{
if(a.score == b.score){
return 0;
}
return a.score>b.score?1:-1;
};
用TreeSet存储Student类型:
TreeSet<Student> stuTreeSet = new TreeSet<>(com);
Student stu1= new Student();stu1.score=89;
Student stu2 = new Student();stu2.score=79;
Student stu3 = new Student();stu3.score=69;
stuTreeSet.add(stu1);
stuTreeSet.add(stu2);
stuTreeSet.add(stu3);
for(Student item:stuTreeSet){
System.out.println(item);
}
LinkedHashSet:在集合结构的基础上,同时维护了一个链表结构用于存储前后节点,从而实现有序存储。
Map 映射
Map存储的是键值对:键(key),就是名字;值(value),存储的对象。
key是无序且唯一的,value可重复
实例化:HashMap
存储数据:
Map map = new HashMap();
//存储数据
map.put("A1","张三");
map.put("A2","李四");
map.put("A3","王五");
map.put("A4","赵六");
通过key获取存入的对象value
//可以通过存入的key获取存入的对象value
Object obj=map.get("A1");
System.out.println(obj);
通过key删除键值对
//通过key删除键值对
//传入key,返回删除的value值
Object rem_obj = map.remove("A1");
System.out.println("删除的value值:"+rem_obj);
//同时传入key和对应的value,返回boolean,不匹配删不掉
boolean bool = map.remove("A2","李四");
是否包含key
//是否包含key
map.containsKey("A1");
是否包含value
//是否包含value
map.containsValue("张三");
获取所有的key
Set setKey = map.keySet();
System.out.println(setKey);
获取所有的value
Collection con = map.values();
System.out.println(con);
TreeMap:key应该是可以比较的,不可以是null
Hashtable:是一种历史遗留的类型,没有使用驼峰命名法
1. Hashtable的key和value都不能是null;
2. Hashtable是线程安全的,和Vector一样性能较差
Hashtable ht=new Hashtable();//历史遗留
ht.put("t1","张三");
LinkedHashMap:有序的
LinkedHashMap lMap=new LinkedHashMap();
lMap.put("01","小明");
ConcurrentHashMap:线程安全的,效率较高
线程安全的Map有两个:Hashtable、ConcurrentHashMap(性能优异,锁的颗粒度比较小)
HashMap的底层实现:数组+链表
1. HashMap 数组的默认容量16 ,每次扩容 2倍
2. 扩容阈值 0.75 超过16*0.75=12就要扩容
3. 为加快检索效率可能会树化 最小树化阈值 8,即一个链上的元素超过8就对该链进行树化
4. 一支树上的元素低于6个,这个树就会退化成链
5. 最小树化容量阈值 64,数组容量要达到64才能树化
6. 如果数组的长度没有达到64,优先扩容,重新分配存储的位置
存入过程:
先计算hash值跟数组长度按位与运算得到数组长度之内的下标,找到对应位置;如果为空就存入该位置,不为空就先判断链表上所有元素的key值是否有一致的,有一致就更新对应key的value值;都不一致就存入以数组内对应位置的对象为起始点的链表的最后;当数组内元素的个数超过当前数组容量*扩容阈值0.75,或者单个链的长度超过最小树化阈值8而数组长度没有达到64,优先扩容2倍,同时重新分配元素的存储位置;一个链的元素超过8,数组容量达到64,对这个链进行树化;一支树的元素低于6个,这个树就会退化。
泛型
泛型:广泛的数据类型,确保类型安全的一种途径
类型不安全:类型转换失败
List list=new ArrayList();
list.add("123");
//list.add(new Object());
for(Object object:list){
//类型转换时没有明确对象的数据类型,进行强制数据类型转换
//会抛出 ClassCastException(类型转换异常)
//类型不安全
String str = (String)object;
System.out.println(str);
}
确保类型转换成功(类型安全)的方式:
1.使用泛型
2. instanceof 检查类型是否一致 a.getClass()==b.getClass()
定义泛型:在类或方法上定义
public class EasyGenericity<J,M,N,Y,E,T,K,V,Easy> {
private M m;
public void test(M m,N n){}
public <E> E test(E e){ //就近原则,E是方法定义的泛型
//表明返回的类型和传入的类型是相同的
//在方法上定义泛型,在方法返回值前面加尖括号
return e;
}
}
定义上限:要继承自某类
public static void main(String[] args) {
testA(new ArrayList<>());
//testA(new HashSet<>());//没有继承自List
//类型安全
}
public static <J extends List> J testA(J j){//表明泛型J要继承自List,定义上限
return j;
}
定义下限:是某类的超类(包括本身)
public class EasyGenericity<J,M,N,Y,E,T,K,V,Easy> {
public static void main(String[] args) {
List<A> list1=new ArrayList<>();
List<B> list2=new ArrayList<>();
List<C> list3=new ArrayList<>();
testAA( list2,new B());//根据第二个参数的类型约束第一个参数必须是B的超类
//list1、list2可以,list3不行
}
//定义下限
public static <A> void testAA(List<? super A> a,A p){
}
}
class A{}
class B extends A{}
class C extends B{}
泛型的使用:
//泛型的使用
List<String> listA = new ArrayList<>();//只能存储String类型
//泛型的检查在声明上,后面空的可以不写(即Object,只起传递作用)
//在编译的时候起作用,在运行的时候不起作用
listA.add("123");
Map<String,Object> map = new HashMap<>();
//存入123是存封装类型,有一个装箱的操作
子类向父类传递泛型