集合框架
集合的概念
- **概念:**对象的容器,定义了对多个对象进行操作的常用方法。可实现数组的功能。
- 和数组的区别:
- 数组长度固定,集合长度不固定。
- 数组可以存储基本类型和引用类型,集合只能存储引用类型。
- 位置:java.util.*;里
Collection体系集合
Collection父接口
- 特点:代表一组任意类型的对象,无序、无下标、不能重复。
- 方法:
- boolean add (Object obj)//添加一个对象。
- boolean addAll(Collection c) //将一个集合中的所有对象添加到此集合中。
- void clear() //清空此集合中的所有对象。
- boolean contains(Object o) //检查此集合中是否包含o对象。
- boolean equals(Object o) //比较此集合是否与指定对象相等。
- boolean isEmpty() //判断此集合是否为空。
- boolean remove (object o) //在此集合中移除o对象。
- int size() //返回此集合中的元素个数。
- 0bject[] toArray() //将此集合转换成数组。
//代码实现
package com.zy.TestCollection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class TestCollection00 {
public static void main(String[] args) {
//创建集合,为接口,要用实现类
Collection collection = new ArrayList();
System.out.println("集合的长度:"+collection.size());//输出:集合的长度:0
//增加方法
collection.add("你好");
collection.add("Hello");
collection.add("world");
System.out.println("集合的长度:"+collection.size());//输出:集合的长度:3
//判断
System.out.println(collection.contains("你好"));//判断是否存在该元素,存在返回true,不存在返回false
System.out.println(collection.isEmpty());//判断是否为空,为空返回true,其他返回false
//使用增强for遍历集合
System.out.println("----------使用增强for遍历----------");
for (Object o : collection) {
System.out.println(o);
}
//输出:----------使用增强for遍历----------
//你好
//Hello
//world
//使用迭代器
System.out.println("----------使用迭代器遍历----------");
Iterator it = collection.iterator();
//迭代器方法
//hasNext()判断有没有下一个,返回boolean类型
//next()获取下一个元素
//remove()删除当前元素
while (it.hasNext())
{
String next = (String) it.next();
System.out.println(next);
//删除不能使用collection.remove(),会报错
it.remove();
}
//输出:----------使用迭代器遍历----------
//你好
//Hello
//world
System.out.println("集合的长度:"+collection.size());//输出:集合的长度:0
}
}
List子接口
- 特点:有序、有下标、元素可以重复。
- 方法:
-
void add(int index,0bject o)//在index位置插入对象。
-
boolean addAll(int index,Collection c)//将一个集合中的元素添加到此集合中的index位置。
-
0bject get(int index)//返回集合中指定位置的元素。
-
List subList(int fromIndex,int toIndex)//返回fromIndex和toIndex之间的集合元素。
-
//代码实现
package com.zy.TestCollection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class TestList {
public static void main(String[] args) {
//创建集合对象
List list = new ArrayList<>();
//添加
list.add("你好");
list.add("World");
list.add(0,"Hello");//自定义添加位置
System.out.println(list.toString());//输出:[Hello, 你好, World]
//删除
// list.remove("你好");//删除对象数据
// list.remove(0);//删除下标对应的数据
// System.out.println(list.toString());//输出:[World]
//获取位置
System.out.println(list.indexOf("你好"));//获取该数据的下标
//输出:1
//遍历
//1.for遍历
System.out.println("------for遍历--------");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));//获取下标输出数据
}
//2.增强for
System.out.println("------增强for遍历--------");
for (Object o : list) {
System.out.println(o);
}
//3.Iterator遍历
System.out.println("------Iterator遍历--------");
Iterator it = list.iterator();
while (it.hasNext())
{
System.out.println(it.next());
}
//4.listIterator遍历
System.out.println("------listIterator遍历--------");
ListIterator listIterator = list.listIterator();
//顺序遍历
System.out.println("------listIterator顺序遍历--------");
while (listIterator.hasNext())
{
System.out.println(listIterator.nextIndex()+":"+listIterator.next());
}
System.out.println("------listIterator逆序遍历--------");
//逆序遍历
while (listIterator.hasPrevious())
{
System.out.println(listIterator.previousIndex()+":"+listIterator.previous());
}
//------for遍历--------
//Hello
//你好
//World
//------增强for遍历--------
//Hello
//你好
//World
//------Iterator遍历--------
//Hello
//你好
//World
//------listIterator遍历--------
//------listIterator顺序遍历--------
//0:Hello
//1:你好
//2:World
//------listIterator逆序遍历--------
//2:World
//1:你好
//0:Hello
List list2 = new ArrayList<>();
list2.add(20);
list2.add(30);
list2.add(40);
list2.add(50);
//int类型被自动装箱
System.out.println(list2.toString());//输出:[20, 30, 40, 50]
//删除
//list2.remove(20)是会报错的,因为默认会是下标删除,20为int类型
//转为对象
list2.remove(new Integer(20));
System.out.println(list2.toString());//输出:[30, 40, 50]
//补充,返回集合中的一段,包前不包括后
List list1 = list2.subList(1, 2);
System.out.println(list1.toString());//输出:[40]
}
}
List实现类
- ArrayList (重点):
- 数据结构实现,查询快、增删慢。
- JDK1.2版本,运行效率快、线程不安全。
- Vector:
- 数据结构实现,查询快、增删慢。
- JDK1.0版本,运行效率慢、线程安全。
- LinkedList:
- 链表结构实现,增删快,查询慢。
ArrayList
//方法实现,其他方法和前面相同,只有一个特殊地方
package com.zy.TestCollection;
import java.util.ArrayList;
public class TestArrayList {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
Student s1 = new Student("张三","22");
Student s2 = new Student("李四","33");
Student s3 = new Student("王五","45");
arrayList.add(s1);
arrayList.add(s2);
arrayList.add(s3);
System.out.println(arrayList);
//删除
arrayList.remove(new Student("张三","22"));//直接写不会成功,重新equals方法后就能成功
System.out.println("集合中的数量:"+arrayList.size());
}
}
//Student类的equals方法重写
public boolean equals(Object obj){
//判断是不是同一个对象
if(this==obj)
{
return true;
}
//判断是否为空
if(obj==null)
{
return false;
}
//判断类型是否为Student
if(obj instanceof Student)
{
//转换类型
Student s = (Student) obj;
//比较属性
if(this.name.equals(s.name) && this.age.equals(s.age))
{
return true;
}
}
return false;
}
源码解析
-
//注意:如果没有向集合中添加任何元素时,容量为0,当执行add()方法后,变为10 //扩容,每次都是原来容量的1.5倍 private static final int DEFAULT_CAPACITY = 10;//默认的容量大小 transient Object[] elementData; //存放元素的数组 private int size;//实际的集合元素个数
Vector
package com.zy.TestCollection;
import java.util.Enumeration;
import java.util.Vector;
public class TestVector {
public static void main(String[] args) {
//创建对象
Vector vector = new Vector();
//添加数据,其他方法和ArrayList相同
vector.add("nihao");
vector.add("Hello");
vector.add("world");
System.out.println(vector);
//遍历,用枚举
Enumeration elements = vector.elements();
while (elements.hasMoreElements())
{
String o = (String) elements.nextElement();
System.out.println(o);
}
//其他方法
System.out.println(vector.firstElement());//返回第一个元素
System.out.println(vector.lastElement());//返回最后一个元素
System.out.println(vector.elementAt(0));//返回对应下标的元素
}
}
LinkedList
- 存储结构:双向链表
//方法和ArrayList的方法都相同
ArrayList和LinkedList区别
- ArrayList:必须开辟连续的空间,查询快,增删慢。
- LinkedList:无需开辟连续空间,查询慢,增删快。
泛型
概念
- Java泛型是JDK1.5引入的一个新特性,其本质是参数类型,把类型作为参数传递。
- 常见形式有泛型类、泛型接口、泛型方法。
- 语法:
- <T,…> T称为类型占位符,表示一种引用类型。
- 好处:
- 提高代码的重用性。
- 防止类型转换异常,提高代码的安全性。
泛型类
package com.zy.TestGeneric;
/**
* 泛型类
* 类名<T>
* T是类型占位符,表示一种引用类型,如果编写多个用逗号隔开
* @author zy
*/
public class TestGenericClass<T> {
//使用泛型,创建变量
T t;
//泛型作为方法参数
public void show(T t){
System.out.println(t);
}
//泛型作为返回值
public T get(){
return t;
}
}
package com.zy.TestGeneric;
public class TestGeneric {
public static void main(String[] args) {
//创建泛型类对象
TestGenericClass<String> tgc = new TestGenericClass<String>();
tgc.t = "zhangsan";
tgc.show("李四");//输出:李四
System.out.println(tgc.get());//输出:zhangsan
TestGenericClass<Integer> tgc2 = new TestGenericClass<Integer>();
tgc2.t = 100;
tgc2.show(200);//输出:200
System.out.println(tgc2.get());//输出:100
//注意:1.泛型只能使用引用类型。2.不同的泛型对象不能复制,例如tgc = tgc2;这样代码会报错
}
}
泛型接口
//泛型接口
package com.zy.TestGeneric;
/**
* 泛型类
* 接口名<T>
* 不能使用泛型静态常量
* @author zy
*/
public interface Testinterface<T> {
T server(T t);
}
//接口实现类1
package com.zy.TestGeneric;
//一开始实现的时候就确定泛型的引用类型
public class MyInterfaceImpl implements Testinterface<String>{
@Override
public String server(String s) {
return s;
}
}
//接口实现类2
package com.zy.TestGeneric;
//这种写法,在创建该实现对象类时才确定引用类型
public class MyInterface2<T> implements Testinterface<T>{
@Override
public T server(T t) {
return t;
}
}
//测试类
package com.zy.TestGeneric;
public class Test {
public static void main(String[] args) {
MyInterfaceImpl myInterface =new MyInterfaceImpl();
System.out.println(myInterface.server("zhangsan"));//输出:zhangsan
MyInterface2 myInterface2 = new MyInterface2<Integer>();
System.out.println(myInterface2.server(100));//输出:100
}
}
泛型方法
package com.zy.TestGeneric;
/**
* 泛型方法
* 语法:<T>在返回值前面
* @author zy
*/
public class TestGenericMethod {
//泛型方法
//T只在该方法中使用
public <T> void showt(T t){
System.out.println(t);
}
public <S> S shows(S s){
System.out.println(s);
return s;
}
//泛型方法的调用时,不用自己输入引用类型,由你输入的数据本身决定
}
泛型集合
- 概念:参数化类型、类型安全的集合,强制集合元素的类型必须一致。
- 特点:
- 编译时即可检查,而非运行时抛出异常
- 访问时,不必类型转换(拆箱)。
- 不同泛型之间引用不能相互赋值,泛型不存在多态。
Set集合
- 特点:无序、无下标、元素不可重复。
- 方法:全部继承自Collection中的方法。
- 实现类:HashSet类,TreeSet类。
//方法实现类
//方法是和Collection相同的。只是存入时的顺序,并不是他真正存进去的数据顺序。
//遍历只有增强for和迭代器iterator。因为没有下标
实现类
HashSet类
- HashSet**【重点】**:
- 基于HashCode计算元素存放位置。
- 当存入元素的哈希表相同时,会调用equals进行确认,如结果为true,则拒绝后者存入。
package com.zy.TestHash;
import java.util.HashSet;
/**
* HashSet集合的使用
* 存储结构:哈希表(数组+链表+红黑树)
* 1、根据hashcode计算保存的位置,如果此位置为空,则直接保存,如果不为空执行第二步
* 2、再执行equals方法,如果equals方法为true,则认为重复,否则形成链表。
*/
public class TestHashSet {
public static void main(String[] args) {
//创建集合
//类
HashSet<Person> hashSet = new HashSet<Person>();
Person p1 =new Person("zhangsan",22);
Person p2 =new Person("wangwu",23);
Person p3 = new Person("lisi",24);
hashSet.add(p1);
hashSet.add(p2);
hashSet.add(p3);
System.out.println(hashSet.size());//输出:3
//这样的添加是会加进去的,因为新创建的对象,哈希值是不同的
// hashSet.add(new Person("lisi",24));
// System.out.println(hashSet.size());//输出:4
//解决上面的问题,我们需要重写,hashcode方法和equals方法
//重写后
hashSet.add(new Person("lisi",24));
System.out.println(hashSet.size());//输出:3
hashSet.remove(new Person("lisi",24));//没有重写前是删除不了的,重写两个方法后可以删除
System.out.println(hashSet.size());//输出:2
//其他方法和前面的相同
//遍历用增强for和迭代器
}
}
//重写的hashCode方法和equals方法
@Override
public boolean equals(Object o) {
if(this==o)
{
return true;
}
if(o==null)
{
return false;
}
if(o instanceof Person)
{
Person p = (Person) o;
if(this.name.equals(p.name) && this.age==p.age)
{
return true;
}
}
return false;
}
@Override
public int hashCode() {
int a = this.name.hashCode();
int b = this.age;
return a+b;
}
补充
- 31是一个质数(素数),减少散列冲突
- 31提高执行效率 31*i = (i<<5)-i;
TreeSet
- 基于排列顺序实现元素不重复。
- 实现了SortedSet接口,对集合元素自动排序。
- 元素对象的类型必须实现Comparable接口,指定排序规则。
- 通过CompareTO方法确定是否为重复元素。
package com.zy.TestHash;
import java.util.Iterator;
import java.util.TreeSet;
/**
* TreeSet存储结构:红黑树
* 要求:元素必须要实现Comparable接口 , compareTo()方法返回值为0,认为是重复元素
*/
public class TestTreeSet {
public static void main(String[] args) {
TreeSet<String> tss = new TreeSet<String>();
tss.add("xyz");
tss.add("abc");
tss.add("hmk");
//内部有排序,安装字母顺序排序,当使用的引用类没有排序方法,不能加入,会报错
System.out.println("长度:"+tss.size());
System.out.println("内容:"+tss.toString());
System.out.println("------------Person----------");
TreeSet<Person> peoples = new TreeSet<Person>();
Person p1 =new Person("zhangsan",22);
Person p2 =new Person("wangwu",23);
Person p3 = new Person("lisi",24);
Person p4 = new Person("lisi",22);
peoples.add(p1);
peoples.add(p2);
peoples.add(p3);
peoples.add(p4);
System.out.println("长度:"+peoples.size());
System.out.println("内容:"+peoples.toString());
//没有实现接口时报错:class com.zy.TestHash.Person cannot be cast to class java.lang.Comparable
//实现接口,重写方法后输出:长度:4
//内容:[Person{name='lisi', age=22}, Person{name='lisi', age=24}, Person{name='wangwu', age=23}, Person{name='zhangsan', age=22}]
//同时,删除和查找,可以直接new对象进行
System.out.println("----------删除,查找-----------");
peoples.remove(new Person("lisi",24));
System.out.println("长度:"+peoples.size());
System.out.println("内容:"+peoples.toString());
System.out.println(peoples.contains(new Person("lisi", 22)));
//输出:长度:3
//内容:[Person{name='lisi', age=22}, Person{name='wangwu', age=23}, Person{name='zhangsan', age=22}]
//true
// 遍历有增强for和迭代器
System.out.println("----------增强for----------");
for (Person people : peoples) {
System.out.println(people);
}
System.out.println("----------迭代器----------");
Iterator<Person> iterator = peoples.iterator();
while (iterator.hasNext())
{
System.out.println(iterator.next());
}
}
}
//接口实现,重写的方法
//implements Comparable<Person>
@Override
public int compareTo(Person o) {
int n1 = this.name.compareTo(o.name);
int n2 = this.age-o.age;
return n1==0?n2:n1;
}
Comparator
package com.zy.TestHash;
import java.util.Comparator;
import java.util.TreeSet;
/**
* TreeSet集合使用
* Comparator:实现定制比较(比较器)
* Comparable:可比较
*/
public class TestTreeSet2 {
public static void main(String[] args) {
//在创建TreeSet时就用匿名内部类的方法实现
//实现了这个接口就不用再实现Comparable接口
TreeSet<Person> peoples = 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;
}
});
Person p1 =new Person("zhangsan",22);
Person p2 =new Person("wangwu",23);
Person p3 = new Person("lisi",24);
Person p4 = new Person("anda",24);
peoples.add(p1);
peoples.add(p2);
peoples.add(p3);
peoples.add(p4);
System.out.println("长度:"+peoples.size());
System.out.println("内容:"+peoples.toString());
//输出:长度:4
//内容:[Person{name='zhangsan', age=22}, Person{name='wangwu', age=23}, Person{name='anda', age=24}, Person{name='lisi', age=24}]
}
}
Map集合
Map接口
-
特点:存储一对数据(Key-Value),无序、无下标,键不可重复,值可重复。
-
方法:
- V put(K key,V value) //将对象存入到集合中,关联键值。key重复则覆盖原值。
- Object get(Object key) // 根据键获取对应的值。
- keySet //返回所有key
- Collection values() //返回包含所有值的Collection集合
- Set<Map.Entry<K,V>> //键值匹配的Set集合。
-
Map接口使用
package com.zy.TestHash;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
//Map接口的使用
public class TestMap {
public static void main(String[] args) {
Map<String,String> map = new HashMap<String,String>();
map.put("a","an");
map.put("b","bn");
map.put("c","cn");
map.put("d","dn");
map.put("c","cc");//键相同,值不同,会覆盖前面加入的数据
System.out.println(map.toString());
//删除传入键删除数据
//遍历
//1.keySet
System.out.println("---------keySet----------");
// Set<String> strings = map.keySet();合并
for (String string : map.keySet()) {
System.out.println(map.get(string));
}
//2.entrySet
System.out.println("---------entrySet----------");
Set<Map.Entry<String, String>> entries = map.entrySet();
for (Map.Entry<String, String> entry : entries) {
System.out.println("key:"+entry.getKey()+" value:"+entry.getValue());
}
//两者比较,entrySet的效率要高于keySet
//判断
System.out.println("-------------------------");
System.out.println(map.containsKey("a"));
System.out.println(map.containsValue("cc"));
}
}
实现类
HashMap(重点)
- JDK1.2版本,线程不安全,运行效率快;允许用null,作为key或者value。
package com.zy.TestHash;
import java.util.HashMap;
import java.util.Map;
/**
* HashMap集合的使用
* 存储结构:数组+链表+红黑树
*/
public class TestHashMap {
public static void main(String[] args) {
//创建HashMap对象
HashMap<Student,String> hashMap = new HashMap<Student,String>();
//创建学生键
Student s1 = new Student("张三",001);
Student s2 = new Student("李四",002);
Student s3 = new Student("王五",003);
hashMap.put(s1,"北京");
hashMap.put(s2,"上海");
hashMap.put(s3,"南京");
hashMap.put(s1,"海南");//替换了前面的内容
hashMap.put(new Student("王五",003),"南京");
System.out.println(hashMap.size());
System.out.println(hashMap.toString());
//没有重写方法时,输出的内容,相同但new的对象不会被识别出来
// 4
//{Student{name='王五', id=3}=南京, Student{name='李四', id=2}=上海, Student{name='张三', id=1}=海南, Student{name='王五', id=3}=南京}
//重写hashCode和equals方法后,没有添加进去:
//3
//{Student{name='王五', id=3}=南京, Student{name='张三', id=1}=海南, Student{name='李四', id=2}=上海}
//删除
// System.out.println(hashMap.remove(s1));//输出:true
// System.out.println(hashMap.remove(s1, "北京"));//输出false,因为s1后面被覆盖为海南
// System.out.println(hashMap.size());
// System.out.println(hashMap.toString());
//遍历
//keySet
System.out.println("----------keySet遍历-------------");
for(Student student : hashMap.keySet())
{
System.out.println("key:"+student.toString()+" value:"+hashMap.get(student));
}
//entrySet
System.out.println("----------entrySet遍历-------------");
for(Map.Entry<Student,String> entry:hashMap.entrySet())
{
System.out.println("key:"+entry.getKey()+" value:"+entry.getValue());
}
//判断,重写hashCode和equals方法后,可以直接用new对象判断
System.out.println(hashMap.containsKey(new Student("张三",001)));//输出:true
System.out.println(hashMap.containsValue("海南"));//输出:true
}
}
源码解析
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;//hashMap初始容量大小,2的四次方,16
static final int MAXIMUM_CAPACITY = 1 << 30;//hashMap的数组最大容量,2的30次方
static final float DEFAULT_LOAD_FACTOR = 0.75f;//默认加载因子
static final int TREEIFY_THRESHOLD = 8;//jdk1.8 当链表长度大于8时,调整为红黑树
static final int UNTREEIFY_THRESHOLD = 6;//jdk1.8 当链表长度小于6时,调整成链表
static final int MIN_TREEIFY_CAPACITY = 64;//jdk1.8 当链表长度大于8时,并且集合元素个数大于等于64时,调整成红黑树
transient Node<K,V>[] table;//哈希表中的数组
transient int size; //元素个数
开始创建对象时,为空,只有调用put方法,就是第一次添加元素时,创建一个长度为16的数组,当大于16×0.75=12时,变为32,就是每次存入数据大于长度的0.75倍时,长度翻倍。
Hashtable
- JDK1.0版本,线程安全,运行效率慢;不允许null作为key或是value.(现在基本不再使用)
Properties
- Hashtable的子类,要求key和value都是String。通常用于配置文件的读取。(使用较多)
TreeMap
- 实现了SortedMap接口(这个接口是Map的子接口),可以对key自动排序。
//方法实现和其他Map类相同,不同是存入数据时有序,使用引用类时,该类要实现Comparable接口,用于排序和TreeSet类似,或者是用匿名类,来实现Comparator接口
//TreeSet里面使用了TreeMap
//HashSet里面也使用了HashMap
Colletions工具类
- 集合工具类,定义了除了存取以外的集合常用方法。
- 方法:
- public static void reverse(List<?> list) //反转集合中元素的顺序
- public static void shuffle(List<?> list) //随机重置集合元素的顺序
- public static void sort(List list) //升序排序(元素类型必须实现Comparable接口)
package com.zy.TestHash;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Collections工具类使用
*/
public class TestCollections {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
list.add(22);
list.add(2);
list.add(42);
list.add(62);
list.add(25);
list.add(20);
System.out.println("排序前:"+list.toString());//输出:排序前:[22, 2, 42, 62, 25, 20]
//排序,按升序排序
Collections.sort(list);
System.out.println("排序后:"+list.toString());//输出:排序后:[2, 20, 22, 25, 42, 62]
//二分查找,必须是有序的才能使用,返回下标
System.out.println("查找22的结果:"+Collections.binarySearch(list, 22));//输出:查找22的结果:2
//copy复制,要求集合的大小必须一样,不然报错
List<Integer> list1 = new ArrayList<>();
//使用for循环,给定长度,全赋值为0
for(int k =0;k<list.size();k++)
{
list1.add(0);
}
Collections.copy(list1,list);
System.out.println("复制到list1的:"+list1.toString());//输出:复制到list1的:[2, 20, 22, 25, 42, 62]
//反转,reverse
Collections.reverse(list);
System.out.println("反转后:"+list.toString());//输出:反转后:[62, 42, 25, 22, 20, 2]
//打乱顺序shuffle
Collections.shuffle(list);//每次都不同
System.out.println("打乱后:"+list.toString());//输出:打乱后:[2, 62, 25, 42, 22, 20]
//补充,List转为数组
System.out.println("----------List转为数组--------");
Integer[] array = list.toArray(new Integer[0]);//括号中的值,小于List长度,数组就是List的长度,超过后,数组长度就是你定义的数字长度
System.out.println(array.length);//输出:6
System.out.println(Arrays.toString(array));//输出:[22, 25, 62, 2, 20, 42]
//数组转为集合
String[] strings = {"张三","李四","王五","老六"};
List<String> list2 = Arrays.asList(strings);//数组转换过来的集合不能改变,不能添加删除这些,因为数组长度是固定的
System.out.println(list2.toString());//输出:[张三, 李四, 王五, 老六]
//不要用基本类型的集合转换为集合,用引用类型
}
}