Java集合类
# Java集合类
# 概述
java.util提供的,集合类可称为容器,集合的长度是可变的(动态长度),集合存放的是对象的引用
# Collection接口
Collection接口不能直接使用,它提供了 添加、删除、管理 元素等操作 给 List 和 Set 子接口进行操作
常用方法
修饰符 | 方法 | 参数 | 说明 |
---|---|---|---|
void | add(Object o) | 1.对象 | 添加指定 对象 |
void | addAll(Collection c) | 1. 集合 | 添加指定集合对象 |
void | clear() | - | 清空集合中的元素 |
boolean | contains(Object o) | 1. 对象 | 查找一个元素是否存在 |
boolean | containsAll(Collection c) | 1.对象 | 查找一个集合相同元素是否存在 |
boolean | retainAll(Collection c) | 1.对象 | 保存指定集合中的元素 |
void | remove(Object o) | 1. 对象 | 指定对象在集合中移除 |
boolean | isEmpty() | - | 判断集合是否为空 |
int | size() | - | 获取集合中的个数 |
Object[] | toArray() | - | 将集合以数组形式输出 |
Iterator<E> | iterator() | - | 集合迭代器(Iterator 接口实例化) |
# List集合
List集合像列表清单,它允许元素重复,但各个元素的顺序就是对象插入的顺序
List接口及实现类
List接口 继承了 Collection接口,因此也有父接口的方法
方法
返回类型 | 方法 | 说明 |
---|---|---|
boolean | add(Object obj) | 插入数据 |
boolean | add(int index , Object obj) | 指定位置插入数据 |
E | get(int index) | 获取指定索引的元素 |
int | indexOf(Object o) | 根据对象查找指定的位置,不存在则-1 |
int | lastIndexOf(Object o) | 从后面向前查找位置,不存在则-1 |
ListIterator<E> | listIterator(int index) | 返回从指定位置的 ListIterator 接口的实例 |
E | remove(int index) | 删除指定位置的内容 |
E | set(int index , Object obj) | 将集合中指定索引位置的对象修改为指定的对象 |
List<E> | subList(int fromIndex , int toIndex) | 返回子集合 |
ListIterator<E> | listIterator() | 返回特殊的迭代器(含插入更改功能) |
实现类
List接口常用的类有 ArrayList 、Vector 和 LinkedList 类
Vector 与 ArrayList 操作是相同的 ,Vector类 过时了 建议使用ArrayList
# ArrayList 类
Class ArrayList
<E>
java.lang.Object java.util.AbstractCollection
<E>
java.util.AbstractList<E>
java.util.ArrayList<E>
ArrayList类 以数组的形式存储 ,该类提供了数组操作的方法!
/*ArrayList类 的定义*/
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess,Cloneable, Serializable
ArrayList类 动态数组
List<E> array = new ArrayList<E>();
List<E> array = new ArrayList<E>(int max); //设置空间大小
# LinkedList 类
Class LinkedList
<E>
java.lang.Object java.util.AbstractCollection
<E>
java.util.AbstractList<E>
java.util.AbstractSequentialList<E>
java.util.LinkedList<E>
LinkedList类 以双向链表的形式存储数据 ,也一样继承 List类 中的方法!
/*LinkedList的定义*/
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, Serializable
LinkedList类 链表结构保存对象
List list = new LinkedList();
Deque接口 拓展 Queue 接口 ,Queue接口实现的方法:
返回类型 | 方法 | 说明 |
---|---|---|
boolean | add(E e) | 添加元素 |
boolean | offer(E e) | 添加元素 |
E | element() | 获取头元素 |
E | peek() | 获取头元素 |
E | poll() | 获取并删除头元素 |
E | remove() | 获取并删除头元素 |
E | removeLast() | 获取并删除最后一个 |
E | removeFirst() | 获取并删除第一个 |
方法区别
add 与 offer 区别
offer 在队列有限制的的情况下优势更大!
element 与 peek 区别
element 队列为空,则抛出异常 peek 队列为空,则返回 null
poll 与 remove 区别
poll 队列为空,则返回 null remove 队列为空,则抛出异常
# ArrayList与LinkedList的区别
ArrayList查找较快,但插入、删除对象的速度较慢
LinkedList查找较慢,但插入、删除对象的速度较快
代码示例:
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class Demo {
public static void main(String[] args) {
//另个用法一样
// List list = new ArrayList();
List list = new LinkedList();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
//集合长度
System.out.println("长度为:"+list.size());
//集合获取索引值(2)
System.out.println("get(2):"+list.get(2));
//更该索引值(2)
list.set(2,"cc");
//删除索引值4
list.remove(4);
//中间插入
list.add(1,"bb");
list.add(null);
//输出内容
System.out.println("遍历集合:");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
/*
长度为:5
get(2):c
遍历集合:
a
bb
b
cc
d
null
*/
# Set集合
Set集合中的对象不按特定的方式排序,只是单纯的把对象添加到集合中,但不能有重复对象,Set接口 继承了 Collection接口,因此也有父接口的方法。想要获取 Set集合中的元素,需要获取整个集合与元素遍历,不能更改集合元素值!!
实现类
# TreeSet类
Class TreeSet
<E>
java.lang.Object java.util.AbstractCollection<E>
java.util.AbstractSet<E>
java.util.TreeSet<E>
TreeSet类 实现了Set集合在遍历集合时按照自然顺序递增排序
存储的类型如果是 对象,那么该对象就必须实现 Comparable接口 ,Comparable接口中有compareTo抽象方法 可以对集合处理排列方式 ,compareTo抽象方法 返回整型进行控制排列(0(相等) 、 正数 (小于)、 负数(大于))
TreeSet<E> set = new TreeSet<E>();
TreeSet类 提供方法
返回类型 | 方法 | 说明 |
---|---|---|
<E> | first() | 返回集合中当前的第一个(最低)元素 |
<E> | last() | 返回集合中当前的最后(最高)元素 |
<E> | comparator() | 用于对集合元素进行排序的比较器,以比较返回 null |
SortedSet<E> | headSet(E toElement) | 返回新的Set集合,新集合 toElement(不包含)之前所有对象 |
SortedSet<E> | subSet(E fromElement, E toElement) | 返回新的Set集合,其元素的范围从 fromElement (含)到 toElement |
SortedSet<E> | tailSet(E fromElement) | 返回新的Set集合,新集合 7fromElement(包含)之后所有对象 |
代码示例:
import java.util.Iterator;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
public class Demo {
public static void main(String[] args) {
TreeSet<Student> set = new TreeSet();
Student s = new Student(1 , 20 , "小明");
Student s2 = new Student(2 , 22 , "小红");
Student s3 = new Student(3 , 22 , "小军");
Student s4 = new Student(4 , 24 , "小张");
Student s5 = new Student(6 , 21 , "张三");
Student s6 = new Student(9 , 23 , "李四");
set.add(s);
set.add(s2);
set.add(s3);
set.add(s4);
set.add(s5);
set.add(s6);
set.add(s6);//重复,则无效
// set.add(null); 异常
System.out.println("集合总数为:"+set.size());
System.out.println("返回集合第一个元素:"+set.first());
System.out.println("返回集合最后一个元素:"+set.last());
System.out.println("排序比较结果为:"+set.comparator());
System.out.println("\n输出集合:");
Iterator it = set.iterator();
//如果迭代有更多的元素,则true
while(it.hasNext()){
//next() 会自动调用对象的 toString()方法
System.out.println(it.next());
}
//测试headSet()方法
System.out.println("\n测试方法2");
//截止至s4对象位置
it = set.headSet(s4).iterator();
while(it.hasNext()){
//next() 会自动调用对象的 toString()方法
System.out.println(it.next());
}
//测试subSet()方法
System.out.println("\n测试方法3");
//指定对象范围s ,s3
it = set.subSet(s , s3).iterator();
while(it.hasNext()){
//next() 会自动调用对象的 toString()方法
System.out.println(it.next());
}
//测试stailSet()方法
System.out.println("\n测试方法4");
//从s4对象位置开始排序
it = set.tailSet(s4).iterator();
while(it.hasNext()){
//next() 会自动调用对象的 toString()方法
System.out.println(it.next());
}
}
}
class Student implements Comparable<Student>{
int id;
int age;
String name;
public Student(){}
//实例对象时获取的参数(信息)
public Student(int id , int age , String name){
super();
this.id = id;
this.age = age;
this.name = name;
}
//迭代输出的信息
@Override
public String toString() {
return "Student{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
//迭代排序用到的方法
@Override
public int compareTo(Student stt) {
/*
* 排序的值尽可能保持 唯一
* 如唯一的id
* */
//按照age排序的顺序(重复会被覆盖掉)
// int tmp = this.age - stt.age;
//按照id排序的顺序
int tmp = this.id - stt.id;
if(tmp != 0){
//Math.abs() 绝对值
//差值 除以本身 绝对值,结果只有 1 或 -1值
tmp = tmp / Math.abs(tmp);
}
/*
* 判断结果只有 0 、 1 、 -1(相等 ; 小于 ; 大于)
* 根据三值来进行排序
* */
return tmp;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Student student = (Student) o;
return id == student.id;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
/*
集合总数为:5
返回集合第一个元素:Student{id=1, age=20, name='小明'}
返回集合最后一个元素:Student{id=9, age=23, name='李四'}
排序比较结果为:null
输出集合:
Student{id=1, age=20, name='小明'}
Student{id=2, age=22, name='小红'}
Student{id=3, age=22, name='小军'}
Student{id=4, age=24, name='小张'}
Student{id=9, age=23, name='李四'}
测试方法2
Student{id=1, age=20, name='小明'}
Student{id=2, age=22, name='小红'}
Student{id=3, age=22, name='小军'}
测试方法3
Student{id=1, age=20, name='小明'}
Student{id=2, age=22, name='小红'}
测试方法4
Student{id=4, age=24, name='小张'}
Student{id=9, age=23, name='李四'}
*/
# HashSet类
Class HashSet
<E>
java.lang.Object java.util.AbstractCollection<E>
java.util.AbstractSet<E>
Classs HashSet<E>
java.lang.Object java.util.AbstractCollection<E>
java.util.AbstractSet<E>
HashSet类 在Set集合中的散列形式存储数据的!
不能更改唯一值否则哈希表的唯一地址会错误,也能被删除! 用HashSet存储的对象,应该重写以下两方法 hashCode()存哈希、equals()方法 哈希地址判断(以上两个方法会影响HashSet唯一性的存储状况)
HashSet集合 可能情况: 集合中不会保存相同的对象 同一个哈希地址可以存放多个不同对象
代码示例:
import java.util.HashSet;
import java.util.Iterator;
import java.util.Objects;
public class Demo2 {
public static void main(String[] args) {
HashSet<Person> set = new HashSet<>();
Person p1 = new Person(1,"小明");
Person p2 = new Person(2,"小红");
Person p3 = new Person(3,"小军");
Person p4 = new Person(4,"小张");
Person p5 = new Person(5,"小海");
Person p6 = new Person(6,"小明");
//首位位置里的id:9
set.add(p1);
/*p1添加错误是因为该集合唯一性是根据 属性id 进行判断唯一的*/
//最后位置里的id:9
p1.id = 9;
System.out.println("更改p1的id改至9添加是否成功?"+set.add(p1));
/*添加无效*/
set.add(p1);
set.add(p2);
set.add(p3);
set.add(p4);
set.add(p5);
set.add(p6);
System.out.println("遍历数组:");
Iterator<Person> in = set.iterator();
while (in.hasNext()){
System.out.println(in.next());
}
}
}
class Person{
int id ;
String name;
public Person(int id , String name){
super();
this.id = id ;
this.name = name;
}
//以下两个方法会影响 Set集合 的存储情况
//输出对象的值
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
//比较哈希值的地址
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Person)) {
return false;
}
Person person = (Person) o;
return id == person.id;
}
//根据hash返回哈希值
@Override
public int hashCode() {
return Objects.hash(id);
}
}
/* 运行结果
更改p1的id改至9添加是否成功?true
遍历数组:
Person{id=9, name='小明'}
Person{id=2, name='小红'}
Person{id=3, name='小军'}
Person{id=4, name='小张'}
Person{id=5, name='小海'}
Person{id=6, name='小明'}
Person{id=9, name='小明'}
*/
# Map集合
Map集合没有 Collection接口 ,但提供了 key (键)和 value(值)的映射, key (键)和 value(值)都是将其变为了 Map.Entry 并且将 Map.Entry 保存在了 Map 集合之中。Map中不能有相同的 key (键),每个key (键)只能映射一个value(值),key (键)决定了存储对象在映射中的存储位置
哈希存储原理
注意:
当对象被存进 HashSet 集合后,就不能修改该对象中的那些参与计算的哈希值的属性了,否则,哈希值不对应找不到,在这种情况下,即使在 contains()方法检索对象,也是返回 null 空的结果,这也导致无法从 HashSet 集合中删除当前对象,从而造成内存泄露
Map接口提供的方法
修饰符 | 方法 | 说明 |
---|---|---|
V | put(K key , V value) | 添加一对 K,V ,覆盖原有的V,则返回旧V值 ,否则为null |
void | clear() | 清空所有 K , V |
boolean | containsKey(Object key) | 查找指定 K值 |
boolean | containsValue(Object value) | 查找指定 V值 |
V | get(Object key) | 通过 K 找 V值 |
Set<K> | keySet() | 获取 K值 集合 |
Collection<V> | values() | 获取 V值 集合 |
V | remove(Object key) | 删除指定 K 删除,返回 V值 ,没有则null |
default V | replace(K key , V value) | Key映射的值,替换指定键条目 |
int | size() | 获取 元素总数 |
实现类
# HashMap类
Class HashMap
<K,V>
java.lang.Object java.util.AbstractMap
<K,V>
java.util.HashMap<K,V>
HashMap类 散列存储 键值对速度更快,允许null对象,无序存储,但添加、删除快
HashMap<K , V> m = new HashMap<K , V>();
/*构造一个空的 HashMap ,默认初始容量(16)和默认负载因子(0.75)(容量和负载因子是影响效率的因素*/
# LinkedHashMap类
Class LinkedHashMap
<K,V>
java.lang.Object java.util.AbstractMap
<K,V>
java.util.HashMap<K,V>
java.util.LinkedHashMap<K,V>
LinkedHashMap类 有序存储 存储在 哈希表 和 双向链表 中,两个存储结合使得有序,实现快速查找,高性能,但消耗空间
# TreeMap类
Class TreeMap
<K,V>
java.lang.Object java.util.AbstractMap
<K,V>
java.util.TreeMap<K,V>
TreeMap类 树状 键值对存放有序,不允许null对象,映射关系有一定的顺序,Key(键)不支持对象,数值,null!!
# Hashtable类
Class Hashtable
<K,V>
java.lang.Object java.util.Dictionary
<K,V>
java.util.Hashtable<K,V>
Hashtable类 安全线程(又称旧版哈希存储) 组中每个哈希桶的对象是以排队形式执行,得以保证 安全线程 ,但效率低
# ConcurrentHashMap类
Class ConcurrentHashMap
<K,V>
java.lang.Object java.util.AbstractMap
<K,V>
java.util.concurrent.ConcurrentHashMap<K,V>
ConcurrentHashMap类 安全高效 每个哈希桶同时执行,但哈希桶中的链表是排队形式的(又称 分段锁机制),得以保证 安全线程 和 高效率的情况
# 实现类的区别
安全方面 多线程 建议使用 Hashtable 、ConcurrentHashMap 有排序要求的 建议使用 LinkedHashMap 、TreeMap 使用操作方面基本一样,少部分用于特殊方面可自行api了解
代码示例:
import java.util.*;
public class Demo {
public static void main(String[] args) {
HashMap<Object,Object> map = new HashMap<>();
//添加数据
map.put("no1" , "no1字符串");
map.put(12 , "12数值");
map.put(12.3, "12.3单精度");
map.put(null , null);
map.put(new Object() , "obj对象");
System.out.println("Map集合总数为:"+ map.size());
System.out.println("在Map集合中 查找12的键 是否存在?"+map.containsKey(12));
System.out.println("在Map集合中 查找 obj的值 是否存在?"+map.containsValue("obj"));
//遍历方式 1
System.out.println("\n遍历Map集合:");
System.out.println("遍历key :");
//以Set集合的形式获取所有对象Key对象
Set<Object> set = map.keySet();
for (Object key : set) {
//通过 键 获取值 并输出
System.out.println("key :" + key + "\t\t\tV :" + map.get(key));
}
System.out.println("\n Collection 遍历value :");
//以Collection形式获取对象值
Collection<Object> con = map.values();
for (Object o : con) {
System.out.println(o);
}
//遍历方式 2
//也可以这样获取set
//Map存储的就是Map.Entry , Map.Entry存储了 Key键 和 Value值
/*
Set<Map.Entry<Object, Object>> set = map.entrySet();
Iterator<Map.Entry<Object, Object>> iter = set.iterator();
while(iter.hasNext()){
Map.Entry<Object,Object> me = iter.next();
System.out.println(me.getKey()+" --> "+me.getValue());
}
*/
}
}
/*
Map集合总数为:5
在Map集合中 查找12的键 是否存在?true
在Map集合中 查找 obj的值 是否存在?false
遍历Map集合:
遍历key :
key :null V :null
key :java.lang.Object@10f87f48 V :obj对象
key :no1 V :no1字符串
key :12.3 V :12.3单精度
key :12 V :12数值
遍历value :
null
obj对象
no1字符串
12.3单精度
12数值
*/
# 集合总结
- 类集合是一个动态的对象数组,可以向集合中加入任意多的内容
- List 接口中是允许有重复元素的,Set 接口中是不允许有重复元素
- 所有的重复元素依靠 hashCode()和 equals 进行区分
- List 接口的常用子类:ArrayList、Vector
- Set 接口的常用子类:HashSet、TreeSet
- TreeSet 是可以排序,一个类的对象依靠 Comparable 接口排序
- Map 接口中允许存放一对内容,key(键)、value(值)
- Map 接口的子类:HashMap、Hashtable、TreeMap
- Map 使用 Iterator 输出的详细步骤
Map集合区别
集合类 | key | vlaue | Super | 说明 |
---|---|---|---|---|
Hashtable | N | N | Dictionary | 安全线程 |
ConcurrentHashMap | N | N | AbstractMap | 锁分段技术 |
TreeMap | N | Y | AbstractMap | 不安全线程 |
HashMap | Y | Y | AbstractMap | 不安全线程 |
# 迭代器
迭代器用来遍历集合中的元素
# Iterator接口
Iterator接口 是 Collection接口 的子接口 用来遍历删除数据用的
方法
返回类型 | 方法 | 说明 |
---|---|---|
boolean | hasNext() | 迭代方向存在元素,则 true |
E | next() | 返回当前指向元素,并前进一位 |
void | remove() | 从底层集合中删除此迭代器返回的最后一个元素 |
# ListIterator接口
ListIterator接口 是 List接口 的子接口 用来 遍历 删除 添加使用
方法
返回类型 | 方法 | 说明 |
---|---|---|
void | add(E e) | 将元素插入列表 |
boolean | hasNext() | 迭代正方向有元素 |
E | next() | 返回列表下一位元素的值 |
int | nextIndex() | 返回随后调用 next()元素 的索引 |
E | previous() | 返回列表上一位元素的值 |
int | previousInext() | 返回随后调用 previous()元素 的索引 |
void | remove() | 删除由 next() 或 previous() 返回的元素 |
void | set(E e) | 替换 由next() 或 previous() 返回的元素 |
迭代器异常说明:
快速失败: 迭代器创建后对集合进行批量更改或删除会导致迭代器出现不确定行为,迭代器会抛出ConcurrentModificationException 异常
安全失败: 迭代器在创建时候会备份一份集合,批量修改会导致数据的不确定,没能即使同步导致的异常,迭代器会抛出 ConcurrentModificationException 异常
代码示例: (更多方法可执行测试)
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class Demo {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
list.add(6);
System.out.println("size : " +list.size());
// 迭代器
Iterator<Integer> ito = list.listIterator();
while (ito.hasNext()){
int n = ito.next();
if (n == 4){
ito.remove();
continue;
}
System.out.print(n+" ");
}
System.out.println("\n======");
System.out.println(list);
System.out.println("====================");
System.out.println( );
System.out.println( );
//此时 list内容 == [1, 2, 3, 5, 6]
ListIterator<Integer> listIto = list.listIterator();
//向前两位
listIto.next();
listIto.next();
listIto.previous();
listIto.next();
listIto.previous();
System.out.println("迭代器前索引 : "+ listIto.nextIndex());
System.out.println("\n======");
System.out.println(list);
System.out.println("======");
}
}
/*
size : 6
1 2 3 5 6
======
[1, 2, 3, 5, 6]
====================
迭代器前索引 : 1
======
[1, 2, 3, 5, 6]
======
*/