集合框架
- Collection接口
- 常用方法:
- 1. add(Object e) 将元素e添加到集合Coll中
- 2. size() 获取添加的元素的个数
- 3. addAll 另一个Collection的元素全都添加
- 4. clear() 清空集合元素
- 5. isEmpty() 判断当前集合是否为空
- 6. contains(Object obj) 判断当前集合中是否包含obj
- 7. containsAll(Collection coll1) 判断形参coll1中的所有元素是否都存在于当前集合中
- 8. hashCode() 返回当前对象的哈希值
- 9. toArray()
- 10. remove(Object o)
- 11. removeAll(Collection<?> c) 差集
- 12. retainAll(Collection<?> c) 求交集
- of() 和copyOf()
- 遍历
- 子接口
- Map接口
- Collections工具类:操作Collection和Map
Collection接口
单列集合,用来存储一个一个的对象
List接口和Set接口都实现了Collection接口
常用方法:
1. add(Object e) 将元素e添加到集合Coll中
2. size() 获取添加的元素的个数
3. addAll 另一个Collection的元素全都添加
4. clear() 清空集合元素
coll.clear()
5. isEmpty() 判断当前集合是否为空
System.out.println(coll.Empty())
6. contains(Object obj) 判断当前集合中是否包含obj
用equals比的,而不是== 所以比的是内容不是地址
7. containsAll(Collection coll1) 判断形参coll1中的所有元素是否都存在于当前集合中
只要有不在的就是false
8. hashCode() 返回当前对象的哈希值
System.out.println(coll.hashCode())
9. toArray()
asList()数组–>集合
10. remove(Object o)
coll.remove(123);//也会调用equals方法
11. removeAll(Collection<?> c) 差集
12. retainAll(Collection<?> c) 求交集
Collection coll = new ArrayList();
coll.add(123);
coll.add(new String("AA"));
coll.add("122");
Collection coll1 = Arrays.asList(123,456,789);
coll.retainAll(coll1);//coll输出的是交集123
- equals(Object o) 判断两个集合相等
of() 和copyOf()
of() 创建一个只读的集合
copyOf(Xxx coll) 如果coll本身就是只读集合,那copyOf就直接返回coll;如果coll不是只读集合,那copyOf()就要返回一个新的集合
遍历
遍历集合的方法:
- 方法一:iterator
- iterator() 返回Iterator接口的实例,用于遍历集合元素
有hasNext()、next() 和remove()【没调next之前不要调remove】方法
Collection coll = new ArrayList();
coll.add(123);
coll.add(new String("AA"));
coll.add("122");
Iterator iterator = coll.iterator();
while(iterator.hasNext()){//判断是否还有下一个元素
Object obj = iterator.next();//调next的时候指针下移,将下移以后集合位置上的元素返回
if("122".equals(obj)){
iterator.remove();//删除集合中122元素
}
}
iterator可以删除集合的元素,但是是遍历过程中通过迭代器对象的remove方法,不是集合对象的remove方法
如果还未调用next() 或在上一次调用next()方法之后已经调用了remove方法,再调用remove都会报IllegalStateException
- 方法二:for each
内部仍然调用了迭代器
for(Object obj:coll){
System.out.println(obj);
}
子接口
子接口:List接口
有序、可重复
- 有序的、可重复的数据 —>“动态”数组(长度可变)
由于Java中数组存储数据的局限性,通常使用List代替数组
ArrayList、LinkedList、Vector三者异同
- ArrayList、LinkedList、Vector三者异同
三个类都实现了List接口,存储的特点相同:存储有序的、可重复的数据
- ArrayList:作为List接口的主要实现类;线程不安全,效率高;底层使用Object[] elementData存储
- LinkedList:对于频繁的插入删除操作,使用此类效率比ArrayList高;底层使用双向链表存储
- Vector:作为List接口的古老实现类;线程安全,效率低;底层使用Object[] elementData存储
常用方法
- 常用方法
- void add(int index,Object ele)
在index位置插入ele元素ArrayList list = new ArrayList(); list.add(123); list.add(456); list.add("AA"); list.add(456); System.out.println(list);//[123, 456, AA, 456] //void add(int index,Object ele) :在index位置插入ele元素 list.add(1,"BB"); System.out.println(list); //[123, BB, 456, AA, 456]
- boolean addAll(int index, Collection eles)
从index位置开始将eles中所有的元素添加//boolean addAll(int index, Collection eles) 从index位置开始将eles中所有的元素添加 List list1 = Arrays.asList(1,2,3); list.addAll(3,list1); System.out.println(list);//[123, BB, 456, 1, 2, 3, AA, 456]
- Object get(int index)
获取指定index位置的元素//Object get(int index) 获取指定index位置的元素 System.out.println(list.get(5));//3
- int indexOf(Object obj)
返回obj在集合中首次出现的位置,如果不存在返回-1int index = list.indexOf("BB"); System.out.println(index);//1
- int lastIndexOf(Object obj)
返回obj在集合中末次出现的位置System.out.println(list.lastIndexOf("BB"));//1
- Object remove(int index)
移除指定index位置的元素,并返回此元素Object remove = list.remove(1);//remove 是“BB” System.out.println(remove);//BB System.out.println(list);//[123, 456, 1, 2, 3, AA, 456]
- Object set(int index, Object ele)
设置指定index位置的元素为elelist.set(5,"CC"); System.out.println(list);//[123, 456, 1, 2, 3, CC, 456]
- List subList(int fromIndex,int toIndex)
返回从fromIndex到toIndex位置的左闭右开区间List subList = list.subList(2,4); System.out.println(subList);//[1, 2]
遍历
- 遍历
- 方式一:Iterator迭代器方式
Iterator iterator = list.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); }
- 方式二:增强for循环 for each
for (Object obj:list) { System.out.println(obj); }
- 方式三:普通for循环
for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); }
- 面试题
- 区分list中的remove方法
collection有一个remove(Object o)删的是对象
list有一个重载的remove(int index)按索引删的
public void updateList(List list){
list.remove(2);//删的是索引2指的3 [1,2,3] --> [1,2]
list.remove(new Integer(2));//[1,2]-->[1]
}
实现类
ArrayList
ArrayList:作为List接口的主要实现类;线程不安全,效率高;底层使用Object[] elementData存储
- jdk7 new ArrayList的时候就创建了数组,类似单例的饿汉式,
- jdk8 add的时候才创建,类似于单例的懒汉式,延迟数组创建,节省内存
LinkedList
LinkedList:对于频繁的插入删除操作,使用此类效率比ArrayList高;底层使用双向链表存储
Vector
Vector:作为List接口的古老实现类;线程安全,效率低;底层使用Object[] elementData存储
子接口:Set
无序、不可重复
-
Set是无序的(不等于素姬姓)、不可重复的数据 类似高中讲的集合
- 无序性
无序性不等于随机性。以HashSet为例,存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。所以无序指的存放的位置不是挨着一个一个放的。 - 不可重复性
保证添加的元素按照equals()方法判断时,不能返回true。即:相同的元素只能添加一个
- 无序性
-
Set接口没有额外定义新的方法,使用的都是Collection中生命过的方法
-
对于自定义类要调用Set方法要重写HashCode和equals方法
添加元素的过程
- 添加元素的过程,以HashSet为例
向HashSet中添加元素a,首先计算元素a所在类的hashCode()方法,计算元素a的哈希值;此时哈希值通过某种算法在HashSet底层数组中的存放位置(索引位置),判断此位置上是否有元素;
如果此位置没有其他元素,则元素a添加成功。【情况1】
如果此位置有其他元素b(或以链表形式存在的多个元素),则比较元素a和元素b的hash值:
如果hash值不同,则元素a添加成功。【情况2】
如果hash值相同,进而需要调用元素a所在类的equals方法,equals()返回true元素a添加失败;equals返回false,则则元素a添加成功。【情况3】
对于添加成功的情况2和情况3而言:元素a与已经存在指定索引位置上数据以链表的方式存储。jdk7中元素a放到数组中,指向原来的元素。jdk8中原来的元素在数组中,指向元素a。(总结:七上八下)
实现类
HashSet
作为Set接口的主要实现类;线程不安全的;可以存储null值
LinkedHashSet
LinkedHashSet是HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据。
遍历其内部数据时,可以按照添加的顺序的遍历
优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet
TreeSet
可以按照添加对象的指定属性进行排序
- 向TreeSet中添加的数据,要求是相同类的对象。
- 两种排序方式:自然排序(实现comparable接口)和定制排序(comparator)
- 自然排序
比较两个对象是否相同的标准为:compareTo()返回0,不再是equals() - 定制排序
比较两个对象是否相同的标准为:compare()返回0,不再是equals()
练习:TreeSet 通过hash值存放元素
public void test1(){
HashSet set = new HashSet();
Person p1 = new Person(1001,"AA");
Person p2 = new Person(1002,"BB");
set.add(p1);
set.add(p2);
System.out.println(set); //[Person{id=1002, name='BB'}, Person{id=1001, name='AA'}]
p1.setName("CC");
set.remove(p1);//当前p1位置是通过name 为AA的p1算出来的, remove时计算name为CC的p1的hashcode 所以p1没有被删除
System.out.println(set);//[Person{id=1002, name='BB'}, Person{id=1001, name='CC'}]
set.add(new Person(1001,"CC")); //能添加成功 因为p1位置的是通过“AA”算的
System.out.println(set);//[Person{id=1002, name='BB'}, Person{id=1001, name='CC'}, Person{id=1001, name='CC'}]
set.add(new Person(1001,"AA"));//能添加成功 虽然和最初的p1位置一样,但是现在p1的是“CC” add的是“AA”
System.out.println(set);//[Person{id=1002, name='BB'}, Person{id=1001, name='CC'}, Person{id=1001, name='CC'}, Person{id=1001, name='AA'}]
}
Map接口
双列集合,用来存储一对一对(key-value)的数据
- Map结构的理解
Map接口常用方法
添加put(Object key,Object value) putAll(Map m)
移除 Object remove(Object key)
返回移除的key对应的value
清空 clear()
清空当前map的所有数据
查询Object get(Object key)
获取指定key对应的value
boolean containsKey(Object key) containsValue(Object value)
containsKey是否包含指定的key
containsValue是否包含指定的value
遍历
-
keySet() 遍历所有的key集
-
values() 遍历所有的value集
-
entrySet() 遍历所有的key-value
方式一:
方式二:
实现类:HashMap
- HashMap是Map的主要实现类。
- 线程不安全,效率高。
- HashMap可以存储null的key和value
HashMap的底层实现原理(面试题)
-
jdk7中HashMap的底层实现原理
-
jdk8和7的不同
LinkedHashMap
保证在遍历map元素时,可以按照添加的顺序来实现遍历。因为LinkedHashMap在原有的HashMap底层结构基础上添加了一对指针,指向前一个和后一个元素。
对于频繁的遍历操作,LinkedHashMap效率高于HashMap
实现类: TreeMap
按照添加的key value进行排序,实现排序遍历。此时考虑key的自然排序或定制排序。
向TreeMap添加key value,要求key必须是由同一个类创建的对象。因为要用key进行排序
实现类:Hashtable
古老的实现类。线程安全,效率低。
不能存储null的key和value
Properties
常用来处理配置文件,key和value都是String类型
Collections工具类:操作Collection和Map
copy方法:
要注意dest的size() 要比list的size() 大,不然会抛异常。size()是实际的元素数而不是数组的长度
同步控制