
JAVA-集合框架
集合
什么是集合
概念
对象的容器,实现了对对象常用的操作
和数组的区别
- 数组长度固定,集合长度不固定
- 数组可以存储基本类型和引用类型,集合只能存储引用类型
位置
java.util.*;包
collection 体系
Collection 父接口
特点:代表一组任意类型的对象,无序、无下标、不能重复。
创建集合 Collection collection = new ArrayList();
常用方法
- collection.add(Object obj); 添加一个对象;
- adddAll(Collection c) ;//将一个集合中的所有对象添加到此集合中;
- void clear(); 清空此集合中的所有对象;
- contains(Object obj); //检查此集合中是否包含 o 对象;
- equals(Object obj); //比较此集合中是否包含 o 对象;
- isEmpty(); 判断此集合是否为空;
- remove(Object obj); //在此集合中移除 o 对象;
- int size(); //返回此集合中的元素个数;
- Object[] toArray(); //将此集合转换成数组 ;
遍历元素(重点)
增强 for(因为无下标)
or(Object object : collection){ }
使用迭代器
//haNext(); 有没有下一个元素 //next(); 获取下一个元素 //remove(); 删除当前元素 Iterator it = collection.iterator(); while (it.hasNext()) { String s = (String) it.next(); System.out.println(s); // 可以使用it.remove(); 进行移除元素 // collection.remove(); 不能用collection其他方法 会报并发修改异常
列表迭代器
List list = new ArrayList(); list.add("aaa"); list.add("bbb"); list.add("ccc"); ListIterator li = list.listIterator(); while(li.hasNext()){ System.out.println(li.nextIndex() + ":" + li.next()); //从前往后遍历 } while(li.hasPrevious()){ System.out.println(li.previousIndex() + ":" + li.previous()); //从后往前遍历 }
List 子接口
- 特点:有序,有下标,元素可以重复;
- 方法:
- list.add(int index, Object o) //在 index 位置插入对象 o
- list.addAll(int index, Collection c) //将一个集合中的元素添加到此集合中的 index 位置
- list.get(int index) //返回集合中指定位置的元素
- list.remove(Object o) list.remove(index) //删除集合中指定元素或指定位置的元素
- list,isEmpty() //判断是否为空
- list.subList(int fromIndex, int toIndex) //返回 fromindex 和 toindex 之间的集合元素,含头不含尾
public static void main(String[] args) {
List list = new ArrayList<>();
list.add("apple");
list.add("xiaomi");
list.add("huawei");
System.out.println("元素个数:"+ list.size());
System.out.println(list.toString());
// list.remove("xiaomi");
// System.out.println("元素个数:"+ list.size());
// list.remove(0);
// System.out.println("元素个数:"+ list.size());
System.out.println("----------------for遍历-------------------");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
System.out.println("----------------增强for-------------------");
for (Object o : list) {
System.out.println(o);
}
System.out.println("----------------使用迭代器进行遍历-------------------");
Iterator it1 = list.iterator();
while (it1.hasNext()){
System.out.println(it1.next());
}
System.out.println("----------------使用列表迭代器进行正序遍历-------------------");
ListIterator it2 = list.listIterator();
while (it2.hasNext()){
System.out.println(it2.next());
}
//从后向前
System.out.println("----------------使用列表迭代器进行逆序遍历-------------------");
while (it2.hasPrevious()){
System.out.println(it2.previous());
}
System.out.println("-----------------------------------------------------------");
//判断是否存在
System.out.println(list.contains("xiaomi"));
//判断是否为空
System.out.println(list.isEmpty());
System.out.println("-----------------------------------------------------------");
//指定位置输出,含头不含尾
System.out.println(list.subList(1,2));
}
List 实现类
ArrayLsit [重点]:
- 数组结构实现,查询快,增删慢;
- JDK1.2 版本,运行效率快,线程不安全。
Vector:
- 数组结构实现,查询快,增删慢;
- JDK1.0 版本,运行效率慢,线程安全。
LinkedList:
- 链表结构实现,增删快,查询慢。
ArrayList 的使用
创建集合 ArrayList arrayList = new ArrayList<>();
添加元素
arrayList.add();
删除元素
arrayList.remove(new Student("name", 10));
这里重写了 equals(this == obj) 方法
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if(obj instanceof Student){
Student s = (Student)obj;
//4 比较属性
return this.name.equals(s.getName()) && this.age == s.getAge();
}
return false;
}
遍历元素【重点】
- 迭代器
Iterator it = arrayList.iterator(); while(it.hasNext()){ Student s = (Student)it.next(); //强转 }
- 列表迭代器
ListIterator li = arrayList.listIterator(); while(li.hasNext()){ Student s = (Student)li.next(); //从前往后遍历 } while(li.hasPrevious()){ Student s = (Student)li.previous();//从后往前遍历 }
ArrayList 源码分析
private static final int DEFAULT_CAPACITY = 10;
//默认容量 10;注意:如果没有向集合中添加任何元素时,容量 0,添加一个后,容量为 10;
每次扩容是原来的 1.5 倍;
private static final Object[] EMPTY_ELEMENTDATA = {};
存放元素的数组;private int size;
实际元素个数,一定小于容量;
Vector 类
- 创建集合
Vector vector = new Vector<>();
- 增加、删除、判断同上与 list 相同
- 遍历中枚举器遍历
Enumeration en = vector.elements();
while(en.hasMoreElements()){
String o = (String)en.nextElement();
sout(o);
}
LinkedList
ArrayLsit [重点]:
- 数组结构实现,查询快,增删慢;
- JDK1.2 版本,运行效率快,线程不安全。
Vector:
- 数组结构实现,查询快,增删慢;
- JDK1.0 版本,运行效率慢,线程安全。
LinkedList:
- 链表结构实现,增删快,查询慢。
LinkedList linkedList = new LinkedList();
主要方法与 ArrayList()相同;遍历方法,for, Iterator;
泛型
- 本质是参数化类型,把类型作为参数传递
- 常见形式有泛型类、泛型接口、泛型方法
- 语法
<T, V> T 成为类型占位符,表示一种引用类型,可以写多个逗号隔开 - 好处
- 提高代码重用性
- 防止类型转换异常,提高代码安全性
泛型类
/**
* 泛型类
* T是类型占位符,表示一种引用类型,如果编写多个使用逗号隔开
*/
public class MyGeneric<T> {
T t;
public void show(T t) {
System .out.println(t);
}
public T getT() {
return t;
}
}
MyGeneric<String> myGeneric = new MyGeneric<String>();
myGeneric.t = "hello";
myGeneric.show("大家好,加油");
String str = myGeneric.getT();
System.out.println(str);
MyGeneric<Integer> myGeneric2 = new MyGeneric<>();
myGeneric2.t = 100;
myGeneric2.show(200);
Integer integer = myGeneric2.getT();
System.out.println(integer);
泛型接口
/**
* 泛型接口
* 语法, 接口名<T>
* 注意,不能泛型静态常量
*/
public interface MyInterface<T> {
String name = "张三";
T server(T t);
}
public class MyInterfaceImpl implements MyInterface<String> {
@Override
public String server(String t) {
System.out.println(t);
return t;
}
}
public class MyInterfanceImpl2<T> implements MyInterface<T> {
@Override
public T server(T t) {
System.out.println(t);
return t;
}
}
//myinterface
MyInterfaceImpl impl = new MyInterfaceImpl();
impl.server("xxxxxxxxxx");
//myinterfaceimpl2
MyInterfanceImpl2<Integer> impl2 = new MyInterfanceImpl2<>();
impl2.server(1000);
MyInterfanceImpl2<String> impl3 = new MyInterfanceImpl2<>();
impl3.server("ccccccccccc");
泛型方法
/**
* 泛型方法
* 语法: <T> 返回值类型
*/
public class MyGenericMethod {
public <T> T show(T t) {
System.out.println("泛型方法" + t);
return t;
}
}
//泛型方法
MyGenericMethod myGenericMethod = new MyGenericMethod();
myGenericMethod.show("bbbb");
myGenericMethod.show(200);
myGenericMethod.show(1.23);
泛型集合
概念:参数化类型、类型安全的集合,强制集合元素的类型必须一致
特点:
- 编译时即可检查,而非运行时抛出异常
- 访问时,不必类型转换(拆箱)
- 不同泛型之间应用不能相互赋值,泛型不存在多态
ArrayList<String> arrayList = new ArrayList<String>();
arrayList.add("xxx");
arrayList.add("yyy");
for (String s : arrayList) {
System.out.println(s.toString());
}
ArrayList<Student> arrayList2 = new ArrayList<>();
Student s1 = new Student("刘德华", 20);
Student s2 = new Student("郭富城", 21);
Student s3 = new Student("梁朝伟", 22);
arrayList2.add(s1);
arrayList2.add(s2);
arrayList2.add(s3);
Iterator iterator = arrayList2.listIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
Set 集合
- 特点:无序,无下标、元素不可重复;
- 方法:全部继承自 Collection 中的方法;
- 增、删、遍历、判断与 collection 相同;
Set<String> set = new HashSet<>();
//1. 添加数据
set.add("小米");
set.add("华为");
set.add("苹果");
System.out.println("数据个数:" + set.size());
System.out.println(set.toString());
//2 删除数据
//set.remove("小米");
System.out.println("----------------增强for--------------");
for (String s : set) {
System.out.println(s);
}
System.out.println("----------------迭代器--------------");
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
Set 实现类
HashSet[重点]:
- 无序,基于 HashCode 实现元素不重复;
- 当存入元素的哈希码相同时,会调用 equals 进行确认,如结果为 true,则拒绝后者存入;
存储结构:哈希表(数组+链表+红黑树)
存储过程(重复依据)
根据 hashCode 计算保存的位置,如果位置为空,直接保存,若不为空,进行第二步
再执行 equals 方法,如果 equals 为 true,则认为是重复,否则形成链表
基于 HashCode 计算元素存放位置
利用 31 这个质数,减少散列冲突
31 提高执行效率
31 * i = (i << 5) - i
转为移位操作当存入元素的哈希码相同时,会调用 equals 进行确认,如果结果为 true,则拒绝后者存入
public static void main(String[] args) {
HashSet<String> hashSet = new HashSet<>();
hashSet.add("刘德华");
hashSet.add("郭富城");
hashSet.add("梁朝伟");
System.out.println("元素个数:" + hashSet.size());
System.out.println(hashSet.toString());
//删除元素
// hashSet.remove("刘德华");
//增强for
System.out.println("---------------增强for-----------------");
for (String s : hashSet) {
System.out.println(s);
}
System.out.println("---------------迭代器-----------------");
Iterator it= hashSet.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
//重写equals + hashCode
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
name.equals(person.name);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
TreeSet:
基于排列顺序实现元素不重复;
实现了 sortedset 接口,对集合元素自动排序;
元素对象的类型必须实现,Comparable 接口,指定排序规则;
通过 CompareTo 方法确定是否为重复元素;
存储结构:红黑树
创建集合
TreeSet<String> treeSet = new TreeSet<>()
添加元素
treeSet.add();
删除元素
treeSet.remove();
遍历 1. 增强 for 2. 迭代器
判断
treeSet.contains();
补充: TreeSet 集合的使用
- Comparator 实现定制比较(比较器)
- Comparable 可比较的
TreeSet<Person> persons = new TreeSet<>(new Comparator<Person>() { @Override public int compare(Person o1, Person o2) { int n1 = o1.getAge() - o2.getAge(); int n2 = o1.getName().compareTo(o2.getName()); return n1 == 0 ? n2 : n1; } });
TreeSet 案例:
- 要求使用 TreeSet 集合实现字符串按照长度进行排序;
- 实现方法:重写 Comparator 的 compare 方法
/**
* 要求使用TreeSet 集合实现字符串按照长度进行排序;
* helloWord zhang lisi wangwu beijing xian nanjing
* @authoe sx
*/
public static void main(String[] args) {
TreeSet treeSet = new TreeSet<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
int n1 = o1.length()-o2.length();
int n2 = o1.compareTo(o2);
return n1==0?n2:n1;
}
});
//添加数据
treeSet.add("helloWord");
treeSet.add("zhang");
treeSet.add("lisi");
treeSet.add("wangwu");
treeSet.add("beijing");
treeSet.add("xian");
treeSet.add("nanjing");
System.out.println(treeSet.toString());
}
[lisi, xian, zhang, wangwu, beijing, nanjing, helloWord]
Map
- 特点:存储一对数据(Key, Value),无序、无下标,键不可以重复,值可重复;
- 方法:
- V put(K key, V value) 将对象存到集合中,关联键值 ;
- Object get(Object key) 根据键获得对应的值 ;
- Set
返回所有的 Key; - Collection
values() 返回包含所有值的 Collection 集合; - Set<Map.Entry<K, V>> 键值匹配的 Set 集合;
Map 接口的使用:
两种遍历方法:
- keySet()把 map 中的 key 存在 set 集合中;entrySet()把(key,value)封装成一个 entry 类型(映射对);
- entrySet()效率高于 keySet()方法;因为 entrySet 一次性把 key,value 取出。
public static void main(String[] args) {
//创建Map集合
Map<String, String> map = new HashMap<>();
map.put("cn", "中国");
map.put("uk", "英国");
map.put("fc", "法国");
map.put("gm", "德国");
System.out.println("元素个数:" + map.size());
System.out.println(map.toString());
//删除
// map.remove("fc");
// System.out.println(map.toString());
//遍历
//使用keySet()方法
System.out.println("----------keySet---------------");
Set<String> keySet = map.keySet();
for (String key : keySet) {
System.out.println(key + ":" + map.get(key));
}
System.out.println("----------entrySet---------------");
// 使用entrySet方法
Set<Map.Entry<String, String>> entries = map.entrySet();
for (Map.Entry<String, String> entry : entries) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
HashMap[重点]
JDK1.2 版本,线程不安全,运行效率快,允许用 null 作为 key 或是 value。
存储结构:哈希表(数组+链表+红黑树)
使用 key 可使 hashcode 和 equals 作为重复
增、删、遍历、判断与上述一致
HashMap 常用方法:alt + insert 重写 equals 和 hashcode 去掉重复项;
public static void main(String[] args) {
//创建集合
HashMap<Student, String> students = new HashMap<>();
//添加元素
Student s1 = new Student("孙悟空", 100);
Student s2 = new Student("猪八戒", 101);
Student s3 = new Student("沙和尚", 102);
students.put(s1, "花果山");
students.put(s2, "黑风洞");
students.put(s3, "流沙河");
students.put(s3, "流沙河");
students.put(new Student("沙和尚", 102), "流沙河");
System.out.println(students.size());
System.out.println(students.toString());
//删除元素
// students.remove(s1);
// System.out.println("删除之后:" + students.size());
System.out.println("+++++++++++++++++++++++++++++++++++++++");
//遍历
for (Student key : students.keySet()) {
System.out.println(key.toString() + ":" + students.get(key));
}
System.out.println("+++++++++++++++++++++++++++++++++++++++");
//使用entrySet()
for (Map.Entry<Student, String> studentStringEntry : students.entrySet()) {
System.out.println(studentStringEntry.getKey() + ":" + studentStringEntry.getValue());
}
System.out.println("+++++++++++++++++++++++++++++++++++++++");
//判断
System.out.println(students.containsKey(s1));
System.out.println(students.containsValue("流沙河"));
}
HashMap 源码分析:
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
//默认初始容量大小1 左移 4 位 = 2^4 = 16,底层移位计算最快;
static final int MAXIMUM_CAPACITY = 1 << 30;
//最大容量大小;DEFAULT_LOAD_FACTOR
//默认加载因子;static final int TREEIFY_THRESHOLD = 8;
static final int MIN_TREEIFY_CAPACITY = 64;
//链表长度大于 8,数组长度大于 64 时存储结构改为红黑树;static final int UNTREEIFY_THRESHOLD = 6;
//链表长度小于 6,变回链表结构
源码分析总结:
- HashMap 刚创建时,table 是 null,节省空间,当添加第一个元素时,table 容量调整为 16
- 当元素个数大于阈值 75%(16*0.75 = 12)时,会进行扩容,扩容后的大小为原来的两倍,目的是减少调整元素的个数
- jdk1.8 当每个链表长度 >8 ,并且数组元素个数 ≥64 时,会调整成红黑树,目的是提高效率
- jdk1.8 当链表长度 <6 时 调整成链表
- jdk1.8 以前,链表是头插入,之后为尾插入
Hashtable
JDK1.0 版本,线程安全,运行效率慢,不允许 NULL 作为 key 或是 value。
基本不用。
Properties
Hashtable 的子类,需要 key 和 value 都是 String。在流中使用较多,通常用于配置文件的读取。在流的学习中进行使用。
public static void main(String[] args) {
//创建集合
Properties properties = new Properties();
//添加数据
properties.setProperty("username", "zhangsan");
properties.setProperty("age", "20");
System.out.println(properties);
//遍历
//keyset
for (Object o : properties.keySet()) {
System.out.println(o);
}
//entrySet
for (Map.Entry<Object, Object> objectObjectEntry : properties.entrySet()) {
System.out.println(objectObjectEntry);
}
//stringPropertyName
for (String stringPropertyName : properties.stringPropertyNames()) {
System.out.println(stringPropertyName + ":" + properties.getProperty(stringPropertyName));
}
}
和流有关的方法
//和流有关的方法
PrintWriter pw = new PrintWriter("D:\\workspace\\IDEA-workspace\\kuangshen_java\\集合框架\\src\\com\\shan\\collection\\properties\\print.properties");
properties.list(pw);
pw.close();
store 方法
FileOutputStream fos = new FileOutputStream("D:\\workspace\\IDEA-workspace\\kuangshen_java\\集合框架\\src\\com\\shan\\collection\\properties\\print.properties");
properties.store(fos, "注释");
fos.close();
结果:
#\u6CE8\u91CA properties文件不允许中文
#Sat Apr 10 01:08:21 CST 2021
age=20
username=zhangsan
load 方法
// load方法
Properties properties2 = new Properties();
FileInputStream fis = new FileInputStream("D:\\workspace\\IDEA-workspace\\kuangshen_java\\集合框架\\src\\com\\shan\\collection\\properties\\\\print.properties");
properties2.load(fis);
fis.close();
System.out.println(properties2.toString());
TreeMap
实现了 SortedMap 接口(是 map 的子接口),可以对 key 自动排序
public static void main(String[] args) {
TreeMap<Student, String> students = new TreeMap<>();
Student s1 = new Student("孙悟空", 103);
Student s2 = new Student("猪八戒", 101);
Student s3 = new Student("沙和尚", 102);
students.put(s1, "花果山");
students.put(s2, "黑风洞");
students.put(s3, "流沙河");
students.put(s3, "流沙河");
students.put(new Student("沙和尚", 102), "流沙河");
System.out.println(students.size());
System.out.println(students.toString());
//删除元素
// students.remove(s1);
// System.out.println("删除之后:" + students.size());
System.out.println("+++++++++++++++++++++++++++++++++++++++");
//遍历
for (Student key : students.keySet()) {
System.out.println(key.toString() + ":" + students.get(key));
}
System.out.println("+++++++++++++++++++++++++++++++++++++++");
//使用entrySet()
for (Map.Entry<Student, String> studentStringEntry : students.entrySet()) {
System.out.println(studentStringEntry.getKey() + ":" + studentStringEntry.getValue());
}
System.out.println("+++++++++++++++++++++++++++++++++++++++");
//判断
System.out.println(students.containsKey(s1));
System.out.println(students.containsValue("流沙河"));
}
定制比较器:重写 compare 方法:
可以自定义规则
TreeMap<Student, String> students = new TreeMap(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
int n1 = o1.getAge() - o2.getAge();
int n2 = o1.getName().compareTo(o2.getName());
return n1 == 0 ? n2 : n1;
}
});
Collection 工具类
概念:集合工具类,定义了除了存取以外的集合常用方法;
1. 排序 Collections.sort()
2. 直接二分查找int i = Collections.binarySearch(list, x);
成功返回索引;
其他方法 :
复制Collections.copy(dest, list);
反转Collections.reverse(list);
打乱ollections.shuffle(list);
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(20);
list.add(3);
list.add(14);
list.add(50);
list.add(36);
//排序
System.out.println("排序之前---" + list.toString());
Collections.sort(list);
System.out.println("排序之后---" + list.toString());
int i = Collections.binarySearch(list, 3);
System.out.println("第" + i + "位");
List<Integer> dest = new ArrayList<>();
//开辟空间,否则dest为空,无法直接copy放入
for (int j = 0; j < list.size(); j++) {
dest.add(0);
}
Collections.copy(dest, list);
System.out.println(dest.toString());
//翻转
Collections.reverse(list);
System.out.println(list);
//打乱
Collections.shuffle(list);
System.out.println(list);
}
补充:
// list转成数组
Integer[] arr = list.toArray(new Integer[10]);
sout(arr.length);
sout(Array.toString(arr));
// 数组转成集合
// 此时为受限集合,不能 添加和删除!
String[] name = {"张三","李四","王五"};
List<String> list2 = Arrays.asList(names);
// 把基本类型数组转为集合时,需要修改为包装类
Integer[] nums = {100, 200, 300, 400, 500};
List<Integer> list3 = Arrays.asList(nums);