什么是集合
集合,集合是java中提供的一种容器,可以用来存储多个数据。
集合和数组的区别
- 数组的长度是固定的。集合的长度是可变的
- 集合中存储的元素必须是引用类型数据
存储结构
数据的存储结构
- 栈结构:后进先出/先进后出(手枪弹夹) FILO (first in last out)
- 队列结构:先进先出/后进后出(银行排队) FIFO(first in first out)
- 数组结构:查询快:通过索引快速找到元素 增删慢:每次增删都需要开辟新的数组,将老数组中的元素拷贝到新数组中
- 链表结构:查询慢:每次都需要从链头或者链尾找起 增删快:只需要修改元素记录的下个元素的地址值即可不需要移动大量元素
哈希表的数据结构
- 加载因子:表中填入的记录数/哈希表的长度
数组中的16个位置,其中存入16*0.75=12个元素,如果在存入第十三个(>12)元素,导致存储链子过长,会降低哈希表的性能,那么此时会扩充哈希表(在哈希),底层会开辟一个长度为原长度2倍的数组,把老元素拷贝到新数组中,再把新元素添加数组中
当存入元素数量>哈希表长度*加载因子,就要扩容,因此加载因子决定扩容时机
集合继承关系
查看ArrayList类发现它继承了抽象类AbstractList同时实现接口List,而List接口又继承了Collection接口。Collection接口为最顶层集合接口了。
interface List extends Collection {
}
public class ArrayList extends AbstractList implements List{
}
这说明我们在使用ArrayList类时,该类已经把所有抽象方法进行了重写。那么,实现Collection接口的所有子类都会进行方法重写
Collection 接口 | ---------------------------------------------------------------- | | List接口 Set接口 | | ---------------- ------------- | | | | ArrayList类 LinkedList类 HashSet类 LinkedHashSet类
集合Collection
- Collection接口中的方法
- 是集合中所有实现类必须拥有的方法
- 使用Collection接口的实现类,程序的演示
- ArrayList implements List
- List extends Collection
- 方法的执行,都是实现的重写
Modifier and Type | Method and Description |
---|---|
boolean |
add(E e)
确保此集合包含指定的元素(可选操作)。
|
boolean |
addAll(Collection extends E> c)
将指定集合中的所有元素添加到此集合(可选操作)。
|
void |
clear()
从此集合中删除所有元素(可选操作)。
|
boolean |
contains(Object o)
如果此集合包含指定的元素,则返回
true 。
|
boolean |
containsAll(Collection> c)
如果此集合包含指定
集合中的所有元素,则返回true。
|
boolean |
equals(Object o)
将指定的对象与此集合进行比较以获得相等性。
|
int |
hashCode()
返回此集合的哈希码值。
|
boolean |
isEmpty()
如果此集合不包含元素,则返回
true 。
|
Iterator |
iterator()
返回此集合中的元素的迭代器。
|
default Stream |
parallelStream()
返回可能并行的
Stream 与此集合作为其来源。
|
boolean |
remove(Object o)
从该集合中删除指定元素的单个实例(如果存在)(可选操作)。
|
boolean |
removeAll(Collection> c)
删除指定集合中包含的所有此集合的元素(可选操作)。
|
default boolean |
removeIf(Predicate super E> filter)
删除满足给定谓词的此集合的所有元素。
|
boolean |
retainAll(Collection> c)
仅保留此集合中包含在指定集合中的元素(可选操作)。
|
int |
size()
返回此集合中的元素数。
|
default Spliterator |
spliterator()
创建一个 Spliterator 在这个集合中的元素。
|
default Stream |
stream()
返回以此集合作为源的顺序
Stream 。
|
Object[] |
toArray()
返回一个包含此集合中所有元素的数组。
|
|
toArray(T[] a)
返回包含此集合中所有元素的数组;
返回的数组的运行时类型是指定数组的运行时类型。
|
toArray()方法 Collection接口方法
Object[] toArray() 集合中的元素,转成一个数组中的元素, 集合转成数组
返回是一个存储对象的数组, 数组存储的数据类型是Object
Collection<String> coll = new ArrayList<String>();
coll.add("abc");
coll.add("itcast");
coll.add("itheima");
coll.add("money");
coll.add("123");
Object[] objs = coll.toArray();
for(int i = 0 ; i < objs.length ; i++){
System.out.println(objs[i]);
}
/*
* Java中三种长度表现形式
* 数组.length 属性 返回值 int
* 字符串.length() 方法,返回值int
* 集合.size()方法, 返回值int
*/
contains()方法 Collection接口方法
判断对象是否存在于集合中,对象存在返回true
Collection<String> coll = new ArrayList<String>();
coll.add("abc");
coll.add("itcast");
coll.add("itheima");
coll.add("money");
coll.add("123");
boolean b = coll.contains("itcast");
System.out.println(b);// true
clear()方法 Collection接口方法
清空集合中的所有元素 集合容器本身依然存在
Collection<String> coll = new ArrayList<String>();
coll.add("abc");
coll.add("bcd");
System.out.println(coll);
coll.clear();
System.out.println(coll);
remove()方法 Collection接口方法
boolean remove(Object o)移除集合中指定的元素
Collection<String> coll = new ArrayList<String>();
coll.add("abc");
coll.add("money");
coll.add("itcast");
coll.add("itheima");
coll.add("money");
coll.add("123");
System.out.println(coll);
boolean b = coll.remove("money");
System.out.println(b);
System.out.println(coll);
Iterator迭代器
Modifier and Type | Method and Description |
---|---|
default void |
forEachRemaining(Consumer super E> action)
对每个剩余元素执行给定的操作,直到所有元素都被处理或动作引发异常。
|
boolean |
hasNext()
如果迭代具有更多元素,则返回
true 。
|
E |
next()
返回迭代中的下一个元素。
|
default void |
remove()
从底层集合中删除此迭代器返回的最后一个元素(可选操作)。
|
迭代器代码实现部分
Collection<String> coll = new ArrayList<String>();
// 如果ArrayList改成其他Iterator的实现类也可以实现
// 比如HashSet也是Iterator的实现类
coll.add("abc1");
coll.add("abc2");
coll.add("abc3");
coll.add("abc4");
//迭代器,对集合ArrayList中的元素进行取出
//调用集合的方法iterator()获取出,Iterator接口的实现类的对象
Iterator<String> it = coll.iterator();
//迭代是反复内容,使用循环实现,循环的条件,集合中没元素, hasNext()返回了false
while(it.hasNext()){
String s = it.next();
System.out.println(s);
}
迭代器的执行过程
//cursor记录的索引值不等于集合的长度返回true,否则返回false
public boolean hasNext() {
return cursor != size; //cursor初值为0
}
//next()方法作用:
//①返回cursor指向的当前元素
//②cursor++
public Object next() {
int i = cursor;
cursor = i + 1;
return elementData[lastRet = i];
}
while循环和for循环迭代写法
Collection<String> coll = new ArrayList<String>();
coll.add("abc1");
coll.add("abc2");
Iterator<String> it = coll.iterator();
Iterator<String> it2 = coll.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
for (Iterator<String> it2 = coll.iterator(); it2.hasNext(); ) {
System.out.println(it2.next());
}
集合迭代中的转型
在使用集合时,我们需要注意以下几点
- 集合中存储其实都是对象的地址
- jdk1.5版本以后可以存储基本数值,因为出现了基本类型包装类,它提供了自动装箱操作,所以集合中的元素就是基本数值的包装类对象
如果集合没有声明类型默认就是Object类型可以存储任何类型的对象,取出时要使用元素的特有内容,必须向下转型,如果集合中存放的是多个对象,这时进行向下转型可能会发生类型转换异常
Collection coll = new ArrayList();
coll.add("abc");
coll.add("aabbcc");
coll.add("shitcast");
Iterator it = coll.iterator();
while (it.hasNext()) {
//由于元素被存放进集合后全部被提升为Object类型
//当需要使用子类对象特有方法时,需要向下转型
String str = (String) it.next();
System.out.println(str.length());
}
Iterator接口也可以使用<>来控制迭代元素的类型的
Collection<String> coll = new ArrayList<String>();
coll.add("abc");
coll.add("aabbcc");
coll.add("shitcast");
Iterator<String> it = coll.iterator();
while (it.hasNext()) {
String str = it.next();
//当使用Iterator<String>控制元素类型后,就不需要强转了。获取到的元素直接就是String类型
System.out.println(str.length());
}
迭代器的并发修改异常
迭代器的并发修改异常 java.util.ConcurrentModificationException ,在遍历的过程中,使用了集合方法修改了集合的长度导致异常
解决办法
- 在迭代时,不要使用集合的方法操作元素。
- 通过ListIterator迭代器操作元素是可以的,ListIterator的出现,解决了使用Iterator迭代过程中可能会发生的错误情况。
List<String> list = new ArrayList<String>();
list.add("abc1");
list.add("abc2");
list.add("abc3");
list.add("abc4");
//对集合使用迭代器进行获取,获取时候判断集合中是否存在 "abc3"对象
//如果有,添加一个元素 "ABC3"
Iterator<String> it = list.iterator();
while(it.hasNext()){
String s = it.next();
//对获取出的元素s,进行判断,是不是有"abc3"
if(s.equals("abc3")){
list.add("ABC3");
}
System.out.println(s);
增强for循环
JDK1.5版本后,出现新的接口 java.lang.Iterable Collection开是继承Iterable Iterable作用,实现增强for循环
// 格式
/*
for( 数据类型 变量名 : 数组或者集合 ){
sop(变量);
}
*/
// 遍历数组
String[] str = {"abc","itcast","cn"};
for(String s : str){
System.out.println(s);
}
int[] arr = {3,1,9,0};
for(int i : arr){
System.out.println(i);
}
// 遍历集合
ArrayList<String> array = new ArrayList<String>();
array.add("a");
array.add("b");
for(String p : array){
System.out.println(p);
}
泛型
泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。 Java语言引入泛型的好处是安全简单。
在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。
泛型的引入
集合中是可以存放任意对象的,只要把对象存储集合后,那么这时他们都会被提升成Object类型。当我们在取出每一个对象,并且进行相应的操作,这时必须采用类型转换。比如下面程序:
ArrayList list = new ArrayList();
//由于集合没有做任何限定,任何类型都可以给其中存放
//相当于:Object obj=new Integer(5);
list.add("abc");
list.add("itcast");
list.add(5);
Iterator it = list.iterator();
while(it.hasNext()){
//需要打印每个字符串的长度,就要把迭代出来的对象转成String类型
String str = (String) it.next();
//编译时期仅检查语法错误,String是Object的儿子可以向下转型
//运行时期String str=(String)(new Integer(5))
//String与Integer没有父子关系所以转换失败
//程序在运行时发生了问题java.lang.ClassCastException
//String str=(String)obj;
System.out.println(str.length());
}
泛型的定义和使用
JDK1.5 出现新的安全机制,保证程序的安全性
泛型: 指明了集合中存储数据的类型 <数据类型>
Collection<String> coll = new ArrayList<String>();
coll.add("abc");
coll.add("rtyg");
coll.add("43rt5yhju");
// coll.add(1); // 限定了类型 整型不能通过编译
Iterator<String> it = coll.iterator();
while(it.hasNext()){
String s = it.next();
System.out.println(s.length());
}
Java中的伪泛型
泛型只在编译时存在,编译后就被擦除,在编译之前我们就可以限制集合的类型,起到作用
例如:ArrayList
编译后:ArrayList al = new ArrayList();
泛型类
/*
修饰符 class 类名<代表泛型的变量> { }
*/
// API中的ArrayList集合
class ArrayList<E>{
public boolean add(E e){ }
public E get(int index){ }
}
// 创建对象时,确定泛型的类型
// 例如,ArrayList<Integer> list = new ArrayList<Integer>();
class ArrayList<String>{
public boolean add(String e){ }
public String get(int index){ }
}
// 例如,ArrayList<Integer> list = new ArrayList<Integer>();
class ArrayList<Integer>{
public boolean add(Integer e){ }
public Integer get(int index){ }
}
泛型方法
/*
修饰符 <代表泛型的变量> 返回值类型 方法名(参数){ }
*/
// 把集合元素存储到指定数据类型的数组中,返回已存储集合元素的数组
// public <T> T[] toArray(T[] a){ }
ArrayList<String> list = new ArrayList<String>();
String[] arr = new String[100];
String[] result = list.toArray(arr);
// 变量T的值就是String类型。变量T,可以与定义集合的泛型不同
// public <String> String[] toArray(String[] a){ }
泛型接口
// 带有泛型的接口
public interface List <E>{
abstract boolean add(E e);
}
// 实现类,先实现接口,不理会泛型
public class ArrayList<E> implements List<E>{ }
// 后期创建集合对象的时候,指定数据类型
new ArrayList<String>();
// 实现类,实现接口的同时,也指定了数据类型
public class XXX implements List<String>{ }
new XXX();
泛型的好处
- 将运行时期的ClassCastException,转移到了编译时期变成了编译失败
- 避免了类型强转的麻烦
ArrayList<String> list = new ArrayList<String>();
list.add("abc");
list.add("itcast");
//list.add(5);
// 当集合明确类型后,存放类型不一致就会编译报错
// 集合已经明确具体存放的元素类型,那么在使用迭代器的时候
// 迭代器也同样会知道具体遍历元素类型
Iterator<String> it = list.iterator();
while(it.hasNext()){
String str = it.next();
System.out.println(str.length());
}
// 当使用Iterator<String>
// 控制元素类型后,不需要强转,获取到的元素直接就是String类型
泛型的通配符
public static void main(String[] args) {
ArrayList<String> array = new ArrayList<String>();
HashSet<Integer> set = new HashSet<Integer>();
array.add("123");
array.add("456");
set.add(789);
set.add(890);
iterator(array);
iterator(set);
}
/*
* 定义方法,可以同时迭代2个集合
* 参数: 怎么实现 , 不能写ArrayList,也不能写HashSet
* 参数: 或者共同实现的接口
* 泛型的通配,匹配所有的数据类型 ?
*/
public static void iterator(Collection<?> coll){
Iterator<?> it = coll.iterator();
while(it.hasNext()){
//it.next()获取的对象,什么类型
System.out.println(it.next());
}
}
泛型的限定
public static void main(String[] args) {
//创建3个集合对象
ArrayList<ChuShi> cs = new ArrayList<ChuShi>();
ArrayList<FuWuYuan> fwy = new ArrayList<FuWuYuan>();
ArrayList<JingLi> jl = new ArrayList<JingLi>();
//每个集合存储自己的元素
cs.add(new ChuShi("张三", "后厨001"));
cs.add(new ChuShi("李四", "后厨002"));
fwy.add(new FuWuYuan("翠花", "服务部001"));
fwy.add(new FuWuYuan("酸菜", "服务部002"));
jl.add(new JingLi("小名", "董事会001", 123456789.32));
jl.add(new JingLi("小强", "董事会002", 123456789.33));
// ArrayList<String> arrayString = new ArrayList<String>();
iterator(jl);
iterator(fwy);
iterator(cs);
}
/*
* 定义方法,可以同时遍历3集合,遍历三个集合的同时,可以调用工作方法 work
* ? 通配符,迭代器it.next()方法取出来的是Object类型,怎么调用work方法
* 强制转换: it.next()=Object o ==> Employee
* 方法参数: 控制,可以传递Employee对象,也可以传递Employee的子类的对象
* 泛型的限定 本案例,父类固定Employee,但是子类可以无限?
* ? extends Employee 限制的是父类, 上限限定, 可以传递Employee,传递他的子类对象
* ? super Employee 限制的是子类, 下限限定, 可以传递Employee,传递他的父类对象
*/
public static void iterator(ArrayList<? extends Employee> array){
Iterator<? extends Employee> it = array.iterator();
while(it.hasNext()){
//获取出的next() 数据类型,是什么Employee
Employee e = it.next();
e.work();
}
}
List接口
它是一个元素存取有序的集合,通过索引就可以精确的操作集合中的元素,集合中可以有重复的元素,通过元素的equals方法,来比较是否为重复的元素,List接口有多个子类,比如ArrayList、LinkedList
Modifier and Type | Method and Description |
---|---|
boolean |
add(E e)
将指定的元素追加到此列表的末尾(可选操作)。
|
void |
add(int index, E element)
将指定的元素插入此列表中的指定位置(可选操作)。
|
boolean |
addAll(Collection extends E> c)
按指定集合的迭代器(可选操作)返回的顺序将指定集合中的所有元素附加到此列表的末尾。
|
boolean |
addAll(int index, Collection extends E> c)
将指定集合中的所有元素插入到此列表中的指定位置(可选操作)。
|
void |
clear()
从此列表中删除所有元素(可选操作)。
|
boolean |
contains(Object o)
如果此列表包含指定的元素,则返回
true 。
|
boolean |
containsAll(Collection> c)
如果此列表包含指定
集合的所有元素,则返回true。
|
boolean |
equals(Object o)
将指定的对象与此列表进行比较以获得相等性。
|
E |
get(int index)
返回此列表中指定位置的元素。
|
int |
hashCode()
返回此列表的哈希码值。
|
int |
indexOf(Object o)
返回此列表中指定元素的第一次出现的索引,如果此列表不包含元素,则返回-1。
|
boolean |
isEmpty()
如果此列表不包含元素,则返回
true 。
|
Iterator |
iterator()
以正确的顺序返回该列表中的元素的迭代器。
|
int |
lastIndexOf(Object o)
返回此列表中指定元素的最后一次出现的索引,如果此列表不包含元素,则返回-1。
|
ListIterator |
listIterator()
返回列表中的列表迭代器(按适当的顺序)。
|
ListIterator |
listIterator(int index)
从列表中的指定位置开始,返回列表中的元素(按正确顺序)的列表迭代器。
|
E |
remove(int index)
删除该列表中指定位置的元素(可选操作)。
|
boolean |
remove(Object o)
从列表中删除指定元素的第一个出现(如果存在)(可选操作)。
|
boolean |
removeAll(Collection> c)
从此列表中删除包含在指定集合中的所有元素(可选操作)。
|
default void |
replaceAll(UnaryOperator
将该列表的每个元素替换为将该运算符应用于该元素的结果。
|
boolean |
retainAll(Collection> c)
仅保留此列表中包含在指定集合中的元素(可选操作)。
|
E |
set(int index, E element)
用指定的元素(可选操作)替换此列表中指定位置的元素。
|
int |
size()
返回此列表中的元素数。
|
default void |
sort(Comparator super E> c)
使用随附的
Comparator 排序此列表来比较元素。
|
default Spliterator |
spliterator()
在此列表中的元素上创建一个 Spliterator 。
|
List |
subList(int fromIndex, int toIndex)
返回此列表中指定的
fromIndex (含)和
toIndex之间的视图。
|
Object[] |
toArray()
以正确的顺序(从第一个到最后一个元素)返回一个包含此列表中所有元素的数组。
|
|
toArray(T[] a)
以正确的顺序返回一个包含此列表中所有元素的数组(从第一个到最后一个元素);
返回的数组的运行时类型是指定数组的运行时类型。
|
add()方法 List接口方法
-
add(Object e) 向集合末尾处,添加指定的元素
-
add(int index, Object e) 向集合指定索引处,添加指定的元素,原有元素依次后移,带有索引的操作,防止越界问题
-
StringIndexOutOfBoundsException
-
IndexOutOfBoundsException
-
ArrayIndexOutOfBoundsException
List<String> list = new ArrayList<String>();
list.add("abc1");
list.add("abc2");
list.add("abc3");
list.add("abc4");
System.out.println(list);
list.add(1, "itcast");
System.out.println(list);
remove()方法 List接口方法
- remove(Object e) 将指定元素对象,从集合中删除,返回值为被删除的元素
- remove(int index) 将指定索引处的元素,从集合中删除,返回值为被删除的元素
List<Double> list = new ArrayList<Double>();
list.add(1.1);
list.add(1.2);
list.add(1.3);
list.add(1.4);
Double d = list.remove(0);
System.out.println(d);
System.out.println(list);
set()方法 List接口方法
- set(int index, Object e) 将指定索引处的元素,替换成指定的元素,返回值为替换前的元素
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
Integer i = list.set(0, 5);
System.out.println(i);
System.out.println(list);
get()方法 List接口方法
- get(int index) 获取指定索引处的元素,并返回该元素
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
System.out.println(list.get(1));
removeFirst()方法和removeLast()方法 LinkedList特有方法
LinkedList<String> link = new LinkedList<String>();
link.add("1");
link.add("2");
link.add("3");
link.add("4");
String first = link.removeFirst();
String last = link.removeLast();
System.out.println(first);
System.out.println(last);
System.out.println(link);
getFirst()方法和getLast()方法 LinkedList特有方法
LinkedList<String> link = new LinkedList<String>();
link.add("1");
link.add("2");
link.add("3");
link.add("4");
if(!link.isEmpty()){
String first = link.getFirst();
String last = link.getLast();
System.out.println(first);
System.out.println(last);
}
addFirst()方法和addLast()方法 LinkedList特有方法
LinkedList<String> link = new LinkedList<String>();
link.addLast("heima");
link.add("abc");
link.add("bcd");
link.addFirst("itcast");
System.out.println(link);
Set接口
它是个不包含重复元素的集合,取出元素的方式可以采用:迭代器、增强for ,Set接口有多个子类,比如HashSet、LinkedHashSet
存取原理
- 首先调用本类的hashCode()方法算出哈希值
- 在容器中找是否与新元素哈希值相同的老元素,如果没有直接存入,如果有转到第三步
- 新元素会与该索引位置下的老元素利用equals方法一一对比,一旦新元素.equals(老元素)返回true,停止对比,说明重复,不再存入,如果与该索引位置下的老元素都通过equals方法对比返回false,说明没有重复,存入
Modifier and Type | Method and Description |
---|---|
boolean |
add(E e)
如果指定的元素不存在,则将其指定的元素添加(可选操作)。
|
boolean |
addAll(Collection extends E> c)
将指定集合中的所有元素添加到此集合(如果尚未存在)(可选操作)。
|
void |
clear()
从此集合中删除所有元素(可选操作)。
|
boolean |
contains(Object o)
如果此集合包含指定的元素,则返回
true 。
|
boolean |
containsAll(Collection> c)
返回
true如果此集合包含所有指定集合的元素。
|
boolean |
equals(Object o)
将指定的对象与此集合进行比较以实现相等。
|
int |
hashCode()
返回此集合的哈希码值。
|
boolean |
isEmpty()
如果此集合不包含元素,则返回
true 。
|
Iterator |
iterator()
返回此集合中元素的迭代器。
|
boolean |
remove(Object o)
如果存在,则从该集合中删除指定的元素(可选操作)。
|
boolean |
removeAll(Collection> c)
从此集合中删除指定集合中包含的所有元素(可选操作)。
|
boolean |
retainAll(Collection> c)
仅保留该集合中包含在指定集合中的元素(可选操作)。
|
int |
size()
返回此集合中的元素数(其基数)。
|
default Spliterator |
spliterator()
在此集合中的元素上创建一个
Spliterator 。
|
Object[] |
toArray()
返回一个包含此集合中所有元素的数组。
|
|
toArray(T[] a)
返回一个包含此集合中所有元素的数组;
返回的数组的运行时类型是指定数组的运行时类型。
|
add()方法 Set接口方法
- 无序集合,存储和取出的顺序不同,没有索引,不存储重复元素,代码的编写上,和ArrayList完全一致
Set<String> set = new HashSet<String>();
set.add("cn");
set.add("heima");
set.add("java");
set.add("java");
set.add("itcast");
Iterator<String> it = set.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
System.out.println("==============");
for(String s : set){
System.out.println(s);
}
hashCode()方法 Set接口方法
- 对象的哈希值,普通的十进制整数 父类Object,方法 public int hashCode() 计算结果int整数
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1.hashCode());
System.out.println(s2.hashCode());
/*System.out.println("重地".hashCode());
System.out.println("通话".hashCode());*/
String类重写hashCode()方法
public int hashCode() {
int h = hash;//hash初值为0
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
哈希表的存储自定义对象
底层数据结构,哈希表,存储,取出都比较快,线程不安全,运行速度快
每个对象的地址值都不同,调用Obejct类的hashCode方法返回不同哈希值,直接存入,如果重写了hashCode和equals就可以防止相同的对象存入
public static void main(String[] args) {
//将Person对象中的姓名,年龄,相同数据,看作同一个对象
//判断对象是否重复,依赖对象自己的方法 hashCode,equals
HashSet<Person> setPerson = new HashSet<Person>();
setPerson.add(new Person("a",11));
setPerson.add(new Person("b",10));
setPerson.add(new Person("b",10));
setPerson.add(new Person("c",25));
setPerson.add(new Person("d",19));
setPerson.add(new Person("e",17));
System.out.println(setPerson);
}
public class Person {
private String name;
private int age;
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 Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Person(){}
public String toString(){
return name+".."+age;
}
}
自定义对象重写hashCode和equals
public static void main(String[] args) {
//将Person对象中的姓名,年龄,相同数据,看作同一个对象
//判断对象是否重复,依赖对象自己的方法 hashCode,equals
HashSet<Person> setPerson = new HashSet<Person>();
setPerson.add(new Person("a",11));
setPerson.add(new Person("b",10));
setPerson.add(new Person("b",10));
setPerson.add(new Person("c",25));
setPerson.add(new Person("d",19));
setPerson.add(new Person("e",17));
System.out.println(setPerson);
}
public class Person {
private String name;
private int age;
public int hashCode(){
return name.hashCode()+age*55;
}
//方法equals重写父类,保证和父类相同
public boolean equals(Object obj){
if(this == obj)
return true;
if(obj == null)
return false;
if(obj instanceof Person){
Person p = (Person)obj;
return name.equals(p.name) && age==p.age;
}
return false;
}
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 Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Person(){}
public String toString(){
return name+".."+age;
}
}
ArrayList,HashSet判断对象是否重复的原因
ArrayList的contains方法原理:底层依赖于equals方法
ArrayList的contains方法会使用调用方法时,
传入的元素的equals方法依次与集合中的旧元素所比较,
从而根据返回的布尔值判断是否有重复元素。
此时,当ArrayList存放自定义类型时,由于自定义类型在未重写equals方法前,
判断是否重复的依据是地址值,所以如果想根据内容判断是否为重复元素,需要重写元素的equals方法。
HashSet的add()方法和contains方法()底层都依赖 hashCode()方法与equals方法()
Set集合不能存放重复元素,其添加方法在添加时会判断是否有重复元素,有重复不添加,没重复则添加。
HashSet集合由于是无序的,其判断唯一的依据是元素类型的hashCode与equals方法的返回结果。规则如下:
先判断新元素与集合内已经有的旧元素的HashCode值
- 如果不同,说明是不同元素,添加到集合。
- 如果相同,再判断equals比较结果。返回true则相同元素;返回false则不同元素,添加到集合。
所以,使用HashSet存储自定义类型,如果没有重写该类的hashCode与equals方法,则判断重复时,使用的是地址值,如果想通过内容比较元素是否相同,需要重写该元素类的hashcode与equals方法。
0 条评论