集合与数组的区别
数组长度固定,可以存储基本数据类型和对象
集合长度可以改变,只能存储对象
Collection集合的常用功能:add,remove,contains,toarray,size
Collection接口,单列集合的最顶端接口,里面定义了集合的共享内容(方法)
使用迭代器对集合进行取元素
java.util.Iterator接口:迭代器
Iterator是个接口,使用时需要使用这个接口的实现类
在Collectionz集合中有个方法是iterator(),此方法返回的是Iterator接口的实现对象(是每个集合内部的实现类)
我们不需要关注返回的是Iteractor接口的哪个实现类,只需要知道返回的是实现类对象可以使用就行了(多态)
这种编程思想,称之为面新接口编程
Iterator<和集合的数据类型一样> it=new list.iteractor();
while(it.hasNext()){
数据类型和集合一样 变量名=it.next();
在使用迭代器迭代集合时,对集合长度进行了修改(增删元素),会出现并发yichang
解决:1,不修改长度
2,Iteractor接口有个子接口ListIteractor(只有使用list接口的实现类可以使用)
ListIteractor接口中有add和remove方法
获取迭代器的子类 ListIteractor<String> lit =list.ListIteractor();
集合的使用细节:
有泛型:Collection<String> coll =new ArrayList<String>();
无泛型:Collection<Object> coll =new ArrayList<String>();
增强for循环:底层原理是个迭代器,不能对集合的长度进行修改,否则会抛出并发异常
for (数组/集合的数据类型 变量名:数组名/或者集合名){
syso(变量名);
}
泛型
好处:1,使集合存储的数据类型更加丰富
2,可以把编译期异常,提升到编译期异常
3,避免了类型转换的麻烦
含有泛型的方法:
方法上定义一个泛型:和类上的泛型不同(类的泛型也可以确定了,没有关系)
修饰符<T> 返回值类型 方法名(参数列表T){
方法体;
}
方法上的泛型,在调用方法的时候确定数据类型
方法使用类上的泛型,类是什么泛型,方法就是什么泛型
含有泛型的接口
定义实现类使用含有泛型的接口第一种方式:实现类实现接口的同时指定接口的数据类型
使用的时候,数据类型已经确定了,就不管了;默认使用的就是指定的类型
class GenericInterfaceImpl1 implements GenericInterface<String>
定义实现类使用含有泛型的接口第二种方式:接口使用什么泛型,类就使用什么泛型和接口一样
相当于定义一个含有泛型的类
创建实现类对象的时候确定泛型的数据类型
class GenericInterfaceImpl2<E> implements GenericInterface<E>
new的时候确定泛型的类型
泛型通配符 ? 代表任意的数据类型
泛型的限定:
上限限定:? extends E代表只要是E类型的子类/或者本身即可
下线限定:? super E代表只要是E类型的父类/或者本身
在ArrayList集合中有一个构造方法,可以传递其它的集合
可以参数传递的集合中的元素,添加到创建对象的集合中
ArrayList(Collection<? extends E> c)
?:泛型的通配符,可以是任意的类型
? extends E:传递的集合数据类型,必须是创建的ArrayList集合泛型的子类或者本身
list集合的特点:有序,允许重复,有索引
LinkedList底层原理是个双向链表:查询慢,增删快 使用实现类特有的方法,不能使用多态
使用list存储的数据类型
堆栈:先进后出
队列:先进先出
数组:一块连续的存储区域,查询快,增删慢
链表:每个元素指向下个元素,查询慢,增删快
ArrayList集合数据存储的结构是数组结构,不是同步的,是多线程,效率高
Vector集合数据存储的结构是数组结构,同步的,和ArrayList集合功能一样,被替代了
LinkedList集合数据存储的结构是链表结构,方便元素的添加和删除
HashSet集合底层是个哈希表(数组+单向链表)
LinkedHashSet集合是HashSet集合的一个子类,是哈希表(数组+单向链表)+链表,包含一个双向链表,是有序的集合
set集合的特点
不允许重复
没有索引,不能使用普通for,只能使用迭代器和增强for
HashSet的特点:
1.是一个无序的集合:存储元素和取出元素的顺序可能不一致,同一句话或者对象取出顺序是固定的,根据哈希表计算得到
2.底层是一个哈希表(数组+单向链表):查询快,增删又快
哈希值:是个十进制数,有操作系统给出,如果哈希值没有被使用,每次给出的可能是同一个整数
哈希表的特点
底层是数组+链表的组合:查询快,增删快
面试: 集合的扩容也叫集合再哈希
Hashtable中有个rehach方法,增加此哈希表的容量对其进行重组,以便更有效的容纳和访问其元素;
HashSet特点:不允许存储重复
判断是否重复,依据hashCode方法和equals方法,子类要重写这两个方法,保证不能存储重复的元素
使用HashSet集合存储自定义元素,自定义类型必须重写hashCode和equals方法
LinkedHashSet底层是个哈希表(数组+单向链表)+链表;包含一个双向链表,是个有序集合
判断集合元素唯一的原理
ArrayList:存储元素前使用constains方法
HashSet:重写hashCode和equals方法使用add方法时就会自己调用hashCode和equals判断是否为重复元素
可以使用HashSet集合过滤ArrayList中的重复元素
面试题: hashCode和equals方法的面试题
两个对象 Person p1 p2
问题1:如果两个对象的哈希值相同,两个对象的equals一定会返回true吗?
不一定
如果两个对象的equals方法返回true,两个对象的哈希值一定相同吗?
一定(常规协定)
Map集合
java.util.Map<K,V>接口:是一个双列集合
map集合中的元素都是成对出现的,成对存储的
map集合中的元素都是以一对键和值得形式组成存在的,称为键值对,理解为夫妻对
map集合中的键不能重复,值可以重复
map集合中的每一个键对应着一个值
方法:
put(K key,V value)
remove(Object key)
get(Object key)
set<Map.Entry<K,V>> entrySet() 获取到Map集合中所有的键值对对象的集合(Set集合)
set<Map.Entry<K,V>>可以将Map.Entry<K,V>理解为一个Person类,而且还是Map的成员内部类(成员内部类的使用:外部类名.内部类名)
Set<K>keySet() 获取Map集合中所有键,存储到Set集合中
Map集合遍历的两种方式:
方式1:
a.获取Map集合中的所有键,返回对应的Set集合
Set<String> keys =map.keySet();
b.遍历键的集合,获取到每一个键
for(String key: keys){
c.通过键,找到对应的值
Student s =map.get(key);
syso. "key+ s.getName()+s.getAge()"
}
方式2:根据键值对对象找键和值的方式
a.获取Map集合中所有的键值对元素,返回对应得Set集合
b.遍历键值对元素,获取到每一个键值对元素对象
c.通过键值对元素对象,获取对应的键,和对应的值
HashMap 特点:
是Map集合的子集合
底层采用哈希表结构
HashMap集合中的key不能重复,通过重写hashCode()和equels()方法来保证键的唯一
不能保证元素存于取得顺序完全一致
LinkedHashMap 特点:
是HashMap集合的子集合
底层采用哈希表+链式结构
LinkedHashMap集合中的key不能重复,通过重写hashCode() 与 equals()方法来保证键的唯一。
保证元素存于取的顺序完全一致
Hashtable:
Map的实现类Hashtable
Hashtable底层是哈希表,特点和hashMap一样,被它替代了
Hashtable是单线程慢,hashMap是多线程运行快
唯一一个不允许存储null
Properties:Map 集合的一种,是Hashtable(同步效率低被hashMap替代,唯一不能存空)集合的子集合,它的键和值都是String类型的,唯一能与IO流使用的集合
方法:load store
Collections中的静态方法
public static <T> void sort(list<T>list)排序
如果类型是String的,排序规则是根据字符串的ASCII值排序
public static <T> void shuffle(list<?>list)集合中的元素存储位置随机打乱
可变参数:
格式:
修饰符 返回值类型 方法名(数据类型... 变量名){
方法体;
}
原理:可变参数的底层原理可以看成一个数组,传递不同个数的参数会创建不同的数组长度的数组,来接受这些参数
注意:1,一个方法只能定义一个可变参数
2,一个方法的参数如果有多个,可变参数就必须写在参数列表的末尾
静态导入:
import static java.lang.System.out;导包加static,倒入静态方法
out.println("你好"); 以后的程序可以省略类名.