一、Collection集合
0. Collection接口
0) .概述
a.定义的是所有单列集合中共性的方法
b.所有的单列集合都可以使用共性的方法
c.没有带索引的方法(因为Set集合没有索引)
1) .学习方法
a.学习顶层:学习顶层接口/抽象类中共性的方法
b. 使用底层:顶层无法实例化,通过使用底层熟悉规则
2) .常用方法
1.
public boolean add(E e)
2.public void clear()
3.public boolean remove(E e)
4.public boolean contains(E e)
5.public boolean isEmpty()
6.public int size()
7.public object[] toArray
1. List接口
0. 概述
a. 有序(存储和取出元素顺序相同)
b. 允许存储重复的元素
c. 有索引,可以使用普通的for循环遍历
2. Set接口
0.概述
a.不允许存储重复元素
b. 无索引(不能使用普通的for循环遍历)
二、迭代器
1. Iterator接口概述
Iterator迭代器,即Collection集合元素的通用获取方式
在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续再判断,如果还有就再取出来,一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代
2. 常用方法
boolean hasNext()判断是否有元素E next()取出集合中的下一个元素
3. 注意事项
Iterator迭代器,是一个接口,无法直接使用,需要使用Iterator接口的实现类对象,获取实现类的方式比较特殊
Collection接口有一个方法,为iterator(),这个方法返回的就是迭代器实现类的对象Iterator<E>(其中的泛型E根据集合中的数据类型而定),iterator()返回在此Collection的元素上进行迭代的迭代器
4. 使用步骤(重点)
/*
1. 使用集合中的方法iterator()获取迭代器的实现类对象,使用Iterator接口接收(多态)
2. 使用Iterator接口中的方法hasNext()判断还有没有下一个元素(最开始,指针指向-1位置)
3. 使用Iterator接口中的方法next()取出集合中的下一个元素(同时会将指针向后移动一位)
*/
5.增强for循环(foreach()语句)
0. 底层
增强for循环:底层使用的也是迭代器,使用for循环的格式,简化了迭代器的书写
1. 特性
Collection<E> extends Iterator<E>:
所有的单列集合都可以使用增强for循环,public interface Iterator<T>实现这个接口允许对象成为foreach语句的目标(T->Type , E->Element)
2. 作用
用来遍历集合和数组
3. 格式:
for(集合/数组的数据类型 变量名:集合名/数组名){
sout(变量名);
}
三、泛型
1. ArrayList特点
可以存储多种类型数据
//可以实现,但风险更大,因此多使用泛型
ArrayList list = new ArrayList();
list.add("abc");
list.add(11);
2. 泛型使用
1).定义含有泛型的类
泛型定义在类名后,用<>标识
创建对象的时候确定泛型的数据类型,如果创建时不标明,则默认为是Object类型
public class GenericClass<E>{
private E name;
...
}
2).定义含有泛型的方法
泛型定义在方法的修饰符和返回值类型之间
调用方法时确定泛型类型即可
public <M> void method(M m){
System.out.println(m);
}
3). 实现含有泛型的接口
泛型定义在接口之后,用 <>标识
a. 可以在实现类中确定类型 b. 也可以在创建实现类对象时确定类型(参考ArrayList<>)
a .在实现类中确定类型
public class GenericInterfaceImpl implements GenericInterface<String>{
@Override
public void method(String s){
System.out.prinln(s);
}
}
b. 在创建实现类对象时确定类型
public class GenericInterfaceImpl<I> implements GenericInterface<I>{
@Override
public void method(I i){
System.out.println(i);
}
}
3. 泛型通配符
? : 代表任意的数据类型,未要求统一数据类型(区别于T)
1). 使用方式
不能创建对象使用,只能作为方法的参数使用,定义存储时都不能使用
public class DemoGeneric{
public static void main String(String[] args){
ArrayList<Integer> list1 = new Arraylist<>();
list1.add(1);
list1.add(2);
Arraylist<String> list2 = new Arraylist<>();
list2.add("a");
list2.add("b");
}
/*
定义一个方法,可以遍历所有类型的ArrayList集合
不能用Object类似多态一样,因为泛型没有继承概念
*/
public static void printArray(ArrayList<?> list){
Iterator<?> it = list.iterator();
while(it.hasNext()){
Object o = it.next();(多态)
System.out.println(o);//直接打印o.toString()输出了子类o的内容
}
}
2). 高级使用
泛型的上限限定: ? extends E:
代表使用的泛型只能是E类型的子类/本身
泛型的下限限定:? super E :
代表使用的泛型只能是E类型的父类/本身
四、List接口实现类
List extends Collection
1. ArrayList
1).List接口的大小可变数组(底层实现)的实现
具有查询快、增删慢的特点,大量查询情境下适用。
2).此实现不是同步的,是多线程的
2. LinkedList
1).List接口的双向链表的实现
具有查询慢、增删快的特点,大量增删情境下适用。
2).此实现不是同步的,是多线程的
3).具有很多操作表头和结尾元素的方法
getLast(),addFirst(),push(),pop()...
3.Vector
1).List接口的大小可变数组(底层实现)的实现
具有查询快、增删慢的特点。
2).此实现是同步的,不同于以上两个实现类,是非多线程的
五、哈希值
1.定义
系统随机给出的十进制整数,由系统随机给出
,就是对象的地址值,但是是一个逻辑地址(模拟出来的),不是实际存储的物理地址,对象的toString()方法同样不是实际存储的物理地址, 因此不可看到两个对象的toString()或hashCode()返回相同值,便认定两个对象的地址值相同
2.方法
Object类中有一个方法int hashCode()返回该对象哈希码值
3. 方法源码
/*
native:代表该方法调用的是本地操作系统的方法
*/
public native int hashCode(){
public native int hashCode();
}
4.使用说明
public class DemoHashCode{
public static void main(String[] args){
Person p1 = new Person();
int h1 = p1.hashCode();
System.out.println(h1);//1967205423
Person p2 = new Person();
int h2 = p2.hashCode();
System.out.println(h2);//42121758
/*
toString()方法的源码:
return getClass().getName()+"@"+Integer.toHexString(hashCode());
*/
//com.itheima.demo03.hashCode.Person@75412c2f
//是1967205423的十六进制表示
System.out.println(p1);
/*
即使重写Person的hashCode()的方法,让其返回1
*/
//此处依然返回false,因为hashCode是模拟地址
System.out.println(p1 == p2);
/*
String类重写了Object的hashCode()方法
*/
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1.hashCode());//96354
System.out.println(s2.hashCode());//96354
//特例,内容不同,哈希值相同
System.out.println("重地".hashCode());//1179395
System.out.println("通话".hashCode());//1179395
}
}
5.一些疑问
1).String类重写了equals()方法,为什么还要重写hashCode()方法?
hashCode()方法是利用哈希算法(散列算法),将对象数据根据其特征使用特定算法定义到一个地址上,这样后进入的数据只要看对应的hashCode地址上是否有值,再用equals()方法比较,如果直接用equals()方法比较,比较次数多,降低效率
2).hashCode重写为什么*31
31*i == (i<<5)-i,方便机器实现,同时记住31是一个奇素数,好计算,左移5位性能最好
6.跪一个帖子的回复,直接引用了
1.hashcode是用来查找的,如果你学过数据结构就应该知道,在查找和排序这一章有 例如内存中有这样的位置
0 1 2 3 4 5 6 7
而我有个类,这个类有个字段叫ID,我要把这个类存放在以上8个位置之一,如果不用hashcode而任意存放,那么当查找时就需要到这八个位置里挨个去找,或者用二分法一类的算法。 但如果用hashcode那就会使效率提高很多。 我们这个类中有个字段叫ID,那么我们就定义我们的hashcode为ID%8,然后把我们的类存放在取得得余数那个位置。比如我们的ID为9,9除8的余数为1,那么我们就把该类存在1这个位置,如果ID是13,求得的余数是5,那么我们就把该类放在5这个位置。这样,以后在查找该类时就可以通过ID除 8求余数直接找到存放的位置了。
2.但是如果两个类有相同的hashcode怎么办那(我们假设上面的类的ID不是唯一的),例如9除以8和17除以8的余数都是1,那么这是不是合法的,回答是:可以这样。那么如何判断呢?在这个时候就需要定义 equals了。 也就是说,我们先通过 hashcode来判断两个类是否存放某个桶里,但这个桶里可能有很多类,那么我们就需要再通过 equals 来在这个桶里找到我们要的类。 那么。重写了equals(),为什么还要重写hashCode()呢? 想想,你要在一个桶里找东西,你必须先要找到这个桶啊,你不通过重写hashcode()来找到桶,光重写equals()有什么用啊
7.总结
1.使用
hashcode()方法提前校验,可以避免每一次比对都调用equals()方法,提高效率
2.保证是同一个对象,如果重写了equals方法,而没有重写hashcode方法,会出现equals相等的对象,hashcode不相等的情况,重写hashcode方法就是为了避免这种情况的出现。
3. 一句话,hashCode()的存在是为了方便查找的
六、Set接口实现类
Set extends Collection
常用实现类有
1. HashSet
1).HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持,不保证有序;
因为底层是哈希表结构,因此查询速度非常快。
2).此实现不是同步的,是多线程的
3).不存储重复元素的原理
前提:
存储的元素必须重写hashCode()和equals()方法
Set集合在调用add()方法的时候,add()方法会调用元素的hashCode()方法和equals()方法,判断元素是否重复:
判断时先调用hashCode()方法,如果返回值相等(哈希冲突)则调用equals()方法判断元素值是否相同
2. LinkedHashSet
1). 有序、Set接口的哈希表和链表实现
底层是一个哈希表(数组 + 链表/红黑树) + 双向链表(记录元素的存储顺序) ,保证元素有序
七、集合工具类Collections
1.Collections概述
java.util.Collections用来对集合进行操作
2.常用方法
addAll(Collection<T>),
shuffle(List<?> list)打乱顺序,
sort(List<T> list)默认规则排序,
sort(List<T> list,Comparator<? super T>)指定规则排序
?泛型占位符未要求统一类型,T泛型占位符要求同一种类型
3. 指定规则排序方法
//匿名内部类实现接口
Collections.sort(list,new Comparator<Integer>(){
@Override
public int compare(Integer o1,Integer o2){
//return o1-o2;//升序
return o2-o1;//降序
}
});