java集合框架笔记
集合
什么是集合?
概念:集合是对象的容器,实现了对对象常用的操作。
和数组的区别
1.数组的长度固定,集合长度不固定。
2.数组可以存储基本类型和引用类型,集合只能存储引用类型、
使用前注意导入相应的包 – java.util *
Collection体系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A6xREBAC-1622650775788)(D:\git\Git\Typora\Typora\1998506-20200830225616629-1434055405.png)]
Collection父接口:
特点:代表任意一组类型的对象,无序,无下标,不能重复。
创建集合的方法: Collection collection = new ArrayList(); --导入java.util.ArrayList
需要掌握:
-
增加元素 ------ add方法
-
删除元素 -------- remove方法 (删除的是对象 object,因为无下标) clear方法(完全删除)
-
(重点) 遍历元素
1.使用增强for(无下标)
for (Object object:collection) { 输出
2.使用迭代器遍历(迭代器是专门用来遍历集合的一种方式)
1.hasNext(); --//有无下一个元素
2. next(); – 获取下一个元素;
3. remove(); – // 删除当前元素
Iterator it = collection.iterator();
while(it.hasNext()){
//注意 不能使用collection方法删除元素
String s = (String) it.next(); 强制转换
//输出 s
// 可以使用 it.remove()方法删除元素,但不能使用collection.remove()删除元素,会报并发修改异常
}
-
判断 是否存在某一元素 或是否为空
代码 collection.contains();
collection.inEmpty();
List子接口
特点 有序,有下标,元素可以重复
创建集合对象
List list = new ArrayList<>();
常用方法
1.添加元素— list.add();
2.删除元素 —
1.使用索引删除 list.remove(索引);
当删除数字与索引矛盾时,对数字进行强制转换 list.remove((Object) 20).— list.remove(new integer(20));
-
遍历元素(重点)
-
使用for循环
for(int i=0;i<list.size();i++){ sout(list.get(i)); }
2.使用增强for
for (Object object : list){ sout(object) }
3.使用迭代器
Iterator it = list.iterator(); while(it.hasNext()){ sout(it.next()); }
4.使用列表迭代器
(注意与迭代器的区别) 列表迭代器可以向前或者向后的遍历,增加,删除,修改元素。
ListIterator lit = list.listIterator(); System.out.println("---使用列表迭代器从前往后-------"); while(lit.hasNext()){ System.out.println(lit.nextIndex()+":"+lit.next()); // nextIndex 是下一个的序号标 } System.out.println("---使用列表迭代器从后往前-------"); while(lit.hasPrevious()){ sout(lit.previousIndex() + ":" + lit.previous() ); }
5.判断
sout(list.contains(object)) // 确定的名字 sout(list.isEmpty()) 是否为空
6.获取下标索引
list.indexOf();
7.返回子集合sublist(x,y) --左开右闭
List SubList = list.subList(x,y); sout(SubList);
List 实现类
ArrayList (重点)
-
数组结构实现,必须要连续空间,查询快,增删慢
-
运行效率快,线程不安全。
Vector
ArrayList
创建集合 ArrayList arrayList() = new ArrayList();
-
添加元素: arrayList.add();
-
删除元素: arrayList.remove();
重写了equals(this == obj)方法
public boolean equals(Object obj){ //1 判断是不是同一个对象 if(this == obj){ return true; } //2 判断是否为空 if(obj == null){ return false; } //3 判断是否是Student类型 if(obj instanceof Student){ Student == (Student)obj; //4 比较属性 if(this.name.equals(s.getName()) && this.age == s.getAge()){ return true; } } //5 不满足条件返回false return false; }
-
-
遍历元素(重点)
1.使用迭代器
Iterator it = arrayList.iterator();
while(it.hasNext()){
Student s = (Student)it.next(); //强转
}
2.列表迭代器
ListIterator li = arrayList.listIterator();
while(li.hasNext()){
Student s = (Student)li.next(); //从前往后遍历
}
while(li.hasPrevious()){
Student s = (Student)li.previous();//从后往前遍历
}
4.判断
arrayList.contains()
arrayList.isEmpty();
5.查找
arrayList.indexOf();
— 源码分析
DEFAULT_CAPACITY = 10; //默认容量
//注意:如果没有向集合中添加任何元素时,容量0,添加一个后,容量为10
//每次扩容是原来的1.5倍
elementData存放元素的数组
size 实际元素个数
vector
创建集合 – Vector vector = new Vector();
大部分与List相同,特别点:可以使用枚举器遍历
Enumeration en = vector.elements();
while(en.hasmoreElements()){
String o = (String)en.nextElements();
sout(o);
}
LinkedList
创建链表集合 LinkedList li = new LinkedList<>();
常用方法与list
泛型
-
本质是参数化类型,把类型作为参数传递
-
常见形式有泛型类,泛型接口,泛型方法
-
语法: T成为类型占位符。表示一种引用类型,可以用多个逗号隔开
-
好处: 1.提高代码重用性。2.防止类型转换异常,提高代码安全性
泛型类
//写一个泛型类 public class MyGeneric<T>{ //1.创建变量 T t; // 2.泛型作为方法的参数 public void show(T t){ sout(t); } //3.泛型作为方法的返回值 public T getT(){ return t; } }
//使用泛型类 public class TestGeneric{ public static void main(String[] args){ //使用泛型类创建对象 // 注意: 1. 泛型只能使用引用类型 // 2. 不用泛型类型对象之间不能相互赋值 MyGeneric<String> myGeneric = new MyGeneric<String>(); myGeneric.t = "hello"; myGeneric.show("hello world!"); String string = myGeneric.getT(); MyGeneric<Integer> myGeneric2 = new MyGeneric<Integer>(); myGeneric2.t = 100; myGeneric2.show(200); Integer integer = myGeneric2.getT(); } }
泛型接口
语法:接口名
注意: 不能泛型静态常量
泛型方法
语法:返回值类型
public class MyGenericMethod{
//泛型方法
public <T> T show(T t){
sout("泛型方法" + t);
return t;
}
}
//调用
MyGenericMethod myGenericMethod = new MyGenericMethod();
myGenericMethod.show("字符串");// 自动类型为字符串
myGenericMethod.show(200);// integer类型
myGenericMethod.show(3.14);// double类型
泛型集合
概念:参数化类型、类型安全的集合,强制集合元素的类型必须一致
特点:
- 编译时即可检查,而非运行时抛出异常
- 访问时,不必类型转换(拆箱)
- 不同泛型之间应用不能相互赋值,泛型不存在多态
Set集合
Set子接口
特点:无序,无下标,元素不可重复
方法:全部继承字Collection的方法
//创建集合
Set<String> set = new HashSet<>();
// 1.添加元素 --无序
//删除
// 遍历
//1.使用增强for
//2.使用迭代器
//判断
Set实现类
### HashSet 重点
基于HashCode计算元素存放位置
当存入元素的哈希码相同时,会调用equals方法确认,若结果为true,则拒绝存入
HashSet集合的使用,存储结构(哈希表):(数组加链表加红黑树)
//新建集合
HashSet<String> hashSet = new HashSet<String>();
// 1.添加元素
//删除
//遍历
//3.1 增强for
//3.2 迭代器
HashSet 的使用
存储结构: 哈希表(数组加链表+红黑树)
存储过程(重复依据)
(1)。根据HashCode,计算保存的位置
(2)。再执行equals方法,如果equals为true,则认为是重复,否则,形成链表
实现类
HashSet<Person> persons = new HashSet<Person>();
//1.添加数据
//遍历
//3.1 增强for
//3.2 迭代器
//判断 contains , isEmpty();
一个小问题://注:假如相同属性便认为是同一个对象,该怎么做?
答案:可以重写hashCode和equals代码:
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
//为什么使用pirme = 31?
1.31是一个质数,这样的数字在计算时可以尽量减少散列冲突。
2.可以提高执行效率,因为31*i=(i<<5)-i,31乘以一个数可以转换成移位操作,这样能快一点;但是也有网上一些人对这两点提出质疑。
TreeSet
- 基于排序顺序实现不重复。
- 实现了SortedSet接口,对集合元素自动排序。
- 元素对象的类型必须实现Comparable接口,指定排序规则。
- 通过CompareTo方法确定是否为重复元素。
使用TreeSet保存数据
存储结构:红黑树
要求:元素必须要实现Comparable接口 compareTo()方法返回值为0,认为是重复元素
//创建集合
TreeSet<Person> persons = new TreeSet<>(); //有创建Person类
基本操作与Conection一致
查看Comparable接口的源码,发现只有一个compareTo抽象方法,在/Person/的类中实现它:
public class Person implements Comparable<Person>{
@Override
//1.先按姓名比
//2.再按年龄比
public int compareTo(Person o) {
int n1=this.getName().compareTo(o.getName());
int n2=this.age-o.getAge();
return n1==0?n2:n1;
}
}
除了实现Comparable接口里的比较方法,TreeSet也提供了一个带比较器Comparator的构造方法,使用匿名内部类来实现它:
/**
* TreeSet的使用
* Comparator:实现定制比较(比较器)
*/
public class Demo5 {
public static void main(String[] args) {
TreeSet<Person> persons=new TreeSet<Person>(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;
}
});
Person p1=new Person("tang",21);
Person p2=new Person("he", 22);
Person p3=new Person("yu", 21);
persons.add(p1);
persons.add(p2);
persons.add(p3);
System.out.println(persons.toString());
}
}
接下来我们来做一个小案例:
/**
* 要求:使用TreeSet集合实现字符串按照长度进行排序
* helloworld tangrui hechengyang wangzixu yuguoming
* Comparator接口实现定制比较
*/
public class Demo6 {
public static void main(String[] args) {
TreeSet<String> treeSet=new TreeSet<String>(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("helloworld");
treeSet.add("tangrui");
treeSet.add("hechenyang");
treeSet.add("yuguoming");
treeSet.add("wangzixu");
System.out.println(treeSet.toString());
//输出[tangrui, wangzixu, yuguoming, hechenyang, helloworld]
}
}
Map体系接口
Map接口的特点:
1.用于存储任意键值对(key-value)
2.键: 无序,无下标,不允许重复(唯一)
3.值:无序,无下标,允许重复、
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fi8MFhw0-1622650775791)(C:\Users\阳爹\Desktop\fe47000396f10c5dba2a.png)]
Map集合概述
特点:存储一对数据(key-value),无序,无下标,键不可重复、
导入包
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
Map接口的使用
特点:1.存储键值对。2.键不能重复,值可以重复,3.无序
//创建Map集合
Map<String,String> map = new HashMap<>();
//2.删除// map.remove("cn");
//遍历
// 3.1使用keySet()
Set<String> keyset = map.keySet();
//3.2使用entrySet方法
Set<Map.Entry<String,String>> entries = map.entrySet();
//判断
System.out.println(map.containsKey("cn"));
System.out.println(map.containsValue("泰国"));
Map集合的实现类
HashMap集合的使用(重点)
存储结构:哈希表(数组加链表加红黑树)
使用key可 hashcode和equals作为重复 保证没有重复
//创建集合
HashMap<Student,String> students =new HashMap<Student,String>();
//创建hashmap之后没有添加元素 table=null, size=0。 目的:节省空间
//添加元素 - put();
//2.删除
//3.遍历
//3.1 使用keySet();
for(Student key:students.keySet()){
System.out.println(key.toString() + students.get(key));
}
//3.2使用entrySet();
for (Map.Entry<Student,String> entry:students.entrySet()
) {
System.out.println(entry.getKey() + entry.getValue());
}
//4.判断
System.out.println(students.containsKey(new Student("孙悟空",100)));
System.out.println(students.containsValue("杭州"));
-
HashMap源码分析
-
默认初始化容量:
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
- 数组最大容量:
static final int MAXIMUM_CAPACITY = 1 << 30;
- 数组最大容量:
-
默认加载因子:
static final float DEFAULT_LOAD_FACTOR = 0.75f;
-
链表调整为红黑树的链表长度阈值(JDK1.8):
static final int TREEIFY_THRESHOLD = 8;
-
红黑树调整为链表的链表长度阈值(JDK1.8):
static final int UNTREEIFY_THRESHOLD = 6;
-
链表调整为红黑树的数组最小阈值(JDK1.8):
static final int MIN_TREEIFY_CAPACITY = 64;
-
HashMap存储的数组:
transient Node<K,V>[] table;
-
HashMap存储的元素个数:
transient int size;
- 默认加载因子是什么?
- 就是判断数组是否扩容的一个因子。假如数组容量为100,如果HashMap的存储元素个数超过了100*0.75=75,那么就会进行扩容。
- 链表调整为红黑树的链表长度阈值是什么?
- 假设在数组中下标为3的位置已经存储了数据,当新增数据时通过哈希码得到的存储位置又是3,那么就会在该位置形成一个链表,当链表过长时就会转换成红黑树以提高执行效率,这个阈值就是链表转换成红黑树的最短链表长度;
- 红黑树调整为链表的链表长度阈值是什么?
- 当红黑树的元素个数小于该阈值时就会转换成链表。
- 链表调整为红黑树的数组最小阈值是什么?
- 并不是只要链表长度大于8就可以转换成红黑树,在前者条件成立的情况下,数组的容量必须大于等于64才会进行转换。
HashMap的数组table存储的就是一个个的Node<K,V>类型,很清晰地看到有一对键值,还有一个指向next的指针(以下只截取了部分源码):
static class Node<K,V> implements Map.Entry<K,V> { final K key; V value; Node<K,V> next; }
- 默认加载因子是什么?
HashSet源码分析
了解完HashMap之后,再回过头来看之前的HashSet源码,为什么放在后面写你们看一下源码就知道了(部分):
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
public HashSet() {
map = new HashMap<>();
}
}
可以看见HashSet的存储结构就是HashMap,那它的存储方式是怎样的呢?可以看一下add方法:
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
很明了地发现它的add方法调用的就是map的put方法,把元素作为map的key传进去的。
Hashtable
-
JDK1.0版本,线程安全,运行效率慢;不允许null作为key或是value。
-
初始容量11,加载因子0.75。
这个集合在开发过程中已经不用了,稍微了解即可。
Properties
- Hashtable的子类,要求key和value都是String。通常用于配置文件的读取。
它继承了Hashtable的方法,与流关系密切,此处不详解。
TreeMap
实现了SortedMap接口(是Map的子接口),可以对key自动排序
/**
* TreeMap的使用
* 存储结构:红黑树
*/
public class Demo3 {
public static void main(String[] args) {
TreeMap<Student, Integer> treeMap=new TreeMap<Student, Integer>();
Student s1=new Student("tang", 36);
Student s2=new Student("yu", 101);
Student s3=new Student("he", 10);
//1.添加元素
treeMap.put(s1, 21);
treeMap.put(s2, 22);
treeMap.put(s3, 21);
//不能直接打印,需要实现Comparable接口,因为红黑树需要比较大小 - 改写compareTo方法(下面)
System.out.println(treeMap.toString());
//2.删除元素
treeMap.remove(new Student("he", 10));
System.out.println(treeMap.toString());
//3.遍历
//3.1 使用keySet()
for (Student key : treeMap.keySet()) {
System.out.println(key+" "+treeMap.get(key));
}
//3.2 使用entrySet()
for (Entry<Student, Integer> entry : treeMap.entrySet()) {
System.out.println(entry.getKey()+" "+entry.getValue());
}
//4.判断
System.out.println(treeMap.containsKey(s1));
System.out.println(treeMap.isEmpty());
}
}
在学生类中实现Comparable接口:
public class Student implements Comparable<Student>{
@Override
public int compareTo(Student o) {
int n1=this.id-o.id;
return n1;
}
除此之外还可以使用比较器来定制比较
TreeMap<Student, Integer> treeMap2=new TreeMap<Student, Integer>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
// 略
return 0;
}
});
Collections工具类
概念:集合工具类,定义了除了存取以外的集合常用方法。
方法: //sort排序 //binarySearch 二分查找 返回下标 // copy复制 //reverse() 反转
//补充:list转成数组 //数组转成集合//把数组转成集合,这个集合是一个受限制的集合,不能增加和删除
//把基本类型数组转成集合时,需要修改为包装类型
public class demo4 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(5);
list.add(10);
list.add(78);
list.add(8);
//sort排序
System.out.println("排序之前" + list.toString());
// Collections.sort(list);
System.out.println("排序之后" + list.toString());
//binarySearch 二分查找 返回下标
int i = Collections.binarySearch(list, 10);
System.out.println(i);
// copy复制
List<Integer> dest = new ArrayList<>();
for (int j = 0; j < list.size(); j++) {
dest.add(0);
}
Collections.copy(dest, list); //要求集合大小一样
System.out.println(dest.toString());
//reverse() 反转
Collections.reverse(list);
System.out.println(list.toString());
//shuffle 打乱
Collections.shuffle(list);
System.out.println("打乱之后" + list.toString());
//补充:list转成数组
System.out.println("---------list转成数组-----");
Integer[] arr = list.toArray(new Integer[4]);
System.out.println(arr.length);
System.out.println(Arrays.toString(arr));
//数组转成集合
System.out.println("-----数组转成集合------");
String[] names = {"张三","例子","李四"};
List<String> list2 = Arrays.asList(names);
System.out.println(list2);
//把数组转成集合,这个集合是一个受限制的集合,不能增加和删除
System.out.println("-----");
//把基本类型数组转成集合时,需要修改为包装类型
Integer[] nums = {100,200,300,400};
List<Integer> list3 = Arrays.asList(nums);
System.out.println(list3);
}
}
erse(list);
System.out.println(list.toString());
//shuffle 打乱
Collections.shuffle(list);
System.out.println("打乱之后" + list.toString());
//补充:list转成数组
System.out.println("---------list转成数组-----");
Integer[] arr = list.toArray(new Integer[4]);
System.out.println(arr.length);
System.out.println(Arrays.toString(arr));
//数组转成集合
System.out.println("-----数组转成集合------");
String[] names = {"张三","例子","李四"};
List<String> list2 = Arrays.asList(names);
System.out.println(list2);
//把数组转成集合,这个集合是一个受限制的集合,不能增加和删除
System.out.println("-----");
//把基本类型数组转成集合时,需要修改为包装类型
Integer[] nums = {100,200,300,400};
List<Integer> list3 = Arrays.asList(nums);
System.out.println(list3);
}
}