集合(一)Collection接口
文章目录
-
集合(一)Collection接口
一、Collection接口
-
Collection接口:单列集合,用来存储一个一个的对象
-
List接口:存储有序的、可重复的数据
- ArrayList、LinkedList、Vector
-
Set接口:存储无序的、不可重复的数据
- HashSet、LinkedHashSet、TreeSet
- 向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals();
1.1 Collection接口常用方法
- add(Object obj),addAll(Collection coll),size(),isEmpty(),clear();
- contains(Object obj),containsAll(Collection coll),remove(Object obj),removeAll(Collection coll),retainsAll(Collection coll),equals(Objiect obj)
- hasCode(),toArryay(),iterator();
1.2 Collection集合与数组间的转换
- 集合————>数组:toArray()
public class Test {
public static void main(String[] args) {
Collection<Integer> coll = new ArrayList<>();
coll.add(12);
coll.add(123);
coll.add(565);
Object[]arr=coll.toArray();
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]);
}
}
- 数组————>集合:调用Arrays类的静态方法asList()
public class Test2 {
public static void main(String[] args) {
List<String> list = Arrays.asList("Aa", "AA", "Bbb");
System.out.println(list);
//雷区
//Arrays.asList源码:传入一个变长泛型参数(注意泛型!),返回一个List集合,在Java中,泛型要求包容的是对象类型,而基本数据类型在Java中不属于对象,但是在示例代码中传入一个int[]类型的数组,程序并没有报错,是因为int[]是一个数组对象,示例代码把int类型的数组作为泛型传入,所以代码没有报错。
List arr1= Arrays.asList(new int[]{123, 456});
System.out.println(arr1.size());//1
//所以使用Arrays.asList时,如果传入数组类型为基本数据类型,就使用对应的包装类作为入参:
List arr2= Arrays.asList(new Integer[]{123, 456});
System.out.println(arr2.size());//2
}
}
1.3 遍历Colliction俩种方式
①使用迭代器Iterator
**GOF给迭代器模式的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。迭代器模式,就是为容器而生。**类似于“公交车上的售票员、”火车上的乘务员“、”空姐“
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R4vS7IIK-1657550642664)(C:\Users\永不遗憾&象牙塔\AppData\Roaming\Typora\typora-user-images\image-20220709104328004.png)]
-
Itrator仅用于遍历集合,Iterator本身并不提供承装对象的能力。如果需要创建Iterator对象,则必须有一个被迭代的集合。
-
集合对象每次调用Iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前。
public class Test{
public static void main(String[] args) {
Collection<Integer> coll = new ArrayList<>();
coll.add(12);
coll.add(123);
coll.add(565);
Iterator it = coll.iterator();
//hasNext();判断是否还有下一个元素
while (it.hasNext()) {
//next():①指针下移②将下移以后集合位置上的元素返回
System.out.println(it.next());
}
}
}
remove()的使用:
-
测试Iterator中的remove()
-
如果还未调用next()或在上次调用next方法之后已经调用了remove方法,再调用remove都会报IllegalStaeException.
-
迭代器内部定义了remove(),可以在遍历时,删除集合中的元素,此方法不同于集合直接调用remove()
public class Test {
public static void main(String[] args) {
Collection coll = new ArrayList();
coll.add(12);
coll.add(123);
coll.add(565);
coll.add(new Person("TOM",12));
coll.add(new String("Tom"));
Iterator it = coll.iterator();
while (it.hasNext()) {
Object o = it.next();
if (o.equals(12)) {
it.remove();
}
}
it=coll.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}
②foreach循环(或增强for循环)
- 内部仍调用了迭代器
- for(集合元素的类型 局部变量 :集合对象)
遍历集合
public class Test{
public static void main(String[] args) {
Collection<Integer> coll = new ArrayList<>();
coll.add(12);
coll.add(123);
coll.add(565);
for (Integer integer : coll) {
System.out.println(integer);
}
}
}
遍历数组
public void test(){
int []arr=new int[]{1,2,3,4,5,6};
//for(数组元素类型 局部变量:数组对象)
for(int i:arr){
System.out.println(i);
}
}
二、Collection子接口List接口
2.1 存储特点:
- Collection接口:单列集合,用来存储一个一个的对象
- List接口:存储有序的、可重复的数据。——>“动态”数组,替换原来的数组
- ArrayList:作为Lsit接口的主要实现类:线程不安全的,效率高;底层使用Objiect[]elementData存储
- LinkedList:对于频繁的插入操作、删除操作,使用此类效率比ArrayList高:底层使用双向链表存储
- Vector:作为List接口的古老实现类;线程是安全的,效率低:底层使用Object[]elementData存储
- 添加的对象,所在的类要重写equals()方法
- List接口:存储有序的、可重复的数据。——>“动态”数组,替换原来的数组
2.2 常用方法
- 增:add(Object obj)
- 删:remove(int index)/remove(Object obj)
- 改:set(int index,Object ele)
- 查:get(int index)
- 插: add(int index,Object ele)
- 长度:size()
- 遍历:①Iterator迭代器方式
②增强for循环
③普通的循环
2.3源码分析
2.3.1 ArrayList的源码分析
jdk 7 情况下
ArrayList list=new ArrayList();底层创建了长度是10的Object[]数组elementData
list.add(123);elementData[0]=new Integer(123);
…
list.add(11);//如果此次的添加导致底层elementData数组容量不够,则扩容。
默认情况下,扩容为原来的容量1.5倍,同时需要将原有数组中的数据复制到新的数组中。
结论:建议开发中使用带参的构造器:ArrayList list=new ArrayList(int capacity)
jdk 8中ArrayList的变化
ArrayList=new ArrayList(); 底层Objiect[] elementData初始化为{},并没创建长度为10的数组
list.add(123); 第一次调用add()时,底层才创建10的数组,并将数据添加到elementData[0]
…
后续的添加和扩容操作与jdk7无异
小结
- jdk7中的ArrayList的对象的创建类似于单例的饿汉式,而jdk8中的ArrayList的对象创建类似于单例懒汉式,延迟了数组创建节省内存
2.4 LinkedList的源码分析
LinkedList list=new LinkedList();//内部声明了Node类型的first和last属性,默认值为null
list.add(123);//将123封装到Node中,创建了Node对象。
其中,Node定义为:提现了LinkedList的双向链表的说法
private static class Node<E>{
E item;
Node<E> next;
Node<E> prev;
Node(Node<E>prev,E element,Node<E> next){
this.item=element;
this.next=next;
this.prev=prev;
}
}
2.5 Vector的源码分析
-
jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。
-
在扩容方面,默认扩容为元凯的数组长度的2倍
2.6 作业(面试题)
ArrayList、LinkedList、Vector者的异同?
-
相同点:三个类都实现了List接口,存储数据的特点相同:存储有序的、可重复的数据
-
不同点:特点,数据结构,源码分析(见上 )
练习:在List内去除重复数字值,要求尽量简单
public class Test {
public static void main(String[] args) {
ArrayList<Integer> Listy = new ArrayList<>();
Listy.add(1);
Listy.add(2);
Listy.add(2);
Listy.add(2);
Listy.add(5);
Listy.add(6);
List list = dupliicateList(Listy);
for (Object o : list) {
System.out.println(o);
}
}
private static List dupliicateList(List list) {
HashSet <Integer>hashSet = new HashSet();
hashSet.addAll(list);
return new ArrayList(hashSet);
}
三、Collection子接口Set接口
3.1 存储特点
-
无序的,不可重复的
-
以HashSet为例说明:
- 无序性:不等于随机性。存储的数据在底层数组中并非照数组索引的顺序添加,而是根据数据的哈希值决定
- 不可重复性:保证添加的元素equals()判断时,不能返回true,即:相同的元素只能添加一个
-
Collection接口:单列集合,用来存储一个一个的对象
- Set接口:存储无序的、不可重复的数据
- HashSet:作为Set接口的主要实现类;线程不安全的;可以存储null值
- LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以照添加的顺序遍历
- TreeSet:可以照添加对象的指定属性,进行排序。
- Set接口:存储无序的、不可重复的数据
3.2 常用方法
-
Set接口中没额外定义新的方法,使用的都是Collection中声明过的方法。
-
要求:向Set(主要指:HashSet和LinkedHashSet)中添加的数据,其所在的类一定要重写hashCode()和equals()
-
要求:重写的hashCode()和equals()尽可能保持一致性:相等的对象的散列码;重写两个方法的小技巧:对象中用作equals()方法比较的Field,都应该用来计算hashCode
3.2.1 TreeSet
-
自然排序中,比较两个对象是否相同的标准为:compareTo()返回,不再是equals().
-
定制排序中,比较俩个对象是否相同的标准为:compare()返回,不再时equals().
使用说明: -
向TreeSet中添加的数据,要求是相同类的对象
-
俩种排序方式:自然排序(实现Comparable接口)和定制排序(Comparator)
自然排序
public class Test {
public static void main(String[] args) {
TreeSet<Person> set = new TreeSet<>();
set.add(new Person("WangChao",31));
set.add(new Person("Lisi",14));
set.add(new Person("ZAHNG三",16));
set.add(new Person("王五",17));
Iterator<Person> it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}
public class Person implements Comparable <Person>{
int age;
String name;
public Person(){
}
@Override
public String toString() {
return "姓名"+this.name+
"年龄"+this.age;
}
public Person(String name, int age){
this.name = name;
this.age = age;
}
@Override
public int compareTo(Person o) {
return -(this.age - o.age);//降序
//this.age - o.age 升序
}
}
定制排序
public class Test2 {
public static void main(String[] args) {
TreeSet<Person> set = new TreeSet<Person>(new Comparator<Person>(){
@Override
public int compare(Person p1, Person p2) {
int num = p1.getAge() - p2.getAge();
int num2 = num == 0 ? p1.getName().compareTo(p2.getName()) : num;
return num2;
}
});
set.add(new Person("WangChao",31));
set.add(new Person("Lisi",14));
set.add(new Person("ZAHNG三",16));
set.add(new Person("王五",17));
for (Person p : set) {
System.out.println(p);
}
}
}
3.2.3 HashSet练习题
public class Test2 {
public static void main(String[] args) {
HashSet<Person> set = new HashSet<>();
Person p1 = new Person(1001, "AA");
Person p2 = new Person(1002, "BB");
set.add(p1);
set.add(p2);
p1.name = "CC";
set.remove(p1);
System.out.println(set);
set.add(new Person(1001,"CC"));
System.out.println(set);
set.add(new Person(1001,"AA"));
System.out.println(set);
}
}
/*[1002,BB, 1001,CC]
[1002,BB, 1001,CC, 1001,CC]
[1002,BB, 1001,CC, 1001,CC, 1001,AA]*/