目录
3.2 为什么Hashse存放自定义类型元素时,要重写对象中的hashCode和equals方法,才能保证元素唯一。
一.List接口:
1.1 List集合概述:
List接口继承自collection接口,我们称实现List接口的对象称之为List集合,在List集合中可以出现重复元素,所有的元素是以线性的存储结构存储。List集合具有一下特点:
1.元素存取有序,即取出元素和存入元素的顺序一致。(线性的存储结构注定他的存取方式)
2.List集合带有索引,跟数组索引一样,可以通过索引找到并且操作List集合中的每一个元素。索引0是第一个元素,索引1是第二个元素,以 此类推.....。
3.List集合中的元素允许重复,可以使用equal方法比较两个元素是否相等。
List接口有两个实现子类arrayList和linkedlist,他们的方法都是来自List中已经定义好的,子类本身不需要额外定义(其他额外需求除外)
1.2 ArrayList集合:
1.2.1 ArrayList概述:
1.arrayList集合是list接口的实现子类,继承了list中的方法,并且子类没有其他的太多的改动。
2.arrayList集合数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据、遍历数据,所以arrayList是最常用的集合。
3.ArrayList底层是由数组实现的,核心的是数组扩容机制,他的长度是可以改变的,并且存储的是引用数据类型的数据。但是也导致一些缺点的出现它可以实现对集合元素的随机访问,但是集合中的元素的移除和插入效率要比另一个实现类低很多,因为他是要通过遍历集合找到元素之后,才插入对应的位置。
1.2.2 ArrayList使用以及常用方法:
Arrarylist位于Java.util包下,系统不会自动导包,所以我们需要手动导入:
java.util.ArrayList;
Arraylist类实例化成对象,调用方法:
ArrayList<> arrayList = new ArrayList<>();//实例化对象
Arraylist常用方法:
Arrarylist是list接口的子类,list继承了collection接口所有的方法,同时根据List本身的索引特点增加几个方法:
public void add(int index, E element)
: 将指定的元素,添加到该集合中的指定位置上。
public E get(int index)
:返回集合中指定位置的元素。
public E remove(int index)
: 移除列表中指定位置的元素, 返回的是被移除的元素。
public E set(int index, E element)
:用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
代码演示:
import java.util.ArrayList;
import java.util.LinkedList;
public class Shangkelist {
public static void main(String[] args) {
/*
*/
ArrayList arrayList = new ArrayList();//实例化对象
int sizestart=arrayList.size();
System.out.println("开始的arraylist长度:"+sizestart);
arrayList.add(20);//增加元素
arrayList.add("20");
arrayList.add("ob");
arrayList.add(("20"));
arrayList.add(1,"200");
int listend=arrayList.size();
System.out.println("结束的长度"+listend);
boolean res=arrayList.remove("20");
System.out.println(res);
System.out.println("=====");
Object str1=arrayList.get(2);//获取元素
// arrayList.remove(0);
System.out.println(str1);
for(Object str:arrayList){
// System.out.println(arrayList);
System.out.println(str);
}
注意点:
1.得到集合长度,能更好的根据索引处理集合元素,而得到集合长度的方法不是之前的length()方法,而是size()方法。
2. remove方法不仅可以通过索引删除元素,也可以通过指定对象名删除元素(出现多个相同的元素时会先删除第一个重复的元素)
3.add方法可以指定要加的元素位置和元素,加入到对应的位置(不只是通过加对象名,加在集合最后面)。
1.3 linkedList集合:
1.3.1 linkedlist集合概述:
1.linkedList是Java.util包下,是list接口的实现子类
2.linkedList集合数据存储的结构是双向链表链表结构。方便元素添加、删除的集合。
3.linkedlist集合和arraylist集合不同的是:arraylist是线性的存储结构,linkedlist是一个双向链表存储结构,这造成两者的优缺点不同,linkedlist对顺序访问进行了优化,向List中间插入与删除的开销并不大。随机访问则相对较慢,合适用于元素插入和删除
1.3.2 linkedlist集合的使用以及常用方法:
linkedlist集合和arraylist使用差不多,都是先导入包,实例化对象,调用方法。
Linkedlist常用方法:
public void addFirst(E e)
:将指定元素插入此列表的开头。
public void addLast(E e)
:将指定元素添加到此列表的结尾。
public E getFirst()
:返回此列表的第一个元素。
public E getLast()
:返回此列表的最后一个元素。
public E removeFirst()
:移除并返回此列表的第一个元素。
public E removeLast()
:移除并返回此列表的最后一个元素。
public E pop()
:从此列表所表示的堆栈处弹出一个元素。
public void push(E e)
:将元素推入此列表所表示的堆栈。
public boolean isEmpty()
:如果列表不包含元素,则返回true
注意点:
1.pop方法弹出的元素是堆栈中第一个元素,实际上就是调用removeFirst方法
2.push()方法压入的位置是栈堆第一个位置
3.这些都是linkedlist的特有方法,其他不如add,get等方法都是通用的。
4.linkedlist允许元素重复的。
代码实例:
/*
验证LinkedList的方法
1.允许元素重复
*/
LinkedList link = new LinkedList();
//添加元素
int sizestar=link.size();
System.out.println("开始长度长度为"+sizestar);
link.add("20");
link.add("20");
link.add("30");
link.add("40");
//添加元素到指定位置
link.addFirst(1);//第一个位置
link.addLast(20);
/*
pop和push都是操作第一个元素
*/
//将这个元素推入栈中
link.push("推入元素1");
//指定索引,移除元素
link.remove(1);
//弹出元素:弹出的元素是个对象
Object res1=link.pop();
System.out.println("弹出元素1:"+res);
int a=link.hashCode();
//结束长度
int size2=link.size();
System.out.println("长度为"+size2);
System.out.println("哈希值:");
//循环输出link元素
for(int i=0;i<link.size();i++){
System.out.println(link.get(i));
}
}
二.Set接口:
2.1 Set接口概述:
1.set接口和list接口都是collection接口的子接口,set接口的方法比collection接口的方法没有什么补充,方法基本一致。但是和list方法有很大的一个不同,就是set集合不允许元素重复并且元素是存取顺序不一定一致,而list方法元素可以重复。
2..set接口有三个实现子类:
TreeSet
,LinkedHashSet,
HashSet。
注意点:
set接口是不允许元素重复的,但是重复不会报错,只会输出第一个重复的值。
2.2 Hashset集合概述:
1.Hashset是set接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的,他的底层支持是Java.util.Map
2.Hashset是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存储和查找性能。保证元素唯一性的方式依赖于:Hashcode方法和equals方法。
为什么他的元素存取都是无序的?看代码:
public static void main(String[] args) {
//不能重复,就算有重复的对像,只会存储一个。
//无序的
HashSet<String> set = new HashSet<>();
set.add(new String("20"));
set.add("abc");
set.add("abd");
set.add("aba");
set.add("abc");
//遍历输出
for (String str:set) {
System.out.println(str);
}
}
运行结果:
可以看到他的输出结果和我们的存入顺序不一致。
为什么他的元素不允许重复呢?看代码:
import java.util.HashSet;
public class OopshangkeSet {
public static void main(String[] args) {
//不能重复,就算有重复的对像,只会存储一个。
//无序的
HashSet<String> set = new HashSet<>();
set.add(new String("20"));
set.add("abc");
set.add("abc");
set.add("abc");
set.add("abc");
set.add("abd");
set.add("aba");
set.add("abc");
//遍历输出
for (String str:set) {
System.out.println(str);
}
}
}
运行结果:
可以看到他的只会输出一个重复的元素。其他的都不会输出,说明其他的重复的元素都没有被存储。
这个不允许重复和无序输出具体原因写在文章最后
2.3 Hashset存储自定义元素:
给HashSet中存放自定义类型元素时,需要重写对象中的hashCode和equals方法,建立自己的比较方式,才能保证HashSet集合中的对象唯一.
代码演示:
重写hashcode和equals方法
*/
import java.util.Objects;
public class Oopset {
private int age;
private String name;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Oopset)) return false;
Oopset oopset = (Oopset) o;
return getAge() == oopset.getAge() &&
getName().equals(oopset.getName());
}
@Override
public int hashCode() {
return Objects.hash(getAge(), getName());
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Oopset{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
public Oopset(int age, String name) {
this.age = age;
this.name = name;
}
}
测试类:
import java.util.HashSet;
import java.util.Objects;
public class OopsetTest {
public static void main(String[] args) {
HashSet<Oopset> hashSet = new HashSet<Oopset>();
// Oopset student = new Oopset(20, "小强");
// Oopset student1 = new Oopset(21, "小张");
// Oopset student2 = new Oopset(21, "小张");
// Oopset student3 = new Oopset(24, "小李");
hashSet.add(new Oopset(20, "小强"));
hashSet.add(new Oopset(21, "小张"));
hashSet.add(new Oopset(21, "小张"));
hashSet.add(new Oopset(24, "小李"));
for (Oopset stu:hashSet) {
System.out.println(stu);
}
}
}
2.4:linkedHashset概述:
我们知道HashSet保证元素唯一,可是元素存放进去是没有顺序的,那么我们要保证有序
在HashSet下面有一个子类
java.util.LinkedHashSet
,它是链表和哈希表组合的一个数据存储结构。可以保证存放的元素有顺序。
三.集合部分问题总结以及解决:
3.1. 什么是hashcode
hashcode是通过hash函数也就是某种算法得到的,而hashcode是在hash表中的对应位置,每个对象都有他的hashcode,而一个对象肯定有物理地址,对象的物理地址跟这个hashcode地址不一样,hashcode代表对象的地址说的是对象在hash表中的位置,物理地址说的对象存放在内存中的地址,
那么对象如何得到hashcode呢?通过对象的内部地址(也就是物理地址)转换成一个整数,然后该整数通过hash函数的算法就得到了hashcode
3.2 为什么Hashse存放自定义类型元素时,要重写对象中的hashCode和equals方法,才能保证元素唯一。
Hashset是如何判断元素相同呢?
1.先通过hashcode计算出元素的哈希值,如果有已经存在的hashcode与它相同,那么视为相同元素,执行第二步操作。如果不相同,说明元素不相同,添加到集合中。
2.当哈希值相同时,调用equals方法比较两个元素属性是否相同,相同不添加元素,不相同添加到集合中。
测试:
public class HashsetTest {
public static void main(String[] args) {
HashSet hashSet = new HashSet();
hashSet.add(new Student("小李"));
hashSet.add(new Student("小李"));
int a=hashSet.size();
System.out.println("元素个数为:"+a);
}
}
class Student{
int id;
String name;
}
输出结果:
说明两个元素都添加进去了。为什么呢?
回到hashset是如何判断元素相同的,首先我是分别传入两个相同属性的对象——对象相同,地址值不同。所以hashcode不同,判断机制就会返回false判断他们两个不同,这样集合中就存入了两个同一类的相同对象。所以我们要重写hashcode方法,把判断指向他的属性(名字),这个时候就是获取到属性值name对应的hashcode,而不是地址值的hashcode。
@Override public int hashCode() { return this.hash(name); }@Override public int hashCode() { return this.hash(name); }
由于返回的true,进入第二步调用equals方法,我们自定义的类中是没有equals方法的,调用的是Objects类中的equals方法,而它比较的也是地址值,所以我们也要重写equals方法。
@Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Student)) return false; Student student = (Student) o; return name.equals(student.name); }
重写完之后,比较的是对应的属性的内容。最终才判断是否是同一个元素。
注意点:
Object里的equals比较的是对象的地址,hashcode也是根据地址计算的。重写之后hashcode比较的是属性,equals比较的也是属性
equal()比较的是内容相等,==比较的是全等(内容以及地址相等),hahscode比较的是地址(hash表中的地址)相等。
3.3 List集合扩容机制:
明天补充