集合
目录前言
本文为B站Java教学视频BV1Kb411W75N的相关笔记,主要用于个人记录与分享,如有错误欢迎留言指出。
本章笔记涵盖视频内容P516~P561
1. 集合框架概述
- 定义:集合,数组都是对多个数据进行存储操作的结构,简称Java容器;此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储
-
数组在储存多个数据方面的缺点:
- 一旦初始化以后,其长度就不可修改
- 数组中提供的方法非常有限,对于添加,删除,插入数据等操作非常不便,同时效率不高
- 对于获取数组中实际元素的个数的需求,数组没有现成的属性或方法可用
- 数组存储数据的特点:有序,可重复。对于无序,不可重复的需求,不能满足
-
集合框架:
- Collection接口:单列集合,用来存储单个单个的对象
- List接口:存储有序的,可重复的数据(ArrayList,LinkedList,Vector)
- Set接口:储存无序的,不可重复的数据(HashSet,LinkedHashSet,TreeSet)
- Map接口:双列集合,用来存储一对(key - value)的数据(HashMap,LinkedHashMap,Hashtable,Properties)
- Collection接口:单列集合,用来存储单个单个的对象
2. Collection接口
2.1 常用方法
- add(Object e):将元素e添加到集合coll中
- size():获取添加的元素的个数
- addAll(Collection coll):将coll集合中的元素添加到当前的集合中
- clear():清空集合元素
- isEmpty():判断当前集合是否为空
- contains(Object obj):判断当前集合中是否包含obj
- containsAll(Collection coll):判断形参coll中的所有元素是否都存在于当前集合中
- remove(Object obj):从当前集合中移除obj元素
- removeAll(Collection coll):从当前集合中移除coll中包含的所有元素
- retainAll(Collection coll):获取当前集合和coll集合的交集,并返回给当前集合
- equals(Object obj):当当前集合和形参集合的元素都相同时,返回true
- hashCode():返回当前对象的哈希值
- toArray():转化集合为数组
- literator():返回Iterator接口的实例,用于遍历集合元素。
public void test(){
Collection coll = new ArrayList(); //以collection接口下的ArrayList类型为例
//1. add(Object e):将元素e添加到集合coll中
coll.add("AA");
coll.add(123); //自动装箱
coll.add(new Date());
coll.add(new Person("Jerry",20)); //自定义类型
//2. size():获取添加的元素的个数
System.out.println(coll.size());//4
//3. addAll(Collection coll1):将coll1集合中的元素添加到当前的集合中
Collection coll1 = new ArrayList();
coll1.add(456);
coll1.add("CC");
coll.addAll(coll1);
System.out.println(coll.size());//6
//4. clear():清空集合元素
coll.clear(); //清空的是集合内部元素,而不是集合本身
//5. isEmpty():判断当前集合是否为空
System.out.println(coll.isEmpty()); //true
coll.add("AA");
coll.add(123); //自动装箱
coll.add(new Date());
coll.add(new Person("Jerry",20));
//6. contains(Object obj):判断当前集合中是否包含obj
System.out.println(coll.contains(123));//true
/*
该方法在判断时会调用obj类内的equals(),若该子类重写了equals则比较内部元素,此处返回true;若该子类未重写equals则比较地址值,此处返回false;同理,如果参数是一个类名(地址值),则无论有没有重写都会返回false
*/
System.out.println(coll.contains(new Person("Jerry",20)));
//7. containsAll(Collection coll1):判断形参coll1中的所有元素是否都存在于当前集合中
//Arrays.asList的作用是将数组转化为list,一般用于初始化简化代码,省去add的部分
Collection coll2 = Arrays.asList(123,456);
System.out.println(coll.containsAll(coll2));
//8. remove(Object obj):从当前集合中移除obj元素
coll.remove(123);
//和contain一样remove需要用到equals()方法,自定义类需要重写
coll.remove(new Person("Jerry",20));
//9. removeAll(Collection coll1):差集,从当前集合中移除coll1中包含的所有元素
Collection coll3 = Arrays.asList(123,456);
coll.removeAll(coll3);
//10. retainAll(Collection coll1):交集,获取当前集合和coll1集合的交集,并返回给当前集合
Collection coll4 = Arrays.asList(123,456,789);
coll.retainAll(coll4);
//11. equals(Object obj):当当前集合和形参集合的元素都相同时,返回true
//若形参本身有序,则比较在乎顺序;若形参本身无序,则比较不在乎顺序(ArrayList是有序的)
Collection coll5 = Arrays.asList(123,456);
System.out.println(coll.equals(coll5));
//12. hashCode():返回当前对象的哈希值
System.out.println(coll.hashCode());
//13. 集合 → 数组:toArray()
Object[] arr = coll.toArray(); //由于集合内部类型可能不一,所以用Object类数组接收较好
for(int i = 0;i < arr.length;i++){
System.out.println(arr[i]);
}
//13.1 数组 → 集合
List<String> list = Arrays.asList(new String[]{"AA","BB","CC"});
System.out.println(list); //[AA,BB,CC]
//注意:这种方法对于基本数据类型可能会出错
List arr1 = Arrays.asList(new int[]{123,456});
System.out.println(arr1);//会直接报地址值,因为集合将整个数组都看成一个元素
//解决方法:
//方式一:
List arr2 = Arrays.asList(123,456);//直接添加
//方式二:
List arr3 = Arrays.asList(new Integer[]{123,456});//使用包装类
//14. literator():返回Iterator接口的实例,用于遍历集合元素。
//(见下)
}
class Person{
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
2.2 Iterator迭代器
-
定义:Iterator对象称为迭代器,主要用于遍历Collection集合的元素。Iterator仅用于遍历集合,其本身并不提供承装对象的能力。集合对象每次调用iterator()方法都将得到一个全新的迭代器对象。
-
Iterator常用方法
//hasNext():判断是否还有下一个元素
//next():指针下移,并将下移后集合位置上的元素返回
Iterator iterator = coll.iterator;
while(iterator.hasNext()){
System.out.println(iterator.next());
}
//remove():删除当前集合位置上的元素
//与集合的remove方法不同,Iterator通过遍历迭代器对象的方法删除集合的元素
//如果还未调用next()或在上次调用next方法之后已经调用了remove方法,再调用remove都会报错
Iterator iter = coll.iterator; //回到起点
while(iter.hasNext()){
Object obj = iter.next();
if(obj.equals("Tom")){
iter.remove();
}
}
- foreach (增强for循环)
//for(数组元素的类型 局部变量 : 数组对象)
//遍历集合
for(Object obj : coll){
System.out.println(obj);
}
//遍历数组
for(int i : arr){
System.out.println(i);
}
/*
foreach只能用于遍历!不能完全取代for!
如果使用foreach赋值,有可能会出现赋值后值没有改变的情况(比如String类型)
*/
2.3 List接口
-
定义:List集合类中元素有序且可重复,集合中的每个元素都有对应的顺序索引,因此常用List替代数组。List接口的实现类常用的有:ArrayList,LinkedList和Vector
-
List接口下常用实现类的异同
-
相同点
- 三个类都实现了List接口,都可以存储有序的,可重复的数据
-
不同点
- ArrayList:作为List接口的主要实现类;线程不安全,效率高;底层使用Object[] elementData存储数据
- LinkedList:对于频繁的插入,删除操作,使用LinkedList的效率比ArrayList高;底层使用双向链表存储
- Vector:作为List接口的古老实现类;线程安全,效率低;底层使用Object[] elementData存储数据
-
相同点
-
List接口中的常用方法
- void add(int index, Object ele):在index位置插入ele元素
- boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
- Object get(int index):获取指定index位置的元素
- int indexOf(Object obj):返回obj在集合中首次出现的位置
- int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
-
Object remove(int index):移除指定index位置的元素,并返回此元素
- 此处相当于List重载了Collection中的remove方法,当remove参数为一个int型的时候,编译器会自动使用List中的方法,删除指定索引的数,而不是调用equals遍历数组删除指定值的数。如果要调用Collection中的方法,可以使用remove(Integer x)的方法
- Object set(int index, Object ele):设置指定index位置的元素为ele
- List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合
2.4 Set接口
- 定义:Set接口是Collection的子接口,但其没有提供额外的方法;Set集合类中元素无序且不可重复,如果尝试把两个相同的元素加入同一个Set集合中则操作失败;Set判断两个对象是否相同使用的是equals()
-
Set接口下常用实的现类
- HashSet:Set接口的主要实现类;线程不安全;可以储存null值
- LinkedHashSet:HashSet的子类,在添加数据的同时,每个数据还维护了两个引用(指针),记录此数据的前一个和后一个数据,遍历其内部数据时,可以按照添加的顺序遍历。对于频繁的遍历操作,LinkedHashSet效率高于HashSet
- TreeSet:可以按照添加对象的指定属性进行排序
-
Set中无序性与不可重复性的理解
- 无序性:不等于随机性。储存的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定
- 不可重复性:包整添加的元素按照equals()判断时,不能返回true,即:相同的元素只能添加一次。但是判断重复的方式涉及到哈希表底层原理,具体请看HashSet的元素添加过程
2.4.1 HashSet/HashLinkedSet
-
HashSet中元素的添加过程
向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置,判断数组此位置上是否已经有元素
-
若此位置上没有其它元素,则元素a添加成功
-
若此位置上有其它元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值
- 若hash值不相同,则元素a添加成功
- 若hash值相同,则需要调用元素a所在类的equals()方法
- 若equals()返回false,元素a添加成功
- 若euqals()返回true,元素a添加失败
jdk7中处理重复数组位置元素的方法是头插法添加链表
jdk8中处理重复数组位置元素的方法是尾插法添加链表
-
2.4.2 TreeSet
- 向TreeSet中添加的数据,要求是相同类的对象
- TreeSet有两种排序方式:自然排序(实现Comparable接口)和定制排序(Comparator)
- 自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,而不是equals()
- 定制排序中,比较两个对象是否相同的标准为:compare()返回0,而不是equals()
3. Map接口
3.1 常用方法
-
Object put(Object key, Object value):将指定key-value添加到(或修改)当前map对象中
-
void putAll(Map m):将m中的所有key-value对存放到当前map中
-
Object remove(Object key):移除指定key的key-value对,并返回value
-
void clear():清空当前map中的所有数据
-
Object get(Object key):获取指定key对应的value
-
boolean containsKey(Object key):是否包含指定的key
-
boolean containsValue(Object value):是否包含指定的value
-
int size():返回map中key-value对的个数
-
boolean isEmpty():判断当前map是否为空
-
boolean equals(Object obj):判断当前map和参数对象obj是否相等
-
元视图操作的方法(遍历方法)
public void test(){ Map map = new HashMap(); map.put("AA",123); map.put(45,1234); map.put("BB",56); //遍历所有的key集:keySet() Set set = map.keySet(); Iterator iterator = set.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); } System.out.println(); //遍历所有的value集:values() Collection values = map.values(); for(Object obj : values){ System.out.println(obj); //因为value的类型不确定,所以用obj } System.out.println(); //遍历所有的key-value //方式一:entrySet() Set entrySet = map.entrySet(); Iterator iterator1 = entrySet.iterator(); while(iterator1.hasNext()){ Object obj = iterator1.next();//返回的set里面只有Entry一种类型,可以向下转型 Map.Entry entry = (Map.Entry) obj; System.out.println(entry.getKey() + "-" + entry.getValue()); } //方式二: Set keySet = map.keySet(); Iterator iterator2 = keySet.iterator(); while(iterator2.hasNext()){ Object key = iterator2.next(); Object value = map.get(key); System.out.println(key + "-" + value); } }
3.2 Map的实现类的结构
- Map:双列数据,存储key-value对的数据
- HashMap:作为Map的主要实现类;线程不安全,效率高;可以存储值为null的key和value
- LinkedHashMap:在原有的HashMap基础上,添加了一对指针,指向前一个和后一个元素,保证在遍历map元素时,可以按照添加的顺序实现遍历;对于频繁的遍历操作,此类执行效率高于HashMap
- TreeMap:按照添加的key-value对进行排序,实现排序遍历;考虑key的自然排序或定制排序
- Hashtable:作为古老的实现类;线程安全,效率低;不能存储值为null的key和value
- Properties:常用来处理配置文件;key和value都是String类型
- HashMap:作为Map的主要实现类;线程不安全,效率高;可以存储值为null的key和value
3.2.1 HashMap/LinkedHashMap
-
HashMap中元素的添加过程
HashMap map = new HashMap(),在实例化后,底层创建了长度是16的一维数组Entry[] table
map.put(key1,value1),开始添加元素
首先,调用key1所在类的hashCode()计算key1的哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置
-
如果此位置上的数据为空,此时key1-value1添加成功
-
如果此位置上的数据不为空,意味着此位置上已存在数据,比较key1和已经存在数据的哈希值
- 如果key1的哈希值与已存在的数据的哈希值都不相同,此时key1-value1添加成功
- 如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较,调用key1所在类的equals(key2)比较
- 如果equals()返回false:此时key1-value1添加成功
- 如果equals()返回true:使用value1替换value2
jdk8相较jdk7在底层实现方面的不同:
-
new HashMap()时,底层没有创建一个长度为16的数组,首次调用put()方法时才会创建
-
jdk7底层结构只有数组+链表,jdk8中底层结构:数组+链表+红黑树
当数组的某一个索引位置上的元素以链表形式村的数据个数 > 8且当前数组的长度 > 64时,此时此索引位置上的所有数据改用红黑树储存
-
3.2.2 TreeMap
- 向TreeMap中添加key-value,要求key必须是由同一个类创建的对象
- 大多数设置都和TreeSet一致,具体参见TreeSet
3.3 Properties接口
-
定义:Properties是专门用于对properties文件进行操作的接口
(properties文件可在New → Resource Bundle 内创建)
//假设Properties文件内已定义name=Tom,password=abc123 //其中name,password就是key;Tome,abc123就是value,他们都是String类型 public class Test{ public static void main(String[] args) { //Properties常用来处理配置文件,key和value都是String类型 Properties pros = new Properties(); FileInputStream fis = null; try { fis = new FileInputStream("testing.properties"); } catch (FileNotFoundException e) { e.printStackTrace(); } try { pros.load(fis); //加载流对应的文件 } catch (IOException e) { e.printStackTrace(); } String name = pros.getProperty("name"); //获取key对应的value String password = pros.getProperty("password"); try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } //testing.properties文件(放在工程文件根目录下) name=Tom password=abc123
4.Collections工具类
-
定义:Collections是一个操作Set,List,Map等集合的工具类
-
常用工具类
- reverse(List):反转List中元素的顺序
- shuffle(List):对List集合元素进行随机排序
- sort(List):根据元素的自然顺序对指定List集合元素按升序排序
- sort(List, Comparator):根据指定的Comparator的定制顺序对List集合元素进行排序
- swap(List ,int int):将指定List集合中的i处元素和j处元素进行交换
- Object max(Collection):根据元素的自然排序,返回给定集合中的最大元素
- Object max(Collection, Comparator):根据Comparator指定的顺序,返回给定集合中的最大元素
- Object min(Collection):根据元素的自然排序,返回给定集合中的最小元素
- Object min(Collection, Comparator):根据Comparator指定的顺序,返回给定集合中的最小元素
- int frequency(Collection, Object):返回指定集合中指定元素的出现次数
- void copy(List dest, List src):将src中的内容复制到dest中
- boolean replaceAll(List list, Object oldVal, Object newVal):使用新值替换List中的元素
//对于void copy(List dest, List src)有特别需要关注的地方 //被拷贝的List与目标List要求大小一致 List list = new ArrayList(); list.add(123); list.add(0); list.add(65); list.add(-99); //以下写法会直接报异常IndexOutOfBoundsException List dest = new ArrayList(); //新建的List没有大小 List dest1 = new ArrayList(list.size()); //内部没有元素,实际dest.size()还是0 Collections.copy(dest,list); //正确写法 List dest2 = Arrays.asList(new Object[list.size()]); //内部被list.size()个null填满了 Collections.copy(dest,list);
-
Collections类还提供了多个synchronizedXxx()方法,该方法可以将指定集合包装成线程同步的集合