集合
一、集合
集合的概述
集合就是用来存储多个数据的容器
集合和数组的区别
-
相同点
都是容器,可以存储多个数据
-
不同点
-
数组的长度是不可变的,集合的长度是可变的
-
数组可以存基本数据类型和引用数据类型
-
集合只能存引用数据类型,如果要存基本数据类型,需要存对应的包装类
-
集合的体系
-
Collection 接口
-
List 接口的三个实现类:ArrayList、LinkedList、Vector 均按添加时的顺序存放,不自动排序,若是需要自动排序,可以使用 Collections.sort 方法排序,它默认按自然顺序排序,也可以通过添加比较器实现自定义排序;
-
Set 接口的三个实现类:HashSet、TreeSet 有序,默认按自然顺序排序,可通过构造器传入自定义的比较器自定义排序策略,但是,LinkedHashSet无序
-
Map 接口
-
TreeMap 有序,他是红黑树的实现
-
HashMap 是有序的,存数据的时候默认按 key 排序,因为它按二叉树结构存储数据
二、 Collection
-
Collection集合概述
- 是单例集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素
- JDK 不提供此接口的任何直接实现.它提供更具体的子接口(如Set和List)实现
-
创建Collection集合的对象
- 多态的方式
- 具体的实现类ArrayList
-
Collection集合常用方法
方法 | 说明 |
---|---|
boolean add(E e) | 添加元素 |
boolean add(E e) | 添加元素 |
boolean remove(Object o) | 从集合中移除指定的元素 |
boolean removeIf(Object o) | 根据条件进行移除 |
void clear() | 清空集合中的元素 |
boolean contains(Object o) | 判断集合中是否存在指定的元素 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中元素的个数 |
Collection的遍历
迭代器(Iterator)
-
迭代器介绍
- 迭代器,集合的专用遍历方式
- Iterator iterator(): 返回此集合中元素的迭代器,通过集合对象的iterator()方法得到
-
Iterator中的常用方法(Iterator是从集合的0索引开始的)
boolean hasNext(): 判断当前位置是否有元素可以被取出
E next(): 获取当前位置的元素,将迭代器对象移向下一个索引位置
例:
public class test {
public static void main(String[] args) {
Collection<String> collection=new ArrayList<>();
collection.add("张三");
collection.add("李三");
collection.add("王三");
Iterator<String> iterator = collection.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
增强for循环
介绍
- 它是JDK5之后出现的,其内部原理是一个Iterator迭代器
- 实现Iterable接口的类才可以使用迭代器和增强for
- 简化数组和Collection集合的遍历
例:
public class test {
public static void main(String[] args) {
Collection<String> collection=new ArrayList<>();
collection.add("张三");
collection.add("李三");
collection.add("王三");
for (String s : collection) {
System.out.println(s);
}
}
}
List集合
- List集合的概述
- 有序集合,这里的有序指的是存取顺序
- 用户可以精确控制列表中每个元素的插入位置,用户可以通过整数索引访问元素,并搜索列表中的元素
- 与Set集合不同,列表通常允许重复的元素
- List集合的特点
- 存取有序
- 可以重复
- 有索引
List特有方法
方法 | 说明 |
---|---|
void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
E get(int index) | 返回指定索引处的元素 |
List集合子类的特点
-
ArrayList集合
底层是数组结构实现,查询快、增删慢
- 数组结构:
查询快、增删慢
- 数组结构:
-
LinkedList集合
底层是链表结构实现,查询慢、增删快
- 队列结构
查询慢、增删快
LinkedList特有的方法
- 队列结构
public void addFirst(E e) | 在该列表开头插入指定的元素 |
---|---|
public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
public E getFirst() | 返回此列表中的第一个元素 |
public E getLast() | 返回此列表中的最后一个元素 |
public E removeFirst() | 从此列表中删除并返回第一个元素 |
public E removeLast() | 从此列表中删除并返回最后一个元素 |
Set集合
- 不可以存储重复元素(重复元素会被删除一个)
- 没有索引,不能使用普通for循环遍历
- 存取无序
Treeset
-
不可以存储重复元素
-
没有索引
-
可以将元素按照规则进行排序(底层为Tree的集合默认按照元素的升序排序)
-
TreeSet():根据其元素的自然排序进行排序
-
TreeSet(Comparator comparator) :根据指定的比较器进行排序
自定义类作为泛型
想使用TreeSet必须先制定排序规则
-
Set的排序实现
自然排序Comparable
- 使用空参构造创建TreeSet集合
用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的 - 自定义的Student类实现Comparable接口
自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法 - 重写接口中的compareTo方法
重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
public class Student implements Comparable<Student>{
private String name;
private int age;
public Student() {
}
public Student(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() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
//按照对象的年龄进行排序
//主要判断条件: 按照年龄从小到大排序
int result = this.age - o.age;
//次要判断条件: 年龄相同时,按照姓名的字母顺序排序,o.getName()是集合中的数据,this.name是传进来的
result = result == 0 ? this.name.compareTo(o.getName()) : result;
return result;
}
}
比较器排序Comparator
实现步骤
- 用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
- 比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法
- 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
public class MyTreeSet4 {
public static void main(String[] args) {
//创建集合对象
TreeSet<Teacher> ts = new TreeSet<>(new Comparator<Teacher>() {
@Override
public int compare(Teacher o1, Teacher o2) {
//o1表示现在要存入的那个元素
//o2表示已经存入到集合中的元素
//主要条件
int result = o1.getAge() - o2.getAge();
//次要条件
result = result == 0 ? o1.getName().compareTo(o2.getName()) : result;
return result;
}
});
//创建老师对象
Teacher t1 = new Teacher("zhangsan",23);
Teacher t2 = new Teacher("lisi",22);
Teacher t3 = new Teacher("wangwu",24);
Teacher t4 = new Teacher("zhaoliu",24);
//把老师添加到集合
ts.add(t1);
ts.add(t2);
ts.add(t3);
ts.add(t4);
//遍历集合
for (Teacher teacher : ts) {
System.out.println(teacher);
}
}
}
二种方式的比较
- 两种比较方式小结
- 自然排序: 自定义类实现Comparable接口,重写compareTo方法,根据返回值进行排序
- 比较器排序: 创建TreeSet对象的时候传递Comparator的实现类对象,重写compare方法,根据返回值进行排序
- 在使用的时候,默认使用自然排序,当自然排序不满足现在的需求时,必须使用比较器排序
- 两种方式中关于返回值的规则
- 如果返回值为负数,表示当前存入的元素是较小值,存左边
- 如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存
- 如果返回值为正数,表示当前存入的元素是较大值,存右边
HasSet
-
底层数据结构是哈希表
-
存取无序
-
不可以存储重复元素
-
没有索引,不能使用普通for循环遍历
-
如果泛型为自定义类型,必须定义排序规则
三、Map
- Map集合概述
interface Map<K,V> K:键的类型;V:值的类型
-
Map集合的特点
- 双列集合,一个键对应一个值
- 键不可以重复,值可以重复,键重复的意思是,第二次出现的值把上面的替换(不会报错)
- 键可以为null,也会键值重复被替换
Map集合常用的方法
方法 | 说明 |
---|---|
V put(K key,V value) | 添加元素 |
V remove(Object key) | 根据键删除键值对元素(返回的是被删除的值) |
void clear() | 移除所有的键值对元素 |
boolean containsKey(Object key) | 判断集合是否包含指定的键 |
boolean containsValue(Object value) | 判断集合是否包含指定的值 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中键值对的个数 |
V get(Object key) | 根据键获取值 |
Set keySet() | 获取所有键的集合 |
Collection values() | 获取所有值的集合 |
Set<Map.Entry<K,V>> entrySet() | 获取所有键值对对象的集合 |
Map集合的遍历
根据key获取values
步骤分析
- 获取所有键的集合。用keySet()方法实现
- 遍历键的集合,获取到每一个键。用增强for实现
- 根据键去找值。用get(Object key)方法实现
public class T2 {
public static void main(String[] args) {
HashMap<Integer,String> hasMap=new HashMap<>();
//Map.ofEntries(Map.entry(101, "阿三面馆"),Map.entry(102, "阿三面馆"),Map.entry(103, "阿三面馆"),Map.entry(104, "阿三面馆"));
hasMap.put(101, "阿三面馆");
hasMap.put(102, "阿四粥馆");
hasMap.put(103, "阿五米馆");
hasMap.put(104, "阿六快递");
System.out.println("方式一,键找值");
Set<Integer> integers = hasMap.keySet();
for (Integer integer : integers) {
System.out.println(integer+"--"+hasMap.get(integer));
}
}
}
根据键值对对象(Entry)
步骤分析
- 获取所有键值对对象的集合
- Set<Map.Entry<K,V>> entrySet():获取所有键值对对象的集合
- 遍历键值对对象的集合,得到每一个键值对对象
- 用增强for实现,得到每一个Map.Entry
- 根据键值对对象获取键和值
- 用getKey()得到键
- 用getValue()得到值
System.out.println("方式二,键值对对象");
Set<Map.Entry<Integer, String>> entries = hasMap.entrySet();
for (Map.Entry<Integer, String> entry : entries) {
System.out.println(entry.getKey()+"--"+entry.getValue());
}
根据Map的foreach
foreach方法:
public class test {
public static void main(String[] args) {
Map<String,String> map=new HashMap<>();
map.put("1001", "张三");
map.put("1002", "李三");
map.put("1003", "王三");
map.forEach(new BiConsumer<String, String>() {
@Override
public void accept(String s, String s2) {
System.out.println(s+s2);
}
});
}
}
foreach方法配合Lambda表达式
System.out.println("方式三,接口中默认的foreach方法配合Lambda表达式");
hasMap.forEach((key,valuse)-> System.out.println(key+"--"+valuse));
HashMap
-
HashMap底层是哈希表结构的
-
依赖hashCode方法和equals方法保证键的唯一
-
如果键要存储的是自定义对象,需要重写hashCode和equals方法
当自定义的对象没有重写hashCode(利用属性和数组的长度生成hash值)和equals方法时,就没法保证键的唯一性了。
-
案例需求
-
创建一个HashMap集合,键是学生对象(Student),值是居住地 (String)。存储多个元素,并遍历。
-
要求保证键的唯一性:如果学生对象的成员变量值相同,我们就认为是同一个对象
-
代码实现
学生类
public class Student {
private String name;
private int age;
public Student() {
}
public Student(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 boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
if (age != student.age) return false;
return name != null ? name.equals(student.name) : student.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
}
测试类:
public class HashMapDemo {
public static void main(String[] args) {
//创建HashMap集合对象
HashMap<Student, String> hm = new HashMap<Student, String>();
//创建学生对象
Student s1 = new Student("林青霞", 30);
Student s2 = new Student("张曼玉", 35);
Student s3 = new Student("王祖贤", 33);
Student s4 = new Student("王祖贤", 33);
//把学生添加到集合
hm.put(s1, "西安");
hm.put(s2, "武汉");
hm.put(s3, "郑州");
hm.put(s4, "北京");
//遍历集合
Set<Student> keySet = hm.keySet();
for (Student key : keySet) {
String value = hm.get(key);
System.out.println(key.getName() + "," + key.getAge() + "," + value);
}
}
}
TreeMap
-
TreeMap底层是红黑树结构
-
依赖自然排序或者比较器排序,对键进行排序
-
如果键存储的是自定义对象,需要实现Comparable接口或者在创建TreeMap对象时候给出比较器排序规则
-
案例需求
- 创建一个TreeMap集合,键是学生对象(Student),值是籍贯(String),学生属性姓名和年龄,按照年龄进行排序并遍历
- 要求按照学生的年龄进行排序,如果年龄相同则按照姓名进行排序
-
代码实现
学生类
```markup
public class Student implements Comparable<Student>{
private String name;
private int age;
public Student() {
}
public Student(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() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
//按照年龄进行排序
int result = o.getAge() - this.getAge();
//次要条件,按照姓名排序。
result = result == 0 ? o.getName().compareTo(this.getName()) : result;
return result;
}
}
测试类
public class Test1 {
public static void main(String[] args) {
// 创建TreeMap集合对象
TreeMap<Student,String> tm = new TreeMap<>();
// 创建学生对象
Student s1 = new Student("xiaohei",23);
Student s2 = new Student("dapang",22);
Student s3 = new Student("xiaomei",22);
// 将学生对象添加到TreeMap集合中
tm.put(s1,"江苏");
tm.put(s2,"北京");
tm.put(s3,"天津");
// 遍历TreeMap集合,打印每个学生的信息
tm.forEach(
(Student key, String value)->{
System.out.println(key + "---" + value);
}
);
}
}