List接口
1. List接口介绍
java.util.List
接口继承自Collection接口, 是==单列集合==的一个重要分支, 习惯性的会将实现了List
接口的对象称为List
集合. 在List
集合中允许出现重复的元素, 所有的元素是以一种线性方式进行储存的, 在程序中可以通过索引来访问集合中的指定元素. 另外, List
集合还有一个特点就是==元素有序==, 即元素的存入顺序和取出顺序一致.
所以, List
接口的特点 :
- 有序, 他是一个元素存取有序的集合, 例如, 存元素的顺序是11, 22, 33.那么集合中, 元素的储存就是按照11, 22, 33的顺序完成的.
- 有索引, 他是一个带有索引的集合, 通过索引就可以精确的操作集合中的元素 (与数组的索引是一个道理)
- 可重复, 集合中可以有重复的元素, 通过元素的equals方法, 来比较是否为重复的元素.
ArrayList
类是List接口的实现类, 该类中的方法都来自与List中定义
2. List接口常用的方法
List作为Collection集合的子接口, 不但继承了Collection接口中的全部方法, 还增加了一些根据元素索引来操作集合的特有方法. 如下 :
public void add(int index, E element)
: 将指定的元素, 添加到该集合中的指定位置上.
- E element代表 泛型元素, 即任何数据类型的元素
-
public E get(int index)
: 返回集合中指定位置的元素 -
public E remove(int index)
: 移除列表中指定位置的元素, 返回的是被移除的元素 -
public E remove(int index)
: 移除列表中指定位置的元素, 返回的是被移除的元素 -
public E set(int index, E element)
: 将指定的元素替换集合中指定位置的元素, 返回值是更新前的元素
List集合的特有的方法都是跟索引有关.
我们之前学习Collection体系的时候, 发现List集合下有很多的集合, 他们储存结构不同, 这样就导致了这些集合他们有各自的特点, 供我们在不同环境下使用.
3. ArrayList
集合
java.util.ArrayList
集合数据存储的结构是数组结构, 元素增删慢, 查找快, 由于日常开发中使用最多的功能为查询数据, 遍历数据, 所以ArrayList
是最常用的集合.
许多程序员开发时非常随意的使用ArrayList
完成任何需求, 并不严谨, 这种用法是不提倡的.
4. LinkedList
集合
java.util.LinkedList
集合数据储存的结构是链表结构, 方便元素添减, 删除的集合
实际开发中对一个集合元素的添加与删除经常涉及到首尾操作,而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。LinkedList
是List的子类,List中的方法LinkedList
都是可以使用,这里就不做详细介绍,我们只需要了解LinkedList
的特有方法即可。在开发时,LinkedList
集合也可以作为堆栈,队列的结构使用。
Set 接口
Set是Collection下面的另一个子接口。Set接口有以下特点:
- 无序。(按照什么顺序存,不一定按照什么顺序取)
- 无索引。(不能根据索引获取元素)
- 不可重复(不能存放重复元素)Set是一个接口,如果要用,需要使用实现类,最常用的实现类是
HashSet
。
哈希(Hash)值就是一个int数字, 我么你可以把哈希值看成对象的一个标识, 一个特征码.
在Object中有一个方法叫做hashCode
, 可以获取对象的哈希值
-
int hashCode()
: 获取对象的哈希值, 是Object类中的, 任意对象都可调用.
public class Student {
private String name;
private int age;
//get set 方法
}
public class StudentTest {
public static void main(String[] args) {
Student stu = new Student("张三丰", 100);
System.out.println(stu.hashCode());
//1163157884
//不new新对象, 改变stu的内容, hashCode怎么变?
//hashCode不变
Student stu2 = new Student("张三丰", 100);
System.out.println(stu.hashCode());
//1965725890
}
}
Object类中的哈希值计算规则是根据地址值计算的, stu
和stu2
地址值不一样, 所以他们的哈希值是不相同的. 哈希值如果通过地址值计算的话, 意义不大.
我们大多数是希望根据对象的属性计算对象的哈希值, 如果两个对象的属性完全相同, 那么对象的哈希值也应该相同.
对象的哈希值是允许重复的.
如果要自己定义哈希值的计算规则, 比如根据对象的属性计算哈希值, 我们需要重写hashCode
方法.
//重新对应哈希值的计算规则, 如果两个对象的属性值一样, 哈希值也相同.
@Override
public int hashCode() {
return name.hashCode() + age;
}
小结:
- 哈希值是一个int数字
- 我们可以将哈希值看成对象的标识,特征码。
- 对象的哈希值允许重复。
- 调用
hashCode
方法可以获取对象的哈希值。 - 如果要自定义哈希值的计算规则,可以重写
hashCode
方法。
可以用快捷键alt + insert
重写hashCode
和equals
方法
hashSet
判断唯一性的过程
- 先比较两个对象的哈希值(调用
hashCode
方法)
如果哈希值不同, 肯定是不同的对象
如果哈希值相同, 不一定是同一个对象 - 如果哈希值相同, 那么还需要使用equals方法
如果equals方法的结果是true, 那么两个对象相同
如果equals方法的结果是false, 那么两个对象不同
java.util.Set
接口和java.util.List
接口一样, 同样继承自Collection
接口, 他与Collection
接口中的方法基本一致, 并没有功能上的扩充, 只是比Collection
接口更加严格了. Set
接口都会以某种规则保证存入的元素不出现重复.
Set
集合有多个子类, 这里我们介绍其中的java.util.HashSet
, java.util.LinkedHashSet
这两个集合.
因为Set接口的无序性, 所以遍历的方式可以采用迭代器, 增强for
HashSet
是根据对象的哈希值来确定元素在集合中的储存位置, 因此具有良好的储存和查找性能, 保证元素唯一性的方式依赖于: hashCode
和equals
方法.
先来一段使用案例:
public class HashSetTest {
public static void main(String[] argsr) {
HashSet <String> set = new HashSet<>();
set.add(new String ("cba"));
set.add("abc");
set.add("bac");
set.add("cba");
for (String name : set) {
System.out.println(name);
}
}
}
//输出结果
//cba
//abc
//bac
//cba字符串只储存了一个, 说明重复的元素set集合不储存
1. HashSet
集合存储数据的结构
首先要来了解一下什么是哈希表
在JDK8
之前, 哈希表底层采用数组加链表实现, 即使用数组处理冲突, 同一hash值的链表都储存在同一个数组里, 但是位于一个桶中的元素较多, 即hash值相等的元素较多时, 通过key值依次查找的效率较低.
而在JDK8
中, 哈希表存储采用数组加链表加红黑树实现的,
总而言之, JDK8
引入红黑树大程度优化了HashMap
的性能, 那么对于我们来讲, HashSet
集合元素的唯一, 其实是根据对象的hashCode
和equals方法来决定的. 如果我们往集合中存放自定义的对象, 那么保证其唯一, 就必须重写hashCode
和equals
方法建立属于当前对象的比较方式.
2. HashSet
储存自定义类型元素
给HashSet
中存放自定义类型的元素时, 需要重写对象中的hashCode
和equals方法, 建立自己的比较方式, 才能保证HashSet
集合中的对象唯一
代码演示 :
//自定义Student类
public class Student {
private String name ;
private int age;
//get / set 方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
创建测试类 :
public class HashSetTest {
public static void main(String[] args) {
HashSet<Student> stuSet = new HashSet<Student>();
Student stu = new Student("于谦", 43);
stuSet.add(stu);
stuSet.add(new Student("郭德纲", 44);
stuSet.add(new Student("于谦", 43));
stuSet.add(new Student("郭麒麟", 23));
stuSet.add(stu);
for(Student stu2 : stuSet) {
System.out.println(stu2);
}
}
}
//执行结果:
Student [name=郭德纲, age=44]
Student [name=于谦, age=43]
Student [name=郭麒麟, age=23]
3. LinkedHashSet
我们知道HashSet
保证元素唯一, 可是元素存放进去是没有顺序的, 那么我们要保证有序, 怎么办呢~
在HashSet
下面有一个子类java.util.LinkedHashSet
, 它是链表和哈希值组合的一个数据储存结构
演示代码如下 :
package drafts.drafts2;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
public class LinkedHashSetTest {
public static void main(String[] args) {
Set<String> set = new LinkedHashSet<>();
set.add("bbb");
set.add("aaa");
set.add("abc");
set.add("bbc");
Iterator<String> it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}