根据b站韩顺平教育韩顺平教育java教学视频学习的学习笔记。个人学习理解,如有疏漏,敬请改正。
目录
集合的优点
- 可以动态保存任意多个对象,使用比较方便
- 提供了一系列方便的操作对象的方法:add、remove、set、get等
- 使用集合添加,删除新元素的代码更加简洁
集合框架体系图
只对于常用的实现类
单列集合(集合里存放的是单个元素)
双列集合(集合里存放的是键值对)
Collection
1、Collection实现子类可以存放多个元素,每个元素可以是Object
2、有些Collection的实现类,可以存放重复的元素,有些不可以
3、有些Collection的实现类,有些是有序的(List),有些不是有序(Set)
4、Collection接口没有直接的实现子类,是通过它的子接口Set和List来实现的
//单列集合Collection接口方法演示,以ArrayList 演示 List list = new ArrayList(); //add()添加元素,可以是任意对象 list.add(123);//底层是add(new Integer(123)); list.add("hello"); list.add(12.23); list.add(true); System.out.println("list" + list); //remove()删除元素 list.remove(0);//以下标的方式删除元素 list.remove(12.23);//直接指定删除元素 System.out.println("list" + list); //addAll() 添加多个元素,参数是Collection对象 ArrayList list1 = new ArrayList(); list1.add("红楼梦"); list1.add("水浒"); list.addAll(list1); System.out.println("list1"+ list); //removeAll() 删除多个元素 list.removeAll(list1); System.out.println("list" + list); //contains() 查找元素是否查找, System.out.println(list.contains("hello"));//T //containsAll()查找多个元素是否存在 System.out.println(list.containsAll(list1));//F //isEmpty()判断是否为空,返回布尔类型 System.out.println(list.isEmpty());//F //size 返回集合内的元素个数 System.out.println(list.size());//2 //clear 清空 list.clear(); System.out.println("list" + list);
Collection接口遍历元素的方式
1、使用Iterator(迭代器)
- Iterator对象称为迭代器,主要用于遍历Collection集合中的元素
- 所有实现了Collection接口的集合类都有一个iterator()方法,用于返回一个实现了Iterator接口的类对象,即可以返回一个迭代器
- Iterator仅用于遍历集合,Iterator本身并不存放对象
迭代器方法
HasNext():判断是否还有下一个元素,用在while循环的判断
next():按个返回集合里的元素
在调用iterator.next()方法之前必须要调用iterator.hasNext()进行判断是否还有下一个元素,否则会报错
//使用迭代器遍历Collection集合 Collection list = new ArrayList(); list.add(new Book("红楼梦","曹雪芹",198)); list.add(new Book("三国演义","罗贯中",188)); list.add(new Book("水浒传","施耐庵",178)); System.out.println(list); //得到list对应的迭代器 Iterator iterator = list.iterator(); //可以使用快捷键itit(Ctrl+j),快速搭建while循环遍历集合 while (iterator.hasNext()) { Object next = iterator.next(); System.out.println(next); } //当遍历完成以后,迭代器指向最后的元素,这时再使用iterator.next();就会报错,所以要重置迭代器 iterator = list.iterator();
2、使用增强for循环
- 增强for循环,可以代替iterator迭代器,特点:增强for就是简化版的iterator,本质一样。只能用于遍历集合或数组
基本语法
for(元素类型 元素名:集合名或数组名){
访问元素}
for (Object book : list){ System.out.println("book = " + book); } int[] it = {1,2,3,4}; for(int in : it){ System.out.println("in=" + in); }
List接口和常用方法
List接口是Collection接口的子接口
- List集合类中元素有序(添加顺序和取出顺序一致)、且可重复
- List集合类中的每个元素都有其对应的顺序索引,即支持索引
- List接口的实现类有很多,可咱API中查看,常用的有ArrayList、LinkedList和Vector
List接口方法
//List方法实现 //1、List集合类中元素有序、可重复 List list = new ArrayList(); list.add("zdz"); list.add("Maddox"); list.add("Jack"); list.add("Tom"); list.add("Maddox"); System.out.println("list :"+ list); //2、add(int index,Object els) 可以在index位置插入一个元素 list.add(1,"张无忌"); System.out.println("list:" + list); //3、addAll(int index,Collection els) 可以在index位置后插入一个Collection集合 List list1 = new ArrayList(); list1.add("zxs"); list1.add("Ajax"); list.addAll(1,list1); System.out.println("list :"+ list); //4、get(index) 获取index处的元素 System.out.println("list3= "+ list.get(2)); //5、indexOf(Object obj) 返回obj在集合中首次出现的位置 System.out.println("Ajax's index = "+ list.indexOf("Ajax")); //lastIndexOf(Object obj) 返回obj在集合中最后一次出现的位置 System.out.println("Maddox's index = "+ list.lastIndexOf("Maddox")); //Object remove(int index) 删除index处的元素并返回此元素 Object list2 = list.remove(1); System.out.println("list :" + list); System.out.println("list2 = "+ list2); //Object set(int index, Collection els) els替换index处的元素 list.set(0,"dasb"); System.out.println("list :" +list); //List subList(int startIndex,int endIndex) 返回从startIndex开始到endIndex之间的子集合 //注意 startList <= subList < endList List list3 = list.subList(2,5); System.out.println("list3 :" + list3);
List的三种遍历方式(ArrayList、LinkedList、Vector)
- 使用iterator
- 使用增强for循环
- 使用普通for循环(使用get()方法获取下标)
ArrayList
- ArrayList可以存放所有元素,包括Null(空元素),Null可以为多个
- ArrayList是由数组来实现数据存储的
- ArrayList基本等同于Vector,除了ArrayList是线程不安全(但执行效率高),在多线程情况下,不建议使用ArrayList。
- ArrayList中维护了一个Object类型的数组elementData,当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第一次添加,则扩容elementData为10,如需要再次扩容,则扩容elementData为1.5倍
- 如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍
Vector
- Vector底层也是一个对象数组:protected Object[] elementData;
- Vector是线程同步的,即线程安全,Vector类的操作方法带有synchronized关键字。在开发中,需要线程同步时,考虑使用Vector
- ArrayList中维护了一个Object类型的数组elementData,当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量默认为10,容量满后,直接两倍扩容,调用无参构造器的时候其实就是调用有参构造器,参数为10,所以两者一样
public Vector(){
this(10);
}
- 如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则按照两倍扩容
LinkedList
1、LinkedList底层实现了双向链表和双端队列
LinkedList中维护了两个属性first和last分别指向首结点和尾结点
LinkedList的CRUD操作对应方法,add()、remove()、set()、get()。其中remove()默认删除第一个结点。
2、所以LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高
3、可以添加任意元素(元素可以重复),包括null
4、线程不安全,没有实现同步
LinkedList的CRUD操作
//LinkedList的CRUD操作 //增加 LinkedList list = new LinkedList(); list.add(1); list.add("hello"); list.add(999); System.out.println("list :"+ list); //删除 list.remove();//删除链表第一个元素,其调用了 list.removeFirst() System.out.println("list :"+ list); list.remove(1); System.out.println("list :"+ list); list.add(2333); System.out.println("list :"+ list); list.add(12); System.out.println("list :"+ list); list.add(2,234); System.out.println("list :"+ list); //获取元素个数 System.out.println("list的元素个数:"+ list.size()); //改 list.set(2,"你哈"); System.out.println("list :"+ list); //查 System.out.println(list.get(2)); System.out.println(list.indexOf("hello")); //遍历 System.out.println("=====迭代器遍历====="); Iterator iterator = list.iterator(); while (iterator.hasNext()) { Object next = iterator.next(); System.out.println("list :"+ next); } System.out.println("=====增强for遍历====="); for (Object e : list) { System.out.println("list :"+ e); } System.out.println("=====普通for遍历====="); for (int i = 0; i < list.size(); i++) { System.out.println("list :" + list.get(i)); }
List集合的选择
在不考虑线程安全的情况下,使用ArrayList,否则Vector
1、如果改查的操作多,ArrayList
2、如果增删的操作多,选择LinkedList
3、一般来说,在程序中,大多数的操作都是查询,因此大部分情况下选择ArrayList
4、在一个项目中,根据业务灵活选择,也可能一个模块使用的是ArrayList,另外一个模块是LinkedList
Set接口和常用方法
和List接口一样,Set接口也是Collection的子接口,因此,常用方法和Collection接口一样
Set接口的实现类的对象(Set接口对象),不能存放重复的元素,可以添加null
Set接口的遍历方式
注意:取出的顺序虽然不是添加的顺序,但是每次取出时顺序都是一样的
迭代器
增强for循环
不能使用索引的方式来获取(Set里存放的是无序元素,取出时的顺序不会和添加的顺序一致)
HashSet
实现了Set接口
1、HashSet实际上是HashMap(底层)
public HashSet(){
map = new HashMap<>();
}
- 添加一个元素时,先得到hash值(HashCode方法)-会转成索引值
- 找到存储数据表table,看这个索引位置是否已经存放有元素
- 如果没有直接加入
- 如果有,调用equals比较,如果相同就放弃添加,如果不同,则添加到最后
- 在Java8中,如果一条链表的元素个数超过TREEIFY_THRESHOLD(默认是8),并且table的大小>=MIN_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树)
3、可以存放null值,即可以有一个空值
4、HashSet不保证元素是有序的,取决于hash后,再确定索引的结果(即不保证存放元素的顺序和取出顺序一致)
5、和Set一样不能有重复值(元素/对象)
LinkedHashSet
LinkedHashSet是HashSet的子类
LinkedHashSet底层是一个LinkedHashMap,底层维护了一个数组+双向链表
LinkedHashSet根据元素的hashCode值来决定元素的存储位置,同时使用链表来维护元素的次序,这使得元素看起来是以插入顺序保存的
LinkedHashSet不允许添加重复元素
在LinkedHashSet中维护了一个hash表和双向链表,双向链表设置头结点和尾结点,每个结点有pre和next属性,这样就可以形成双向链表并保证在遍历LinkedHashSet时插入顺序和遍历顺序一致。
在添加一个元素时,先求hash值,再求索引,确定该元素在table的位置,然后将添加的元素加入到双向链表(如果已经存在,不添加)
Map接口和常用方法
Map接口实现类的特点(很实用 JDK8)
1、Map与Collection并列存在。用于保存具有映射关系的数据:Key-Value(双列元素)
2、Map中的key和value可以是任何引用类型的数据,会封装到HashMap$Node对象中
3、Map中的key不允许重复,原因和HashSet一样,当有重复的key出现的时候相当于与先出现的key对应的value替换原来key下面的value
4、Map中的value可以重复
5、Map的key可以为null但只能有一个为null,value也可以为null且可以有多个
6、常用String类作为Map的key
7、key和value之间存在单向一对一关系,即通过指定的key总能找到对应的value
8、Map存放的一对k-v是放在一个Node中的,
常用方法
map.put(“no1”,”hello”);//添加map元素方法
map.get(key);//根据key来读取元素
remove:根据key删除映射关系
size:获取元素个数
isEmpty:判断个数是否为0
clear:清除
containsKey:查找键是否存在
Map六大遍历方式
4个使用到的方法
1、containsKey:查找键是否存在
2、keySet:获取所有的键
3、entrySet:获取所有关系k-v
4、values:获取所有的值
6种遍历方式
Map map = new HashMap(); map.put("Hello","World"); map.put("罗贯中","三国演义"); map.put("施耐庵","水浒"); map.put(null,"老房子"); map.put("Maddox",null); //第一组 keySet获取键 Set set = map.keySet(); System.out.println("====方式一 增强for循环-keySet======"); for (Object key : set){ System.out.println("NO1:"+ key + "-" + map.get(key)); } System.out.println("====方式二 迭代器-keySet======"); Iterator iterator = set.iterator(); while (iterator.hasNext()) { Object key = iterator.next(); System.out.println("NO2:"+ key + "-" + map.get(key)); } //第二组 使用values获取所有的值 仅仅能够打印值 Collection values = map.values(); //这里可以使用所有的Collection遍历方法 System.out.println("====方式三 增强for循环-values======"); for (Object value : values){ System.out.println("NO3:"+value); } System.out.println("====方式四 迭代器-values======"); Iterator iterator1 = values.iterator(); while (iterator1.hasNext()) { Object value = iterator1.next(); System.out.println("NO4:"+value); } //第三组 entrySet:获取所有关系k-v Set entrySet = map.entrySet(); System.out.println("====方式五 增强for循环-entrySet======"); for(Object entry : entrySet){ Map.Entry m = (Map.Entry)entry; System.out.println("NO5:" + m.getKey() + "-" + m.getValue()); } System.out.println("====方式六 迭代器-entrySet======"); Iterator iterator2 = entrySet.iterator(); while (iterator2.hasNext()) { Object entry = iterator2.next(); Map.Entry m = (Map.Entry)entry; System.out.println("N6:" + m.getKey() + "-" + m.getValue()); }
HashMap
HashSet底层即使用了HashMap,两者之间底层逻辑基本相同
1.Map接口的常用实现类:HashMap、HashTable和Properties
2.HashMap是Map接口使用频率最高的实现类
3.HashMap是以key-val对的方式来存储数据
4.key不能重复,但是值val可以重复,允许使用null键和null值
5.如果添加相同的key则会覆盖原来的key-val,等同于修改
6.与HashSet一样,不保证映射的顺序,因为底层是以hash表的方式来存储的
7.HashMap没有实现同步,一次是线程不安全的(方法没有做同步互斥的操作,没有synchronized)
扩容机制(和HashSet相同)
1.HashMap底层维护了Node类型的数组table,默认为null
2.当创建对象时将加载因子(loadfactor)初始化为0.75
3.当添加key-val时,通过key的哈希值得到在table的索引,然后判断该索引处是否有元素,如果没有元素直接添加;如果该索引处有元素,继续判断该元素的key是否和准备加入的key相等,如果相等则直接替换val;如果不相等需要判断是树结构还是链表结构,做出相应处理。如果添加时发现容量不够则需奥扩容
4.第一次添加,需要扩容table容量为16,临界值(threshold)为12(16*0.75)
5.以后再扩容,则需要扩容table容量为原来的2倍(32),临界值为原来的2倍,即(32*0.75=24),以此类推
6.在Java8中,如果一条链表的元素个数超过TREEIFY_THRESHOLD(默认是8),并且table的大小>=MIN_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树)
HashTable
存放的元素是键值对:k-v
键和值都不能为null
使用方法基本上和HashMap一样
是线程安全的
HashTable扩容(源码)
Properties
- Properties类继承自HashTable类并且实现了Map接口,也是使用一种键值对的形式来保存数据,所以也不能有null值
- 使用特点和HashTable类似
- Properties还可以用于从xxx.properties文件中加载数据到Properties类对象并进行读取和修改
说明:xxx.properties文件通常为配置文件,这个知识点在IO流举例
集合选型规则
在开发中,选择什么集合实现类,主要取决于业务操作特点,然后根据集合实现类特性进行选择
1、先判断存储的类型(一组对象[单列]或一组键值对[双列])
2、一组对象:Collection接口
允许重复:List
增删多:LinkedList[底层维护了一个双向链表]
改查多:ArrayList[底层维护Object类型的可变数组]
不允许重复:Set
无序:HashSet[底层是HashMap,维护了一个哈希表即[数组+链表+红黑树]
排序:TreeSet
插入和取出顺序一致:LinkedHashSet,维护数组+双向链表
3、一组键值对:Map
键无序:HashMap数组+链表+红黑树]
键排序:TreeMap
键插入和取出顺序一致:LinkedHashMap
读取文件:Properties
TreeSet与TreeMap
TreeSet
当我们使用无参构造器,创建TreeSet时,仍然是无序的
使用TreeSet提供的一个构造器,可以传入一个比较器
TreeMap
当我们使用无参构造器,创建TreeMap时,仍然是无序的
Collections工具类
Collections是一个操作Set、List和Map等集合的工具类
Collections提供了一些列静态的方法对集合元素进行排序、查询和修改等操作
排序操作(均为static方法)
reserve(List):反转List中元素的顺序
shuffle(List):对List集合元素进行随机排序
sort(List):根据元素的自然顺序对指定List集合元素按升序排序
sort(List,Comparator):根据指定的Comparator产生的顺序对List集合元素进行排序
swap(List,int i,int j):将指定List集合中的i处元素和j处元素进行交换
查找、替换
Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
Object max(Collection,Comparator):根据Comparator指定的顺序,返回给定集合中的最大元素
Object min(Collection):和max相同
Object min(Collection,Comparator):和max相同 int frequency(Collection,Object):返回指定集合中指定元素的出现次数
void copy(List dest,List src):将src中的内容复制到dest中
boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换List对象的所有旧值
/* Collections工具类 Collections是一个操作Set、List和Map等集合的工具类 Collections提供了一些列静态的方法对集合元素进行排序、查询和修改等操作 */ List list = new ArrayList(); list.add("Hello"); list.add("World"); list.add("Fine"); list.add("wowo"); list.add("surprise"); /* 排序操作(均为static方法) reserve(List):反转List中元素的顺序 */ Collections.reverse(list); System.out.println("list - reverse+ " + list); /* shuffle(List):对List集合元素进行随机排序 */ Collections.shuffle(list); System.out.println("list - shuffle + " + list); // sort(List):根据元素的自然顺序对指定List集合元素按升序排序 Collections.sort(list); System.out.println("list - sort + " + list);; // sort(List,Comparator):根据指定的Comparator产生的顺序对List集合元素进行排序 Collections.sort(list, new Comparator() { @Override public int compare(Object o1,Object o2){ return (((String)o1).length() - ((String)o2).length()); } }); System.out.println("list - sort - com + " + list); // swap(List,int i,int j):将指定List集合中的i处元素和j处元素进行交换 Collections.swap(list,2,3); System.out.println("list - swap + " + list); // 查找、替换 // Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素 System.out.println("Max = " + Collections.max(list)); // Object max(Collection,Comparator):根据Comparator指定的顺序,返回给定集合中的最大元素 System.out.println("MaxOfLength = " + Collections.max(list, new Comparator() { @Override public int compare(Object o1,Object o2){ return (((String)o1).length() - ((String)o2).length()); } })); // Object min(Collection):和max相同 // Object min(Collection,Comparator):和max相同 int frequency(Collection,Object):返回指定集合中指定元素的出现次数 // void copy(List dest,List src):将src中的内容复制到dest中 List dest = new ArrayList(); //为了完成一个完整的拷贝,需要先给dest赋值,使其和list大小相同,否则会有下标越界的错误 for (int i = 0;i < list.size();i++){ dest.add(" "); } Collections.copy(dest,list); System.out.println("dest = " + dest); // boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换List对象的指定旧值 Collections.replaceAll(list,"wowo","tom"); System.out.println("list + replaceAll = " + list);