1.练习回顾
public class Test {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
List list = new LinkedList();
String name;
String sex;
while(true){
System.out.print("请输入姓名:");
name = scanner.next();
System.out.print("请输入性别:");
sex = scanner.next();
Perple perple = new Perple(name,sex);
if(list.contains(perple)){
System.out.println("这位同志已存在,请重新输入!");
continue;
}
list.add(perple);
System.out.println("是否继续(y/n)?");
if(scanner.next().equals("n")){
break;
}
}
//System.out.println("录入的学生信息是:\n"+list);
for (Object obj: list) {
System.out.println(obj);
}
}
}
public class Perple {
String name;
String sex;
public Perple(String name, String sex) {
this.name = name;
this.sex = sex;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Perple perple = (Perple) o;
return Objects.equals(name, perple.name) && Objects.equals(sex, perple.sex);
}
@Override
public int hashCode() {
return Objects.hash(name, sex);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Perple[" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
']';
}
}
2.Arraylist使用
2.1遍历集合
for+索引遍历
for (int i = 0; i < arrayList.size(); i++) {
//默认存储的数据都是Object类型的
Object obj = arrayList.get(i);
System.out.println(obj);
}
for Each遍历
for (Object obj:arrayList) {
System.out.println(obj);
}
注:
不要在遍历ArrayList 过程中使用remove()方法。
①for+索引遍历来的方式:
//错误使用①
for (int i = 0; i < arrayList.size(); i++) {
arrayList.remove(i);
}
使用remove方式移除元素之后arrayList会进行重排,导致元素的索引改变,从而漏掉一些元素。
②for Each 遍历:
//错误使用②
for (Object obj: arrayList) {
arrayList.remove(obj);
}
该方式则是直接会产生异常:ConcurrentModificationException(并发修改异常)。
③解决方式:
倒序遍历
for (int i = arrayList.size()-1; i>=0; i--) {
arrayList.remove(i);
}
使用迭代器(Iterator)
Iterator it = arrayList.iterator();
while (it.hasNext()){
if(it.next()=="aaa"){
it.remove();
}
}
2.2 ArrayList底层源码
对于ArrayList而言,它实现List接口、底层使用数组保存所有元素,其操作上基本是对数组的操作。
2.2.1 ArrayList无参构造:ArrayList()
public ArrayList() {
//elementData 是 ArrayList 底层实际存储数据的对象数组
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
transient Object[] elementData; // non-private to simplify nested class access
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//所以new ArrayList的本质就是创建了一个空的Object类型的数组
2.2.2 ArrayList有参构造:ArrayList(int initialCapacity)
//initialCapacity:初始容量
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
//初始容量大于0,则按初始容量创建数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
//等于0,则直接赋给一个空数组
this.elementData = EMPTY_ELEMENTDATA;
} else {
//小于0,则抛出异常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
2.2.3 添加元素:add(E e)扩容原理
public boolean add(E e) {
//扩容数组
ensureCapacityInternal(size + 1); // Increments modCount!!
将数据存储到数组中,并且元素个数+1
elementData[size++] = e;
return true;
}
//扩容的具体实现
private void ensureCapacityInternal(int minCapacity) {
//判断当前ArrayList是否是空数组
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//private static final int DEFAULT_CAPACITY = 10
//取二者中的较大值
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
//记录该list在结构上被修改的次数
modCount++;
// overflow-conscious code
//当前数组长度小于扩容长度时,执行grow()
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
//原始容量
int oldCapacity = elementData.length;
//新的容量 >>移位运算符 >>1 二进制数右移一位 相当于原数 /2
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
//Array.copyOf:扩容的具体实现
elementData = Arrays.copyOf(elementData, newCapacity);
}
2.2.4 删除元素:remove(Object o)
public boolean remove(Object o) {
//判断数据是否为空
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
//通过系统进行错位复制的方式,删除元素
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
2.2.5 清空集合:clear()
//将每一个元素设置为空,并长度设置为0
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
2.2.6 查找集合中某个元素的位置:indexOf(Object o)
//所以indexOf找不到的时候返回值是 -1
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
2.2.7 返回集合元素长度:size()
//size 记录着元素的个数
public int size() {
return size;
}
3.Set接口及其实现类
3.1 Set接口特点
①Set接口是无序的;
②Set接口中的数据不允许重复;
③Set接口无法通过下标访问数据;
④查找慢,插入删除快(底层数据结构是哈希表、链表和红黑树);
⑤Set集合使用equals()和hashCode()方法实现元素去重。
3.2 HashSet实现类
HashSet特点:
①HashSet是Set接口的实现类;
②线程不安全。
public static void main(String[] args) {
//Set:1.无序 2.不重复 3.没有下标
Set set = new HashSet();
set.add("bbb");
set.add("bbb");
set.add(true);
set.add(12.234);
set.add("aaa");
set.add("aaa");
set.add(100);
set.add(200);
set.add(new Student("张三",20));
//打印集合内容
//System.out.println(set);
//打印长度
//System.out.println(set.size());
//set无法按照索引获取数据,因为set是无序的没有下标
//删除
//set.remove("aaa");
//System.out.println(set);
//包含某个数据
//System.out.println(set.contains("bbb"));
//是否为空
//System.out.println(set.isEmpty());
//System.out.println("================================");
//遍历set集合
//方式1:for Each
for (Object obj : set){
System.out.println(obj);
}
System.out.println("============================");
//方式2: Iterator迭代器
//通过集合对象的iterator()方法,返回一个Iterator对象,通过这个对象可以遍历集合元素
Iterator it = set.iterator();
//it.hasNext():判断集合是否有数据,返回true/false
//it.next():读取集合中的一个数据
while (it.hasNext()){
Object o = it.next();
System.out.println(o);
}
}
HashSet避免对象重复的规则:
①如果对象的hashCde值不同,则不需要用equals方法判断,直接存到HashSet中;
②如果对象的hashCode值相同,需要用equals方法进行比较,如果结果为true,则视为相同元素,不存储;如果结果为false,则视为不同元素进行存储。
如果对象元素要存储到HashSet中,必须覆盖hashCode方法和equals方法,才能从对象内容的角度确保唯一性。
public class Student {
private String name;
private int age;
public Student(){
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
//重写equals
@Override
public boolean equals(Object o) {
if(this==o){
return true;
}
if(!(o instanceof Student)){
return false;
}
Student student = (Student) o;
if(Objects.equals(this.name,student.getName()) &&
Objects.equals(this.age,student.age)){
return true;
}
return false;
}
//重写hashCode,返回hash值,通过hash值得到要存储到数组的下标
//hash值一样的数据存储到同一个数组空间,后再用equals进行内容比较
@Override
public int hashCode() {
return Objects.hash(name, 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() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public static void main(String[] args) {
Student s1 = new Student("张三","男",20);
Student s2 = new Student("张三","男",20);
Student s3 = new Student("李四","男",20);
// Object中的HashCode默认返回的是对象的内存地址
//System.out.println(s1.hashCode());
//System.out.println(s2.hashCode());
Set s = new HashSet();
s.add(s1);
s.add(s2);
s.add(s3);
System.out.println(s);
// hash运算(默认调用数据的hashCode()方法实现)结果是相同的前提下,才会进行相
// hash运算结果不同,是无法去重的
/*String s = "sdf";
String ss = "sdf";
System.out.println(s.hashCode());
System.out.println(ss.hashCode());
*/
}
3.3 迭代器Iterator
HashSet类中没有提供根据集合索引获取索引对应的值的⽅法, 因此遍历HashSet时需要使⽤Iterator迭代器。
返回类型 | 方法 | 描述 |
boolean | hasNext() | 如果有元素可迭代 |
Object | next() | 返回迭代的下一个元素 |
public static void main(String[] args) {
// LinkedHashSet:链表结构的Set集合
// 1.不重复 2.能保证添加的次序(添加和输出次序一致)
Set set = new LinkedHashSet();
set.add("c");
set.add("a");
set.add("d");
set.add("b");
//System.out.println(set);
// 迭代器遍历集合
Iterator it = set.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
}