Arrays存储对象时具有一些弊端;
数组:一旦初始化之后,其长度就确定了,不可修改;同时需要指定其元素类型;数组中提供的方法非常有限,删除插入数据非常不便,同时效率不高;获取数组中实际元素的个数的需求,数据没有现成的属性方法可使用;有序可重复,对于无序的需求,数组不能满足其要求;
集合对多个对象存储;可以动态地把对象方法到集合中可以分为和map,collection保存了单列数据,collection保存了双列数据
collection:
list(可重复的,有序的):ArrayList,LinkedList,Vector
动态数组;
set(无序的,不可重的):Hashset,linkedHashSet,TreeSet
map:hashmap,linkedmap,treemap,hashtable,properites;
collection接口;
**addall()😗*将元素添加到coll中;
**contains(**Object obj) 会调用obj对象所在类的equals()方法;
自定义类时,要求obj所在的类重写equlas()方法;
**containsAll()**方法判断一个集合是否全部包含另一个集合中的元素;
remove():**removaAll(col)**删除当前集合和col集合中共有的数据,相当于就是差集操作;
col.retainAll(col1) 求col和col1中的交集,并返回给当前集合;
**equals(Obj)**判断当前集合和另一个集合中的元素是否相同;
**hashcode()**方法:返回当前集合的hash值;
集合->数组 :toArray()方法:Object[] arr=col.toArray();
数组->集合:Arrays.asList(new String)
List l1= Arrays.List(new Integer(){123,456})
迭代器接口:Iterator ()集合元素的遍历:
col.iterator();获取一个迭代器的实例;
Iterator i=col.iterator();
i.next();
//该方法每次调用都有一个全新iterator()对象,默认指针指向集合的第一个元素之前;//取出下一个元素
while(i.hasNext()){
//指针先下移,随后返回当前指针指向的元素
System.out.print(i.next());}
remove()方法删除集合中不想要的元素;
增强for循环
for(元素类型 局部变量:集合对象)
ArrayList:线程不安全,效率高,底层使用object[]类型的数组进行存储;查找效率高;
LinkedList:底层使用双向链表;对于频繁的删除插入操作效率高;
Vector:线程安全的,效率低,
object[]类型的数组进行存储;
相同点:三个都是实现了list的接口,存储数据的特点相同,都是有序的可重复的数据;
ArrayList al=new ArrayList();
默认扩容容量为1.5倍;同时需要将原有数据复制到新数组中;推荐使用带参的构造器, new ArrayList<>(数组的长度);
jdk 8中:
ArrayList al=new ArrayList();//初始化
第一次调用add方法的时候才创建数组长度为10的数组;list.add(123) //第一次调用add()方法才创建长度为10的数组,并将123的元素添加到数组中;
jdk7中ArrayList的创建相当于单例模式中的饿汉模式;
jdk8中ArrayList的创建相当于单例模式中的懒汉模式;
LinkedList中:LinkedList lk=new LinkedList();
//内部声明Node的first和last属性,默认值为null,就是双向链表的删除和添加操作;前驱结点和后续节点‘
Vector的源码分析:通过Vector()都创建了长度为10的数组,默认扩容为原来长度的2倍,stack是其子类
Set:无序性:不等于随机性,存储的数据并非按照数组索引的顺序添加;而是根据数据的hash值添加; 不可重复:保证添加的元素按照equals()判断时,不能返回true;即相同的元素只能添加一个;
hashSet:作为set接口的主要实现类,线程不安全的,可以存储null值;添加元素的过程:首先调用元素a所在类的hashcode()方法,计算元素a的哈希值,此哈希值按着某种算法计算出在hashset底层数组中的存放位置,即索引位置,判断此位置上是否已经有元素,如果此位置上没有元素,则元素a添加成功;如果此位置上有其他元素b(已经存在以链表形式存在的多个元素),则比较元素a与元素b的hash值,如果hash值不同,则元素a添加成功;如果hash值相同,进而需要调用元素a所在类的equals()方法;equals()返回true,则表明添加失败;,否则返回false,表明添加成功,
这里涉及到hash冲突,因为对于不同的key根据散列函数计算是有可能计算得到相同的哈希数组中的位置的,
hashset的底层实现是hashmap,hashmap在jdk1.7之前使用的数组+链表的方式,在1.8之后就使用的数组+链表+红黑树的方式;因为hashmap是无序的,所以hashset的实现就是无序的,也就是说不会确定当前元素的插入位置;(hashmap)的处理冲突的方式是拉链法;
计算hash的方法常用的有以下几种方式;1.取模法,在hashmap中使用的就是这个方法,只不过是在hashmap中为了计算更加方便高效,每次将数组的长度必须为2的n次方,用hash值与数组长度-1进行与操作,就相当于是取模操作,
在解决hash冲突的时候,通常会遇到的方法:再散列法,拉链法
```java
public void test5(){
HashSet set=new HashSet();
Employee e=new Employee("lxl",18,new My_date(2000,11,27));
Employee e1=new Employee("liuhaoran",20,new My_date(2002,11,03));
set.add(e);
set.add(e1);
System.out.println(set);
e1.setName("xiaohua");
// [Employee{name='xiaohua', age=20, birthday=My_date{year=2002, month=11, day=3}},
// Employee{name='lxl', age=18, birthday=My_date{year=2000, month=11, day=27}}]
set.remove(e1);
System.out.println(set);
//分析这里之所以输出的是两个 的原因是 在执行remove()的时候
// 由于先按照Employee类中重写的hashcode()计算其其在数组中存放的位置
//但按照此值计算得到的hashcode值不同与之前的"liuhaoran"计算得到的hashcode值,因此其在数组中索引的位置不同,
// 因此在"xiaohua"找到的位置处为空,因此此时集合中仍然有两个元素
set.add(new Employee("xiaohua",20,new My_date(2002,11,03)));
System.out.println(set);
//[Employee{name='lxl', age=18, birthday=My_date{year=2000, month=11, day=27}},
// Employee{name='xiaohua', age=20, birthday=My_date{year=2002, month=11, day=3}},
// Employee{name='xiaohua', age=20, birthday=My_date{year=2002, month=11, day=3}}]
set.add(new Employee("liuhaoran",20,new My_date(2002,11,03)));
//此时再添加的时候发现仍然能添加进去,首先计算了其hashcode值,计算得到索引位置,随后
//使用equals()方法进行比较的时候,发现和改过名字的"xiaohua"相比,equals()返回false,因此此时仍然可以将其添加进去
System.out.println(set);
}
对于添加成功的情况2和情况3而言,元素a与已经存在指定索引位置上数据以链表的方式存储;(7上8下)也就是jdk7中是元素a放在数组中,指向原来的元素,jdk8中是原来的元素在数组中,指向新增的元素a;
**linkedHashSet:**是hashset的子类;遍历其内部数组时,可以按照添加的顺序遍历;在添加数据的时候,每个数据还维护了两个引用;记录此数据前一个数据和后一个数据;设计的目的对于频繁的遍历操作优于hashset
**TreeSet**:可以按照添加对象的指定属性进行排序;
**向set中添加的数据,其所在的类一定要重写hashcode()和equals()方法**,在重写hashcode()方法和equals()方法尽可能保持一致性;相等的对象必须具有相同的散列码;
重写两个方法。
**TreeSet**:向他们中添加数据时要确保其是相同类的对象
指定排序是从小到大;
自然排序中,比较两个对象是否相同的标准为:compareto(),不再是equals();
**Map结构的理解:**
map的key是无序的,不可重复的;使用set存储所有的key;-->key所在的类型重写hashcode()方法和equals()方法;(hashmap为例)
map的value是无序的,可重复的,使用colllections存储所有的value;一个键值对构成了Entry对象;Map中的entry是无序的,不可重复的,使用set存储所有的**entry**
**Hashmap的底层实现原理:**
jdk7 Hashmap map=new Hashmap();
实例化之后创建了长度为16的一组entry[] table;
map.put(key,value);其执行过程是首先计算key1所在类的hashcode(),计算key1哈希值,经过某种算法得到在entry数组的位置,如果此位置上为空,则此时entry添加成功;如果此位置上的数据不为空;此位置上存在一个或多个数据(链表),比较当前key和已经存在的一个或多个数据的哈希值;
如果key1的哈希值和已经存在的数据(key-value)的hash值不同,则添加成功;
如果....相同,继续比较,调用key1所在的类的equals(0,比较:如果equals()返回false,则添加成功;
如果equals()返回true,使用value1替换value2;
在不断扩容的过程中(大于threshhold临界值并且要存放的位置非空时)扩容为原来的2倍;
**jdk8:**底层的数组是Node[],首次调用put()方法时,底层创建长度为16的数组;
当数组的某一个位置上的元素以链表形式存在的数据个数大于8,且当前数组的长度>64;此时此位置上的所有数据改为使用**红黑树**存储;
加载因子:0.75;临界值:16*0.75=12
linkedHashmap:能够记录添加元素的先后顺序;
hashset的底层其实是hashmap;
treemap:添加key_value;
**hashtable**:是线程安全的,效率低
---- properities常用来处理配置文件,key,value都是string类型;
**collections**工具类:操作collections,map
collection和collections的区别:
collection是接口,collections是操作这些集合的工具类
reverse()反正list中元素,shuffle(),sort()方法;swap(int index,int index);
List dest=Arrays.asList(new Object(list.size()));
Collections.copy(dest list,source);
泛型方法:在方法中出现了泛型的结构,泛型方法所属的类是不是泛型类都没有关系public <E> List<E> test(E[] arr){}
也就是类的泛型和泛型方法的泛型没有关系;静态方法中不能使用泛型;泛型方法可以声明为泛型;
**DAO**(database access object)
public class DAO<T>{}
**泛型在继承方面的体现**:类A是类B的父类,G<A>,G<B>二者不具备子父类关系,二者是并列关系;类A是类B的父类,A<G>和B<G> 是父子类关系;
**通配符的使用:** ?
List<?> list=null;
list<Object> list1=null;List<String>list2=null;
list=list1;list=list2;
对于list不能向其内部添加数据;除了添加nul之外;允许读取数据,其类型为Object类型;
?extends A:G<? extends A>可以作为G<A>和G<B>的父类,其中B是A的子类;
LIist<? extends Person> list1=null;
List<? super Person> list2=null;
List<Person> l1=null;
List<Student> l2=null;
List<Object> l3=null;
list1=l1;list1=l2;
list2=l1; list2=l3;
? super A:G<? super A>可以作为G<A>和G<B>的父类,其中B是A的父类;