文章目录
- 1.基本介绍
- 2.集合的框架体系(单列、双列)
- 单列集合
- 双列集合
- 比较
- 3.Collection接口和常用方法
- 1.Collection接口实现类的特点
- 2.常用方法(使用ArrayList演示)
- 代码
- 结果
- 3.迭代器遍历
- 基本介绍
- 代码
- 结果
- 4.增强for循环遍历
- 代码
- 结果
- 5.对于方法返回的理解
- 返回类型是基本数据类型
- 返回类型是引用类型
- 6.课堂测试
- 题目
- 代码
- 答案
- 4.List接口和常用方法
- 基本介绍
- 1.常用方法
- 代码
- 结果
- 2.课堂练习
- 题目
- 代码
- 结果
- 3.List三种遍历方式
- 代码
- 结果
- 4.List课堂练习
- 题目
- 代码
- 结果
- 5.ArrayList类
- 1.注意事项
- 2.ArrayList扩容机制
- 结论
- 解析源码(使用无参构造器来创建和使用ArrayList)
- 6.Vector类
- 1.注意事项
- 2.Vector扩容机制
- 结论
- 解析源码
- 3.ArrayList和Vector比较
- 7.LinkedList类
- 1.LinkedList简要说明
- 2.LinkedList底层操作机制
- 3.LinkedList底层源码
- add方法底层源码
- 4.ArrayList和LinkedList比较
- 8.Set接口
- 1.基本介绍
- 2.常用方法
- 代码
- 结果
- 3.全面介绍
- 代码实例
- 结果
- 4.HashSet底层机制
- 结论
- HashSet添加机制
- 练习题目1
- **答案**
- 思考题
- 答案
- 9.LinkedHashSet类
- 1.基本介绍
- 2.LinkedHashSet底层机制
- 3.练习
- 题目
- 代码
- 结果
1.基本介绍
2.集合的框架体系(单列、双列)
单列集合
双列集合
比较
3.Collection接口和常用方法
1.Collection接口实现类的特点
- Collection实现子类可以存放多个对象,如果是基本数据类型,则会自动装箱
- 有些Collection的实现类,可以存放重复的元素,有些不可以
- 有些Collection的实现类,有些是有序的(List),有些是无序的(Set)
- Collection接口没有直接的实现子类,是通过它的子接口Set和List来实现的
2.常用方法(使用ArrayList演示)
代码
package collection;
import java.util.ArrayList;
import java.util.List;
/**
* @author 孙显圣
* @version 1.0
*/
public class CollectionMethod {
public static void main(String[] args) {
List list = new ArrayList(); //这里使用List接收
// add 添加单个元素
list.add("jack");
list.add(10); //这里会自动装箱,转换为Integer
list.add(true);
// remove 删除指定元素
list.remove("jack"); //这里是删除指定对象,返回的是一个boolean类型
list.remove(0); //这里是删除指定索引的内容,返回的是删除的那个元素的值
System.out.println(list);
list.add("jack");
list.add(10); //这里会自动装箱,转换为Integer
// contain 查找元素是否存在
System.out.println(list.contains("jack"));
// size 获取元素个数
System.out.println(list.size());
// isEmpty 判断是否为空
System.out.println(list.isEmpty());
// clear 清空
list.clear();
System.out.println(list);
// addAll 添加多个元素,也就是添加一个集合
ArrayList list1 = new ArrayList();
list1.add("红楼梦");
list1.add("三国演义");
list1.add("水浒传");
list.addAll(list1);
System.out.println("添加后的集合:" + list);
// removeAll 删除多个元素,也就是删除一个集合的所有元素
ArrayList list2 = new ArrayList();
list2.add("水浒传");
list2.add("三国演义");
list.removeAll(list2);
System.out.println(list);
// 查找多个元素是否都存在
System.out.println(list.containsAll(list2));
}
}
结果
3.迭代器遍历
基本介绍
- Iterator对象称为迭代器,主要用于遍历Collection集合中的元素
- 所有实现了Collection接口的集合类都有一个iterator()方法,用于返回一个实现了Iterator接口的对象,即可以返回一个迭代器
- Iterator的结构
- Iterator仅用于集合遍历,Iterator本身不存放对象
代码
package collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
* @author 孙显圣
* @version 1.0
*/
public class CollectionIterator {
public static void main(String[] args) {
Collection col = new ArrayList();
//添加三条数据
col.add(new Book("三国演义", "罗贯中", 10.1));
col.add(new Book("红楼梦", "曹雪芹", 10.2));
col.add(new Book("西游记", "吴承恩", 10.3));
//获取迭代器对象
Iterator iterator = col.iterator();
//使用itit快捷键
while (iterator.hasNext()) {
Object next = iterator.next(); //这个next方法返回的是一个Object类型的引用,指向的是具体的对象
System.out.println(next); //输出会默认调用toString方法,根据动态绑定,调用的是运行类型即Book类型的toString方法
}
//如果要重新遍历,需要重新获取迭代器,因为此时的迭代器已经指向最后一个元素了
iterator.next(); //会报错
}
}
class Book {
private String name;
private String author;
private double price;
public Book(String name, String author, double price) {
this.name = name;
this.author = author;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
//输出详细信息
return "Book{" +
"name='" + name + '\'' +
", author='" + author + '\'' +
", price=" + price +
'}';
}
}
结果
4.增强for循环遍历
代码
package collection;
import java.util.ArrayList;
/**
* @author 孙显圣
* @version 1.0
*/
public class CollectionFor {
public static void main(String[] args) {
ArrayList list = new ArrayList();
//添加三条数据
list.add(new Book("三国演义", "罗贯中", 10.1));
list.add(new Book("红楼梦", "曹雪芹", 10.2));
list.add(new Book("西游记", "吴承恩", 10.3));
//使用增强for循环遍历,快捷键是 I
for (Object o : list) {
System.out.println(o);
}
//底层还是调用的迭代器,可以理解为简化版本的迭代器
}
}
结果
5.对于方法返回的理解
返回类型是基本数据类型
public int add(int a, int b) {
int sum = a + b;
return sum; // 这里返回的是sum的值
}
//此时如果调用这个方法,x.add(1,2)则这个东西就是sum的值
返回类型是引用类型
public class MyCustomClass {
private int value;
public MyCustomClass(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
class MyClass {
public Object createCustomObject(int value) {
MyCustomClass customObj = new MyCustomClass(value);
return customObj; // 返回的是MyCustomClass类型的对象引用
}
}
//在这里如果调用这个方法, x.createCustomObject(3)则代表的是一个Object类型的引用,指向的是返回的customObj这个实例
6.课堂测试
题目
代码
package collection;
import java.util.ArrayList;
import java.util.Iterator;
/**
* @author 孙显圣
* @version 1.0
*/
public class CollectionExercise {
public static void main(String[] args) {
ArrayList list = new ArrayList();
//添加三条数据
list.add(new Dog("小黑", 3));
list.add(new Dog("小白", 4));
list.add(new Dog("小黄", 5));
//迭代器遍历
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
//增强for遍历
for (Object o : list) {
System.out.println(o);
}
}
}
class Dog {
private String name;
private int age;
public Dog(String name, int age) {
this.name = name;
this.age = 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;
}
@Override
public String toString() {
//输出格式为:名字:xxx,年龄:xxx
return "名字:" + name + ",年龄:" + age;
}
}
答案
4.List接口和常用方法
基本介绍
- List接口是Collection接口的子接口
- List集合类中元素有序,并且可以重复
- List集合中的每个元素都有其对应的顺序索引,即支持索引
1.常用方法
代码
package collection;
import java.util.ArrayList;
import java.util.List;
/**
* @author 孙显圣
* @version 1.0
*/
public class ListMethod {
public static void main(String[] args) {
List list = new ArrayList();
// add 添加元素
list.add("张三丰");
list.add("贾宝玉");
// void add(int index, Object ele) 指定位置添加元素
list.add(1,"韩顺平");
System.out.println(list);
// boolean addAll(int index, Collection eles) 指定位置添加多个元素
ArrayList list1 = new ArrayList();
list1.add("jack");
list1.add("tom");
list.addAll(1,list1);
System.out.println(list);
// Object get(int index) 获取指定位置的元素
System.out.println(list.get(1));
// int indexOf(Object obj) 返回obj在集合中首次出现的位置
System.out.println(list.indexOf("tom"));
// int lastIndexOf(Object obj) 返回obj在当前集合中末次出现的位置
list.add("tom");
System.out.println(list.lastIndexOf("tom"));
// Object remove(int index) 移除指定index位置的元素,并返回此元素
list.remove(0);
System.out.println(list);
// Object set(int index, Object ele) 设置指定位置的元素,相当于替换
list.set(2, "jack");
System.out.println(list);
//List subList(int fromIndex, int toIndex) 返回从fromIndex到toIndex - 1位置的子集合
System.out.println(list.subList(0, 4));
}
}
结果
2.课堂练习
题目
代码
package collection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* @author 孙显圣
* @version 1.0
*/
public class ListExercise {
public static void main(String[] args) {
List list = new ArrayList();
//添加十个元素,添加字符串
for (int i = 0; i < 10; i++) {
list.add("str" + i);
}
list.add(1,"韩顺平教育");
System.out.println(list.get(4));
list.remove(5);
list.set(6,"已修改");
Iterator iterator = list.iterator(); //获取迭代器
while (iterator.hasNext()) { //判断是否有下一个元素
Object next = iterator.next(); //如果有下一个元素,则向下移动,并取出下一个元素
System.out.print(next + " "); //这里调用了String类型的toString,打印字符串的内容
}
}
}
结果
3.List三种遍历方式
代码
package collection;
import java.util.ArrayList;
import java.util.Iterator;
/**
* @author 孙显圣
* @version 1.0
*/
public class ListFor {
public static void main(String[] args) {
ArrayList list = new ArrayList();
//添加五条数据
list.add(new Book("三国演义", "罗贯中", 10.1));
list.add(new Book("红楼梦", "曹雪芹", 10.2));
list.add(new Book("西游记", "吴承恩", 10.3));
list.add(new Book("水浒传", "施耐庵", 10.4));
list.add(new Book("三国演义", "罗贯中", 10.1));
//1.迭代器遍历
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
//2.增强for
for (Object o : list) {
System.out.println(o);
}
//3.普通for
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
结果
4.List课堂练习
题目
代码
package collection;
import java.util.ArrayList;
import java.util.List;
/**
* @author 孙显圣
* @version 1.0
*/
public class ListExercise02 {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add(new Book("三国演义", "罗贯中", 12.1));
list.add(new Book("红楼梦", "曹雪芹", 10.2));
list.add(new Book("西游记", "吴承恩", 11.3));
sort(list);
System.out.println(list);
}
public static void sort(List list) {
for (int i = list.size() - 1; i > 0 ; i--) {
for (int j = 1; j <= i ; j++) {
Book o1 = (Book) list.get(j); // o1指向的是j位置所指向的Book对象
Book o2 = (Book) list.get(j - 1); // o2指向的是j - 1位置所指向的Book对象
if (o1.getPrice() < o2.getPrice()) { //交换
list.set(j, o2); //这样j位置存储的就是原来j - 1位置存储的Book对象
list.set(j - 1, o1); //j - 1位置存储的就是原来j位置存储的book对象
}
}
}
}
}
结果
5.ArrayList类
1.注意事项
- ArrayList可以存储任何类型的数据,还可以存储多个null
- ArrayList是由数组来实现数据存储的
- ArrayList基本等同于Vector,除了ArrayList是线程不安全的,多线程不建议使用ArrayList
2.ArrayList扩容机制
结论
- ArrayList中维护了一个Object类型的数组elementData.
transient Object[] elementData
,这里的transient表示该属性不会被序列化 - 当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第一次添加,则扩容elementData为10,如需再次扩容则扩容elementData为1.5倍
- 如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍
解析源码(使用无参构造器来创建和使用ArrayList)
- 打断点,debug,跳入
- 下一步,可以看出,创建了一个空的elementData
- 跳出,下一步,再下一步,到了add方法
- 跳入,进入了Integer将1进行装箱
- 跳出,再跳入,进入add方法,先确认是否要扩容,然后再执行赋值操作
- 跳入,可以发现里面调用了calculateCapacity函数,这个函数返回最小容量,如果是第一次,即elementData是空的则返回10和minCapacity = 1的最大值,也就是10
- 跳入两次
- 下一步,如果最小容量大于目前长度,则需要扩容了
- 下一步,跳入,可以看出是先将当前的长度赋值给旧的容量
- 下一步,新的容量扩容到了旧容量的1.5倍,但是注意,第一次的旧容量是0,所以1.5倍还是0
- 下一步,进入判断语句,发现新容量(0)还是小于最小容量(10),再下一步,新容量变成了最小容量10
- 再下两步,因为不满足第二个判断条件,这次将elementData扩容到新容量的大小,后面没有值的会是空
- 下一步,查看数组,是十个空的
- 一直跳出,直到返回到add,此时的数组已经有容量了
- 下一步,发现这个数组已经有内容了
- 第二次扩容,直接在第二个循环打断点
- 下一步
- 跳入,跳出,再跳入(中间的步骤是用来装箱的)
- 跳入,此时调用calculateCapacity这个方法,直接返回最小容量11
- 跳入两次,进入ensureExplicitCapacity
- 下一步,发现最小容量大于目前数组的长度,所以需要扩容
- 下一步,到grow函数,再跳入,开始进行数组扩容
- 下两步,发现新数组容量15大于最小数组容量11
- 下两步,到了数据扩容部分,再下一步数组扩容完毕,变成15个位置
- 一直跳出到add函数,此时数组已经有位置了
- 下一步,则会将元素赋值给数组的第11个位置,剩下的为null
6.Vector类
1.注意事项
- Vector类的定义
- Vector底层也是一个对象数组,protected Object[] elementData
- Vector是线程同步的,即线程安全,Vector类的操作方法带有synchronized
- 在开发中,需要线程同步安全时,考虑使用Vector
2.Vector扩容机制
结论
- 如果是无参,默认10,满了之后就按两倍扩容
- 如果是指定大小则每次按照两倍扩容
解析源码
- 打断点
- 两次跳入,发现无参构造方法,调用了有参的构造方法,并将值设为10,此时elementData的长度就被设置为10
- 一直下一步,直到进行到add方法
- 跳入,跳出然后再跳入(这一步是进行的自动装箱),然后下一步,这里是将元素数量加一并作为参数
- 跳入,发现最小容量(1)小于elementData的长度,所以不用扩容
- 两次下一步,此时如果再执行就会为elementData数组赋值了
- 一直下一步,直到执行到这里
- 跳入,跳出,再跳入
- 下一步,然后跳入这个方法,现在最小容量是大于这个数组的容量的,所以需要扩容
- 下一步,然后跳入,进入grow函数。这句话是将数组长度赋值给旧的容量
- 下一步,这句话是将新的容量设置为旧的容量的两倍
- 一直下一步,直到这里进行了数组扩容,此时的数组长度是20
- 一直下一步到add,这句话就会将元素添加给给这个数组
- 下一步,再查看数组容量就是20了
3.ArrayList和Vector比较
7.LinkedList类
1.LinkedList简要说明
2.LinkedList底层操作机制
- LinkedList底层维护了一个双向链表
- LinkedList中维护了两个属性,first和last分别指向首节点和尾节点
- 每个节点(Node对象),里面又维护了prev、next、item三个属性,其中通过prev指向前一个节点,通过next指向后一个节点,item用来存储数据,最终实现双向链表
- 所以LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高
3.LinkedList底层源码
add方法底层源码
- 打断点
- 跳入,发现就是调用了一个LinkedList的无参构造方法,然后下一步,使用size记录节点数量0,first指向null,last指向null
- 下一步,到了构造方法的最后,因为刚才是完成了普通初始化,接下来才是构造方法
- 下一步,会回到创建对象的语句,一直下一步,直到add方法
- 跳入,跳出,然后再跳入(这里进行了自动装箱),则进入了add方法
- 跳入,此时的l指向last,也就是null
- 下一步创建一个节点,prev赋值为last即是null,item赋值为e(要添加的数据0),next赋值为null
- 下一步,last指向新节点
- 下一步,l是null的,所以first也指向新节点
- 下一步,size++
- 一直下一步到第二个add
- 跳入,跳出,然后再跳入,到了add方法
- 跳入,l指向last就是刚才的节点,下一步,创建新节点,prev指向刚才的节点,item赋值为e(要添加的数据),next赋值为null
- 下一步,last指向新节点,first不变
- 下一步,由于l不是null,所以l.next指向新的节点,也就是将刚才的节点的next指向新节点
- 下一步,size ++
- 一直下一步到第三个add执行之前,发现已经添加进数据1了
4.ArrayList和LinkedList比较
8.Set接口
1.基本介绍
- 无序(添加和取出的顺序不一致),没有索引
- 虽然取出顺序不一致,但是一旦第一次以一种顺序取出,第二次取出还是那个顺序
- 不允许重复元素,所以最多包含一个null
- 遍历方式不可以使用普通循环(因为没有索引),只能使用迭代器和增强for循环
2.常用方法
代码
package collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* @author 孙显圣
* @version 1.0
*/
public class SetMethod {
public static void main(String[] args) {
Set hashSet = new HashSet();
//添加五条数据
hashSet.add("cat");
hashSet.add("dog");
hashSet.add("pig");
hashSet.add("mouse");
hashSet.add("cat");
//1.迭代器遍历
Iterator iterator = hashSet.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
//增强for遍历
for (Object o : hashSet) {
System.out.println(o);
}
}
}
结果
3.全面介绍
- HashSet实现了Set接口
- HashSet的构造器调用了HashMap
- 可以存放null值,但是只能有一个null值
- HashSet不保证元素是有序的
- 不能有重复元素/对象
代码实例
package collection;
import java.util.HashSet;
/**
* @author 孙显圣
* @version 1.0
*/
public class HashSet01 {
public static void main(String[] args) {
HashSet hashSet = new HashSet();
//添加五条数据
//在添加数据的时候会赶回true或者false
System.out.println(hashSet.add("cat"));
System.out.println(hashSet.add("dog"));
System.out.println(hashSet.add("pig"));
System.out.println(hashSet.add("mouse"));
System.out.println(hashSet.add("cat")); //F
//删除数据
hashSet.remove("pig");
System.out.println(hashSet);
HashSet set = new HashSet();
set.add("luck");
set.add("luck"); //F
set.add(new Dog("tom",3));
set.add(new Dog("tom",3)); //这是两个自定义对象,是不同的所以添加成功
System.out.println("set = " + set);
System.out.println(set.add(new String("lihua")));
System.out.println(set.add(new String("lihua"))); //F,因为数据都是常量池的那一个,所以添加失败
}
}
结果
4.HashSet底层机制
结论
HashSet添加机制
- 首先调用一个实例的hashcode方法,根据某种算法计算出一个hash值(但是如果两个实例的hashcode值是相同的则计算出来的值也一定相同),就意味着他们一定会在同一行
- 根据这个值来计算索引值
- 如果索引的位置为空则直接添加,如果不是空的,则依次与这行的每个数据调用equals方法来比较是否相同,如果相同则添加失败
练习题目1
答案
package collection;
import java.util.HashSet;
import java.util.Objects;
/**
* @author 孙显圣
* @version 1.0
*/
public class HashSetExercise {
public static void main(String[] args) {
HashSet hashSet = new HashSet();
//创建两个name和age相同的Employee实例
Employee employee1 = new Employee("jack", 200);
Employee employee2 = new Employee("jack", 200);
//添加进HashSet,并输出true或者false
System.out.println(hashSet.add(employee1));
System.out.println(hashSet.add(employee2));
}
}
class Employee {
private String name;
private int age;
public Employee(String name, int age) {
this.name = name;
this.age = 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;
}
@Override
public int hashCode() {
return Objects.hash(name,age); //这两个只要数值相同,返回的hashcode就相同
}
//比较两个实例的name和age值是否相同
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof Employee)) {
return false;
}
//此时这个obj一定指向Employee
Employee employee = (Employee) obj;
if (this.name.equals(employee.name) && employee.age == this.age) {
return true;
}
else {
return false;
}
}
}
思考题
答案
package collection;
import java.util.HashSet;
import java.util.Objects;
/**
* @author 孙显圣
* @version 1.0
*/
public class HashSetExercise02 {
public static void main(String[] args) {
MyDate myDate1 = new MyDate("2002", "12", "08");
MyDate myDate2 = new MyDate("2002", "12", "08");
Employee_ employee1 = new Employee_("孙先生", 200, myDate1);
Employee_ employee2 = new Employee_("孙先生", 300, myDate1);
HashSet hashSet = new HashSet();
System.out.println(hashSet.add(employee1));
System.out.println(hashSet.add(employee2));
}
}
class Employee_ {
private String name;
private double sal;
private MyDate birthday;
public Employee_(String name, double sal, MyDate birthday) {
this.name = name;
this.sal = sal;
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSal() {
return sal;
}
public void setSal(double sal) {
this.sal = sal;
}
public MyDate getBirthday() {
return birthday;
}
public void setBirthday(MyDate birthday) {
this.birthday = birthday;
}
//重写hashcode方法,使得只要两个实例的name和birthday的hashcode相同则返回相同的hashcode
//对于name来说,由于是基本数据类型,只要值相同,则hashcode相同
//但是对于birthday来说,需要重写他的hashcode方法,使其在属性相同的前提下,hashcode也相同
@Override
public int hashCode() {
return Objects.hash(name,birthday);
}
//现在只要name和birthday相同就可以使他们在同一行了
//重写equals方法用来比较两个实例的name和birthday是否相同,如果相同则不可以添加到HashSet中
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Employee_)) {
return false;
}
if (((Employee_) obj).name.equals(this.name) && birthday.equals(((Employee_) obj).birthday)) {
return true;
}
else {
return false;
}
}
}
class MyDate {
private String year;
private String month;
private String day;
public MyDate(String year, String month, String day) {
this.year = year;
this.month = month;
this.day = day;
}
public String getYear() {
return year;
}
public void setYear(String year) {
this.year = year;
}
public String getMonth() {
return month;
}
public void setMonth(String month) {
this.month = month;
}
public String getDay() {
return day;
}
public void setDay(String day) {
this.day = day;
}
//这样会使这个类型的实例只要属性相同,返回的hashcode值是相同的
@Override
public int hashCode() {
return Objects.hash(year,month,day);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof MyDate)) {
return false;
}
MyDate myDate = (MyDate) obj;
if (myDate.year.equals(this.year) && myDate.month.equals(this.month) && myDate.day.equals(this.day)) {
return true;
}
else {
return false;
}
}
}
9.LinkedHashSet类
1.基本介绍
- LinkedHashSet是HashSet的子类
- LinkedHashSet底层是一个LinkedHashMap,底层维护了一个数组+双向链表
- LinkedHashSet根据元素的hashCode值来决定元素的存储位置,同时使用双向链表维护元素的次序,使得看起来是以插入顺序保存的
- LinkedHashSet不允许添加重复元素
2.LinkedHashSet底层机制
LinkedHashSet的添加机制跟HashSet是相同的,先根据hashcode计算索引值,如果在同一行就依次调用equals比较大小
3.练习
题目
代码
package collection;
import java.util.LinkedHashSet;
import java.util.Objects;
/**
* @author 孙显圣
* @version 1.0
*/
public class LinkedHashSetExercise {
public static void main(String[] args) {
LinkedHashSet linkedHashSet = new LinkedHashSet();
Car bc = new Car("奔驰", 100000);
Car bc_ = new Car("奔驰", 100000);
linkedHashSet.add(bc);
linkedHashSet.add(bc_);
System.out.println(linkedHashSet);
}
}
class Car {
private String name;
private double price;
public Car(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
//当两个实例的属性相同,则返回true
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Car car = (Car) o;
return Double.compare(car.price, price) == 0 && Objects.equals(name, car.name);
}
//当两个实例的属性相同,则返回的hashcode相同
@Override
public int hashCode() {
return Objects.hash(name, price);
}
}