Java——面向对象编程高级级部分——第四部分——集合
集合
基本介绍
1.可以动态保存任意多个对象,使用比较方便
2.提供了一系列方便操作对象的方法
框架体系图
集合主要分为两大类:单列集合和双列集合
Collection
基本介绍
public interface Collection<E> extends Iterable<E>
1.collection是心啊子类可以存放多个元素,每个元素可以是Object
2.有些Collection的实现类,可以存放重复的元素,有些不可以
3.有些Collection的实现类,有些事有序的,有些不是有序的
4.Collection接口没有直接的实现子类,是通过它的子类接口Set和List来实现的
常用方法
遍历元素的方式1.使用迭代器Iterator
基本介绍
1.Iterator对象称为迭代器,主要用于遍历Collection集合中的元素
2.所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象,即返回一个迭代器
3.Iterator的结构
4.Iterator仅限于集合遍历,Iterator本身并不存放对象
迭代器执行原理
public class Co {
public static void main(String[] args) {
ArrayList<Object> list = new ArrayList<>();
list.add("jack");
list.add(35);
Iterator iterator=list.iterator();//得到一个集合的迭代器
while(iterator.hasNext()){//判断是否有下一个元素
iterator.next();//指针下移,将下移后集合位置上的元素返回
System.out.println(iterator.next());
}
}
}
常用方法
注意:
1.在调用iterator.next()方法之前必须调用iterator.hasNext()进行检测,若不调用,且下一条记录无效,直接调用it.next()会抛出异常
2.当退出循环后,iterator迭代器指向最后的元素
3.如果希望再次遍历,需要重置迭代器
遍历元素的方式1.for循环增强
基本介绍
增强for循环可以代替迭代器
特点:增强for循环就是简化版的iterator,本质相同,只能用于遍历集合或数组
基本语法:
for(元素类型 元素名:集合名或数组名){
访问元素
}
List
基本介绍
1.List集合类中元素有序(即添加顺序和取出顺序一致,且可以重复
2.List集合中的每个元素都有其对应的顺序索引,即支持索引(索引从0开始)
3.List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素
4.常用类:
常用方法
三种遍历方式
iterator
while (iterator.hasNext()) {
Object next = iterator.next();
}
增强for循环
for (Object o :col) {
}
普通for循环
for (int i = 0; i < col.size(); i++) {
Object o=col.get(i);
}
ArrayList
基本介绍
1.允许存放所有的元素,包括null,并且允许加入多个
2.ArrayList是由数组来实现数据存储的
3.ArrayList基本等同于Vector,处理ArrayList线程不安全(执行效率高),多线程情况下,不建议使用ArrayList
底层分析
1.ArrayList中维护了一个Object类型的数组elementData
2.当创建ArrayList对象时,如果使用的是无参构造,则初始elementData容量为0,第一次添加,扩容elementData为10,如果需要再次扩容,则扩容elementData的1.5倍
3.如果使用的是指定大小的构造器,则初始elementData容量为只当大小,如果需要扩容,则直接扩容elementData的1.5倍
Vector
基本介绍
1.类的定义说明:
2.Vector底层也是一个对象数组 protected Object[] elementData
3.Vector是线程同步的,即线程安全,Vector类的操作方法带有synchronized
public synchronized E get(int index)
{
if(index>=elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index);
}
4.在开发中,需要线程同步安全时,考虑使用Vector
与ArrayList 的比较
LinkedList
基本介绍
1.LinkedList实现了双向链表和双端队列特点
2.可以添加任意元素(元素可以重复),包括null
3.线程不安全,没有实现线程同步
底层分析
1.LinkedList底层维护了一个双向链表
2.LinkedList中维护了两个数学first和last分别指向首结点和尾结点
3.每个结点(Node对象),里面又维护了prev,next,item三个属性,其中通过prev指向前一个,通过next指向后一个结点,最终实现双向链表
4.LinkedList的元素的增加和删除不是通过数组来完成,相对来是效率较高
与ArrayList的比较
选择判断
1.如果我们改查的操作多,选择ArrayList
2.如果我们增删的操作多,选择LinkedList
3.大部分情况下选择LinkedList
4.根据业务灵活选择
Set
基本介绍
1.无序(添加和去除的顺序不一致),没有索引
2.不允许重复元素,所以最多包含一个null
3.实现类有:
常用方法和与遍历方式
常用方法与Collection相同
遍历方式与Collection相同,可以使用迭代器和增强for循环,但是注意不能使用索引当时来获取
HashSet
基本介绍
1.HashSet实现了Set接口
2.HashSet实际上是HashMap,HashMap底层是(数组+链表+红黑树)
public HashSet() {
map = new HashMap<>();
}
3.可以存放null值,但是只能存放一个null值
4.Hash不保证元素是有序的,取决于hash后,再确定索引的结果(即不保证存放元素的顺序和取出的顺序一致)
5.不能有重复元素/对象
6.在执行add方法后返回一个boolean值,添加成功返回true,否则返回false
底层分析
LinkedHashSet
基本介绍
1.是HashSet的子类
2.LinkedHashSet底层是一个LinkedHashMap,地城维护了一个数组+双向链表
3.LinkedHashSet根据元素的hashCode值来决定存储位置,同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存的
4.不允许添加重复元素
TreeSet
可以实现排序
TreeSet提供一个构造器,可以传入比较器
使用匿名类,实现比较器的自定义
public class Hashset {
public static void main(String[] args) {
TreeSet treeSet=new TreeSet<>(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return ((String)o1).compareTo((String) o2);
}
});
treeSet.add("jack");
treeSet.add("tom");
treeSet.add("sp");
treeSet.add("a");
System.out.println(treeSet);
}
}
Map
基本介绍
1.Map与Collection并列存在,用于保存具有映射关系的数据:Key-Value
2.Map中的key和value可以是任何引用类型的数据,会封装到HashMap$Node对象中
3.Map中的key不允许重复,当有相同的key时,等价于替换
4.Map中的value可以重复
5.Map中的key可以为null,value也可以为null,注意key为value只能有一个,value为空可以有多个
6.常用String类作为Map的key
7.key和value之间存在单向一对一关系,即通过指定的key总能找到对应的value值
8.Map存放一对数据的key-value是放在一个Node中的,因为Node实现了Entry接口,也有书上说一对k-v就是一个Entry
9.k-v为了方便程序员比哪里,还会 创建EntrySet集合,该集合存放的元素的类型Entry,如图一个Entry对象就有k,v EntrySet<Entry<K,V> 即:transient Set<Map.Entry<K,V>> entrySet
10.在entrySet中定义的类型是Map.Entry,但是实际上存放的还是HasnMapNode,因为static class Node<K,V> implements Map.Entry<K,V>当爸hashMapNode对象存放到entrySet就方便了我们的遍历
Set set=map.entrySet();
for(Object o:set){
Map.Entry entry=(Map.Entry) o;
System.out.println(entry.getKey()+entry.getValue());
}
常用方法
Map体系继承图:
常用方法:
遍历方式
1.containKey:查找键是否存在
2.keySet:获取所有的键
3.entrySet:获取所有的关系
4.values:获取所有的值
1.先取出所有的key,通过key取出对应的value
Set keyset=map.keySet();
//(1)增强for循环
for(Object key:keyset){
System.out.println(key+"-"+map.get(key));
}
//迭代器
Iterator iterator=keyset.iterator();
while (iterator.hasNext()) {
Object key = iterator.next();
System.out.println(key+"-"+map.get(key));
}
2.只取出所有的value
Collection values=map.values();
//(1)增强for循环
for (Object value :values) {
System.out.println(value);
}
//2.迭代器
Iterator iterator1=values.iterator();
while (iterator1.hasNext()) {
Object value = iterator1.next();
System.out.println(value);
}
3.使用entryset
Set entrySet=map.entrySet();//EntrySet<Map.Entry<k,V>>
//(1)增强for循环
for (Object entry :entrySet) {
//将entry转成Map.entry
Map.Entry m=(Map.Entry) entry;
System.out.println(m.getKey()+"-"+m.getValue());
}
//2.迭代器
Iterator iterator2 = entrySet.iterator();
while (iterator2.hasNext()) {
Object entry = iterator2.next();//HasnMap$Bode-实现->Map.Entry(getKey,getValue)
Map.Entry m=(Map.Entry) entry;
System.out.println(( m.getKey()+"-"+m.getValue());
}
实例
public class Hashset {
public static void main(String[] args) {
Map hashMap = new HashMap();
hashMap.put(1,new Emp("jack",3000,1));
hashMap.put(2,new Emp("tom",1000,2));
hashMap.put(3,new Emp("milan",5000,3));
Set keySet=hashMap.keySet();
for (Object key :keySet) {
Emp emp =(Emp) hashMap.get(key);
if(emp.getSal()>1000)
System.out.println(emp);
}
Set entrySet = hashMap.entrySet();
Iterator iterator=entrySet.iterator();
while (iterator.hasNext()) {
Map.Entry entry =(Map.Entry) iterator.next();
Emp emp=(Emp) entry.getValue();
if(emp.getSal()>1000)
System.out.println(emp);
}
}
}
class Emp{
String name;
double sal;
int id;
public Emp(String name, double sal, int id) {
this.name = name;
this.sal = sal;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSal() {
return sal;
}
public void setSal(double sal) {
this.sal = sal;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "Emp{"+name+sal+id+"}";
}
}
HashMap
1.Map接口的常用实现类:HashMap,Hash他变了和Properties
2.HashMap是以key-value对的方式来存储数据
3.key不能重复,但是value可以重复,允许null键和null值
4.如果添加相同的key则会覆盖原来的key-val,等同于修改(key不会替换,value会替换)
5.与HashSet也有不保证映射的顺序,因为底层是以hash表的方式来存储的(hashMap 底层 数组+链表+红黑树)
6.HashMap马原实现同步,因此是线程不安全的,方法马原做到同步互斥的操作
TreeMap
实现排序
构造器中存在一个比较器
TreeMap treeMap=new TreeMap(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return ((String)o1).compareTo((String) o2);
}
});
treeMap.put("jack","hhh");
treeMap.put("tom","hh");
treeMap.put("sp","h");
treeMap.put("a","mm");
System.out.println(treeMap);
集合选型机制
1.判断存储类型
(一组对象【单列】或一组键值对【双列】)
2.一组对象:
Collection接口
允许重复:List
增删多:LinkedList(底层维护了一个双向链表)
改查多:ArrayList(底层维护了一个Object类型的可变数组)
不允许重复:Set
无序:HashSet(底层是HashMap,维护了一个哈希表,即数组+链表+红黑树)
排序:TreeSet
插入和取出顺序一致:LinkedHashSet(底层维护了数组+双向链表)
3.一组键值对:Map
键无序:HashMap(底层是哈希表,维护了一个哈希表,即数组+链表+红黑树)
键排序:TreeMap
键插入和取出顺序一致:LInkedHashMap
读取文件 Properties
Collections工具类
基本介绍
1.是一个操作Set,List,Map等集合的工具类
2.Collections中提供了一系列静态的方法对集合元素进行排序,查询,修改等操作
排序操作:(均为static方法)
泛型
传统方式
1.不能对加入到集合中的数据类型进行约束,不安全
2.比哪里的时候,需要进行类型转换,如果集合中的数据量较大,对效率有影响
泛型的好处
1.编译时,检查添加元素的类型,提高了安全性
2.减少了类型转换的次数,提高效率
使用泛型放入取出时不需要类型转换
3.不再提示编译警告
基本介绍
1.泛型又称参数化类型,解决数据类型的安全性问题
2.在类声明或者实例化时,只要指定号需要的具体类型即可
3.java泛型可以保证如果程序在编译时马原发出警告,运行时就不会产生类型转换的异常,同时代码更加简洁和健壮
4.泛型的作用是:可以在类声明的过程中通过标识表示类中某个属性的类型,或者是某个方法的返回值类型,或者是参数类型
语法
Interface 接口<T>{}
class类<K,V>{}
其中的T,K,V不代表值,而是代表类型
任意字母都可以
实例化
要在类名后面指定类型参数的值(类型)
List<String > stringList=new ArrayList<String>();
Iterator<Customer> iterator=customers.iterator();
实例
public class Hashset {
public static void main(String[] args) {
HashSet<Student> students = new HashSet<>();
students.add(new Student("jack",18));
students.add(new Student("tom",28));
students.add(new Student("mary",38));\
for (Student student :students) {
System.out.println(student);
}
HashMap<String, Student> hm = new HashMap<>();
hm.put("tom",new Student("tom",28));
hm.put("jack",new Student("jack",18));
Set<Map.Entry<String, Student>> entrySet = hm.entrySet();
Iterator<Map.Entry<String, Student>> iterator = entrySet.iterator();
while (iterator.hasNext()) {
Map.Entry<String, Student> next = iterator.next();
System.out.println(next.getKey()+"-"+next.getValue());
}
}
}
class Student{
String name;
int age;
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 +
'}';
}
}
使用细节
1.Interface 接口{} 或class类<K,V>{}中的T,K,V等只能是引用类型,而不能是基本数据类型
2.在指定泛型的具体类型后, 可以传入该类型或者其子类类型
3.List list=new ArrayList();不指定泛型类型则默认泛型是Object
练习
import java.util.*;
public class Hashset {
public static void main(String[] args) {
ArrayList<Emplpyee> emplpyees = new ArrayList<>();
emplpyees.add(new Emplpyee("tom",2000,new MyDate(2000,11,20)));
emplpyees.add(new Emplpyee("jack",1000,new MyDate(2001,11,2)));
emplpyees.add(new Emplpyee("hsp",5000,new MyDate(2005,10,2)));
System.out.println(emplpyees);
emplpyees.sort(new Comparator<Emplpyee>() {
@Override
public int compare(Emplpyee o1, Emplpyee o2) {
if(!(o1 instanceof Emplpyee&& o2 instanceof Emplpyee))
return 0;
int i = o1.getName().compareTo(o2.getName());
if(i!=0)return i;
o1.getBirthday().compareTo(o2.getBirthday());
}
});
}
}
class Emplpyee{
String name;
double sal;
MyDate birthday;
public Emplpyee(String name, double sal, MyDate birthday) {
this.name = name;
this.sal = sal;
this.birthday = birthday;
}
@Override
public String toString() {
return "\nEmplpyee{" +
"name='" + name + '\'' +
", sal=" + sal +
", birthday=" + birthday +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSal() {
return sal;
}
public void setSal(double sal) {
this.sal = sal;
}
public MyDate getBirthday() {
return birthday;
}
public void setBirthday(MyDate birthday) {
this.birthday = birthday;
}
}
class MyDate implements Comparable<MyDate>{
int year;
int month;
int day;
public MyDate(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
@Override
public String toString() {
return "MyDate{" +
"year=" + year +
", month=" + month +
", day=" + day +
'}';
}
@Override
public int compareTo(MyDate o) {
int yearMins = year -o.getYear();
if(yearMins!=0)return yearMins;
int monthMins=month-o.getMonth();
if(monthMins!=0)return monthMins;
return day-o.getDay();
}
}
自定义泛型
自定义泛型类
基本语法
class类<K,V....>{成员}
注意细节
1.普通成员可以使用泛型(属性,方法)
2.使用泛型的数组,不能初始化
3.静态方法中不能使用类的泛型
4.泛型类的类型是在创建对象时去欸的那个的(因为创建对象时,需要指定确定类型)
5.如果创建对象时没有指定类型,默认为Object
自定义泛型接口
基本语法
interface 接口名<K,V....>{}
注意细节
1.接口中,静态成员也不能使用泛型(接口中的属性是静态的,方法是public的,抽象方法可以省略关键字)
2.泛型接口的类型,在继承接口或者实现接口时确定
3.没有只当类型,默认为Odject
自定义泛型方法
基本语法
修饰符 <K,V....>返回类型 方法名(参数列表){}
注意细节
1.泛型方法可以定义子啊普通类中,也可以定义在泛型类中
2.当泛型方法被调用时,传入参数,编译器就会对类型进行确定
3.public void eat(E e){} 修饰符后面没有<T,R> eat方法不是泛型方法,而是使用了泛型作为参数
泛型的继承和通配符
1.泛型不具备继承性
List<Object> list=new ArrayList<String>();//flase 不允许继承
2.<?>支持任意泛型类型
3.<? extends A>支持A类以及A类的子类,规定了泛型的上限
4.<? super A>支持A类以及A类的父类,不限于直接父类,规定了泛型的下限