JAVA集合(二)
一、Collection子接口之一:List接口
使用Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals().
1、List接口概述
2、面试题:ArrayList、LinkedList、Vector三者的异同?
同:
三个类都实现了List接口,存储数据的特点相同:存储有序的、可重复的数据
不同:
ArrayList:List接口的主要实现类;线程不安全,效率高;底层源码是Object[] elementData存储
LinkedList:对于频繁的插入、删除操作。使用此类效率比Arraylist高;底层使用双向链表存储
Vector:List接口的古老实现类;线程安全的,效率低;底层源码Object[]存储 elementData存储
3、ArrayList的源码分析
JDK 7情况下:
JDK 8情况下:
ArrayList list=new ArrayList(); 底层Object[] elementData初始化为{},并没有创建长度为10的数组
list.add(123); 第一次调用add()时,底层才创建了长度10的数组,并将数组123添加到elementData[0]
小结:jdk7中的ArrayList的对象创建类似于单例的饿汉式,而Jdk8中的ArrayList的对象的创建类似于单例的懒汉式,延迟了数组的创建,节省内存。
4、linkedList的源码分析:
LinkedList list =new LinkedList(); 内部声明了Node类型的first和last属性,默认为null。
list(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=item;
this.next=next;
this.prev=prev;
}
}
5、Vector的源码分析:jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组;再扩容方面,默认扩容为原来的数组长度的2倍。
二、List实现类之二:LinkList接口及List接口常用方法
举例1:
@Test
public void test1(){
ArrayList list = new ArrayList();
list.add(123);
list.add(456);
list.add("AA");
list.add(new Person("Tom",12));
list.add(456);
System.out.println(list);
//1.viod add(int index,Object ele);在index位置插入ele元素
list.add(1,"BB");
System.out.println(list);
System.out.println("==================");
//2.boolean addAll(int index,Collection eles);从index位置开始键eles中所有元素添加
List list1=Arrays.asList(1,2,3);
list.addAll(list1);
//注意区别 list.add(list);
System.out.println(list);
System.out.println(list.size());//6+3=9
//3.Object get(int index);获取指定index位置的元素
System.out.println(list.get(2));
}
举例2:
@Test
public void test02(){
ArrayList list = new ArrayList();
list.add(123);
list.add(456);
list.add("AA");
list.add(new Person("Tom",12));
list.add(456);
//4.int indextOf(Object obj);返回obj在集合中首次出现的位置
int index=list.indexOf(456);
System.out.println(index);//存在输出是1,否则输出是-1
//5int lastindexOf(Object obj);返回obj在集合中末次出现的位置
System.out.println(list.lastIndexOf(456));//存在输出是元素所在的索引位置,否则输出是-1
System.out.println("===========");
//6.Object remove(int index);移除指定index位置的元素
Object obj = list.remove(0);
System.out.println(obj);//元素的位置
System.out.println(list);//删除之后所有元素都会迁移
//7.Object set(int index,Object ele);设置指定index位置的元素为ele
list.set(1,"cc");
System.out.println(list);
//8.List sublist(int fromIndex,int toIndex);返回从fromIndex到toIndex位置的左闭右开子集合
List subList=list.subList(2,4);
System.out.println(subList);
}
总结:常用方法
增: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循环、普通循环遍历
遍历举例:
@Test
public void test03(){
ArrayList list = new ArrayList();
list.add(123);
list.add(456);
list.add("AA");
//方式一:Iterator迭代器方式
Iterator iterator = list.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("-------------------");
//方式二:增强for循环
for(Object obj:list){
System.out.println(obj);
}
System.out.println("-------------------");
//方式三:普通for循环
for (int i = 0; i <list.size() ; i++) {
System.out.println(list.get(i));
}
}
三、Collection子接口之二:Set接口
1、Set接口概述
2、set接口的框架
set接口:存储无序的、不可重复的数据
HashSet:作为Set接口的主要实现类;线程不安全的;可以存储null值
LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历
TreeSet:可以按照添加对象指定属性,进行排序。
⑴HashSet的使用:
/*
一、
Set:存储无序的、不可重复的数据
以HashSet为例说明:
1.无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。
2.不可重复性:保证添加的元素按照equals()判断时,不能返回true。即:相同的元素只能添加一个。
二、添加元素的过程:以HashSet为例
我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,
此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即:索引位置):判断
数组此位置上是否已经有元素:
如果此位置上没有其他元素,则元素a添加成功。---》情况1
如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
如果hash值不相同,则元素a添加成功。---》情况2
如果hash值相同,进而需要调用元素a所在类的equals()方法:
equals()返回ture,元素a添加失败
equals()返回false,则元素a添加成功。--》情况3
对于添加成功的情况2和情况3而言:元素a与已经存在指定索引位置上数据以连接表的方式存储
jdk7:元素a放到数组中,指向原来的元素
jdk8:原来的元素在数组中,指向元素a
总结:七上八下
HashSet底层:数组+链表的结构
*/
@Test
public void test1(){
Set set = new HashSet();
set.add(456);
set.add(123);
set.add("AA");
set.add("BB");
set.add(new User("Tom",11));
set.add(new User("Tom",11));
set.add(789);
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
①set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法。
②要求:向Set中添加的数据,其所在的类一定要重写hashCode ()和equals()
要求:重写的hashCode()和equals()尽可能保持一致:想等的对象必须具有相等的散列码。
⑵LinkHashSet的使用:
LinkHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录数据前一个数据和后一个数据。
优点:对于频繁的遍历操作,LinkHashSet效率高于HashSet
⑶TreeSet的使用:
①向TreeSet中添加的数据,要求是相同类的对象。
②两种排序方式:自然排序(实现comparable接口)和定制排序(Comparator接口)
③自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals()。
④定制排序中,比较两个对象是否相同的标准为:compare()返回0,不再是equals()。
自然排序(实现comparable接口)举例:
//按照姓名从小到大排列,年龄从小到大排列
@Override
public int compareTo(Object o) {
if (o instanceof User){
User user=(User)o;
/*
return this.name.compareTo(user.name);
//从大到小
//return -this.name.compareTo(user.name);
*/
int compare=-this.name.compareTo(user.name);
if (compare!=0){
return compare;
}else{
return Integer.compare(this.age, user.age);
}
}else{
throw new RuntimeException("输入的类型不匹配");
}
}
@Test
public void test(){
TreeSet set = new TreeSet();
/*
//失败:不能添加不用类的对象
set.add(123);
set.add("AA");
set.add(new User("Tom",12));
*/
/*
//举例一:结果从下到大输出
set.add(123);
set.add(-6);
set.add(8);
set.add(89);
*/
//举例二:
set.add(new User("Tom",12));
set.add(new User("AA",2));
set.add(new User("BB",8));
set.add(new User("CC",10));
set.add(new User("CC",8));
//快捷键 迭代器遍历 itit
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
}
定制排序(Comparator接口)举例:
@Test
public void test2(){
Comparator com=new Comparator() {
//按照年龄从小到大排列,年龄一样就不要了
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof User && o2 instanceof User){
User u1=(User)o1;
User u2=(User)o2;
return Integer.compare(u1.getAge(), u2.getAge());
}else {
throw new RuntimeException("输入的类型不一致");
}
}
};
//不加参数按照自然排序的方式,加上参数按照参数的排列顺序来
TreeSet set = new TreeSet(com);
set.add(new User("Tom",12));
set.add(new User("AA",2));
set.add(new User("BB",8));
set.add(new User("CC",10));
set.add(new User("CC",8));
//快捷键 迭代器遍历 itit
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
}