目录
1、为什么使用集合
之前学习过的数组存在定容的缺陷,一旦数组定义好,数组长度就无法改变,如果需要改变数组长度,代码将会变得很复杂。但是使用集合就能提高数据的灵活性,同时java官网基于数组根据不同的数据结构,创建了多个类,而这些类统称为集合框架,还有就是java集合可以用来存储不同类型不同数量的对象,因此之后再说的集合框架时就表示多个类。
2、集合框架
3、List集合
此次主要讲解的是ArrayList和LinkedList,首先看一下List集合下的三者区别:
3.1、ArrayList
(1)创建集合对象
List list=new ArrayList();//若内部没有数字就默认为10,若填写则为指定长度
(2)增、删
(3)改、查
3.2、LinkedList
方法使用和ArrayList大致相同,由于LinkedList是链表实现的,所以额外提供了在头部和尾部添加/删除元素的方法,也没有ArrayList扩容的问题。创建集合元素的方式变为:
LinkedList link=new LinkedList();//内部不能自定义长度
额外功能:
3.3、LinkedList底层源码
ArrayList底层是数组结构,查询速度快,但添加和删除效率低,原因是它要牵涉到数据的迁移。
LinkedList底层是双向链表结构,添加和删除效率高,但查询效率低,原因是没有下标帮助只能一个节点一个节点向后进行查找。
下面分别是添加和查询的底层图例介绍:
添加元素时首先进入linkLast语句,一开始last指向null,进入Node节点语句,构建一个Node对象,让last指向Node对象,在将头节点指向Node对象,即指向第一个添加的数据;在添加第二个数据时,last开始指向新的Node节点,因为last一开始不再为空,进行在第一个next对象指向新添加数据的对象。
public boolean add(E e) { //1
linkLast(e);
return true;
}
void linkLast(E e) { //2
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
public class Node<E>{ //3
E item;
Node<E> next;
Node<E> prev;
public Node(E item, Node<E> next, Node<E> prev) {
this.item = item;
this.next = next;
this.prev = prev;
}
}
4、set集合
4.1 、set集合框架
4.2、HashSet集合
(1)、创建HashSet对象
HashSet hash=new HashSet();
根据底层发现有两个有参构造函数,一个无参构造函数,如果没有指定容器的大小,默认为16,负载因子为0.75,负载因子就代表了当空间使用75%时,要求扩容。
(2)、添加元素
hash.add();//添加一个内容
hash.addAll();//添加一个新的HashSet对象,相当于添加多个元素
(3)、删除元素
hash.remove();//只能为内容,不能为下标,因为其无序
hash.clear();//清空容器集合
hash.removeAll();//移除多个
(4)、其他
hash.isEmpty();//判断是否为空
hash.contains();//判断元素是否在容器中
HashSet内部没有修改功能。
(5)、HashSet的遍历方法
1、通过foreach遍历
for(Object o:has){
System.out.printly(o);
}
2、通过迭代器来遍历
hash.iterator();//获取迭代器对象
while(iterator.hashNext()){ //判断指针是否能够移动
Object next=iterator.next(); //指针移动并获取当前元素
System.out.printly(next);
}
4.3、HashSet源码
从构造函数说起,在创建一个HashSet的对象时,底层创建的是HashMap,因此介绍源码直接介绍HashMap底层源码即可。
4.4、TreeSet集合
TreeSet基于TreeMap实现,TreeSet可以实现有序集合,但是有序性需要通过比较器实现。同时TreeSet中的方法和HashSet中的方法一样,只是实现方式不同。其还是基于二叉树结构(左小右大),为一个有序的集合。
例如存储一个String类型,就可以运行成功,原因是内部实现了comparable接口
例如存储一个对象类型,运行会出现错误原因是对象类型没有转成comparable类型
发现:TreeSet中的元素必须实现Comparable接口方可放入TreeSet。
解决方法:
(1)、让类实现Comparable接口
对象类型类名后加上implements Comparable,内部重写compareTo方式,这样会自动排序,返回1表示大于Object o,返回0表示相同元素,返回-1表示当前增加元素比Object o小。
(2)、在创建TreeSet时指定排序的对象
(1) 创建TreeSet对象
TreeSet treeset= new TreeSet();
若括号内没有为其指定排序的规则,那么就要求该集合的元素有排序规则。如果元素的类已经创建完成,不能修改该类的源码(开闭原则),又想把该类的对象放入TreeSet中,这时就需要在创建TreeSet时指定排序的规则。
public class TreeClass {
public static void main(String[] args) {
TreeSet tree=new TreeSet(new myComparable()); //此处引用代码在最后,为了实现类的排序
tree.add(new Student(18,"张三"));
tree.add(new Student(13,"李四"));
tree.add(new Student(19,"王五"));
System.out.println(tree);
}
}
class Student {
private int age;
private String name;
@Override
public String toString() { //一定要进行toString重写,不然不能输出对应形式的语句
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
public Student() {
}
public Student(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class myComparable implements Comparator { //排序方式,必有,不然会报错
@Override
public int compare(Object o1, Object o2) {
Student s1= (Student) o1;
Student s2= (Student) o2;
if(s1.getAge()>s2.getAge()){
return 1;
}else if(s1.getAge()<s2.getAge()){
return -1;
}else{
return 0; //表示相同元素,直接覆盖
}
}
}
5、Map
Map中的每个元素属于键值对模式。如果向Map中添加元素时,需添加key和value。它属于一个接口,该接口常见的实现类有HashMap。
5.1、Map工作方式
(1)如何创建Map
Map map=new Map(); //默认初始化大小为16,负载因子为0.75
Map map=new Map(14); //初始化大小,无负载因子
Map map=new Map(15,0.80f); //默认初始化大小为15,负载因子为0.80
(2)添加操作
map.put(''name'',"张三"); //内部填写key值与value值。要求key值必须唯一,不能重复否则后者会覆盖前者
map.putAll(); //内部填写一个Map类型的值,使其每个元素都添加到map中
map.putIfAbsent("name","李四"); //如果指定的key存在,则不放入map中,如果不存在则放入map中
(3)删除操作
map.remove(""); //根据指定key值删除元素
map.remove("",""); //根据key值和value值删除元素
map.clear(); //清空map容器
(4)修改操作
map.replace("",""); //替换元素
(5)查询操作
map.containskey(""); //判断map是否存在指定的key,返回的是boolean值
map.get(""); //根据指定的key,返回其对应的value值
map.keySet(); //返回map中所有的key,接受时用set类型即Set keys=map.keySet();
(6)遍历map的方式
for(Object k:keys){
Object value=map.get(k);
System.out.println(k+" "+value);
}
5.2、HashMap的底层原理
在JDK1.7与JDK1.8是存在区别的;两者区别为:
JDK1.7使用数据结构:数组+链表 而且链表插入模式为头部插入(有可能造成死循环)。
JDK1.8使用数据结构:数组+链表+红黑树 而且链表的插入模式为尾部插入。
当map.put("","")根据key的hash值计算出一个正数,根据该hash正数对16取余求出在数组(0~15)中的下标位置,如果经过hash计算结果相同,这种相同称为hash碰撞(冲突),比对equals方法,如果equals不同,则挂在链表上(单向链表),若相同,则进行替换。如果hash冲突的个数比较多,则会出现插入和查询效率低的现象,当hash冲突超过8个时转变为红黑二叉树,转变条件计是hash>=8。