目录
1.1为什么要用集合框架
数组有问题: 定容 (数组初始化的时候 一旦固定容量 从此就只能存储该容量的数据)
如果我们一旦创建数组 发现数组的容量不够用了 此时就需要扩容 就非常麻烦了。
java官方 就 基于数组 根据不同的数据结构 封装出来了很多的 类 这些类统称为 集合框架
所以说 以后再说到 集合框架 就要想到 里面有很多的类
1.2 集合框架的概念
Java集合框架(Java Collections Framework简称JCF)是为表示和操作集合,而规定的一种统一的标准的体系结构。集合框架包含三大块内容:对外的接口、接口的实现和对集合运算的算法。
集合就是用于存储对象的容器。 只要是对象类型就可以存进集合框架中。集合的长度是可变的。 集合中不可以存储基本数据类型的值
2.List集合特点
- List集合是有序集合: 数据的添加和存储次序一致
- List集合可以存储重复的数据
- List集合中的数据可以通过下标访问
2.1ArrayList
2.1.1ArrayList添加数据
public class MyArrayList {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
//添加元素
arrayList.add("java");
arrayList.add(2);
arrayList.add(2.2);
arrayList.add(true);
arrayList.add(new Date());
//指定位置添加
arrayList.add(2,3);
//添加多个元素
ArrayList arrayList1 = new ArrayList();
arrayList1.add("java01");
arrayList1.add("java02");
arrayList.addAll(arrayList1);
System.out.println(arrayList);
2.1.2ArrayList删除数据
//删除指定位置
arrayList.remove(2);
System.out.println(arrayList);
//清除全部
//arrayList.clear();
//system.out.println(arrayList);
2.1.3ArrayList修改数据
//修改指定元素
arrayList.set(0,1);
System.out.println(arrayList);
2.1.4ArrayList查询数据
List list = new ArrayList();
list.add("java1");
list.add("java2");
list.add("java3");
list.add("java4");
//for each遍历
for(Object o : list){
System.out.println(o);
}
//获取指定下标元素
Object o2 = list.get(1);
System.out.println(o2);
//查询数组长度
Object o3 = list.size();
System.out.println(o3);
//获取第一次出现的位置
int indexOf = list.indexOf("java1");
System.out.println(indexOf);
//判断是否有这个元素
boolean o4= list.contains("java5");
System.out.println(o4);
//for循环遍历
for(int i=0;i< list.size();i++){
Object o5 = list.get(i);
System.out.println(o5);
}
2.1.5ArrayList底层原理
从构造方法来入手。new ArrayList(22) 底层声明了一个Object类型的数组 名字elementData
Object[] elementData
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) { //大于0
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) { //等于初始化为一个空数组
this.elementData = EMPTY_ELEMENTDATA;
} else { //抛出一个异常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
==========add("java01")======E理解为Object类型================
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 扩容
elementData[size++] = e; //把元素赋值给数组的相应位置
return true;
}
==========indexOf("java02") 判断元素在集合中第一次的位置=============
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i])) //和数组中的每个元素内容进行比对
return i; //返回元素在集合中位置
}
return -1;
}
===========size() 请求数组的长度======================
public int size() {
return size;
}
============contain("java05")判断元素是否在集合中==============
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
===============get(1) 获取指定位置的元素========
public E get(int index) {
rangeCheck(index); //判断指定的位置是否合法
return elementData(index);
}
E elementData(int index) {
return (E) elementData[index];
}
============toString() 为什么不打印对象的引用地址
[java01, java02, java03, java02]因为重写了Object里面的toString方法。
public String toString() {
Iterator<E> it = iterator();
if (! it.hasNext())
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = it.next();
sb.append(e == this ? "(this Collection)" : e);
if (! it.hasNext())
return sb.append(']').toString();
sb.append(',').append(' ');
}
}
通过对ArrayList方法的底层代码分析:底层就是对数组的操作。
ArrayList的底层就是基于数组实现的。
2.1.6ArrayList的特点
ArrayList的底层原理是数组结构,查询效率高,但是数据的添加、删除效率年低。因为它牵涉到数据的迁移。
2.2LinkedList
2.2.1LinkedList添加数据
public static void main(String[] args) {
LinkedList linkedList = new LinkedList();
//添加元素
linkedList.add("java01");
linkedList.add('a');
linkedList.add(2);
linkedList.add(2.23);
linkedList.add(false);
linkedList.add(new Date());
//指定位置添加元素
linkedList.add(2,"java02");
System.out.println(linkedList);
//添加到头部
linkedList.addFirst("javaFirst");
//添加到尾部
linkedList.addLast("javaLast");
System.out.println(linkedList);
//添加多个元素
LinkedList linkedList1=new LinkedList();
linkedList1.addLast(1);
linkedList1.addLast(2);
linkedList.addAll(linkedList1);
System.out.println(linkedList);
2.2.2LinkedList删除数据
//删除头部
linkedList.removeFirst();
System.out.println(linkedList);
//删除尾部
linkedList.removeLast();
System.out.println(linkedList);
//删除指定位置元素
linkedList.remove(1);
System.out.println(linkedList);
2.2.3LinkedList修改数据
//修改指定位置的元素
linkedList.set(0,1);
System.out.println(linkedList);
2.2.4LinkedList查询数据
public static void main(String[] args) {
LinkedList linkedList = new LinkedList();
linkedList.add("java01");
linkedList.add("java02");
linkedList.add("java03");
//查询数组长度
int i = linkedList.size();
System.out.println(i);
//根据下标获取指定元素
Object o =linkedList.get(1);
System.out.println(o);
//获取第一个元素
Object o1 = linkedList.getFirst();
System.out.println(o1);
//获取最后一个元素
Object o2 = linkedList.getLast();
System.out.println(o2);
//判断是否有这个元素
boolean b = linkedList.contains("java01");
System.out.println(b);
//判断是否为空
boolean b1 = linkedList.isEmpty();
System.out.println(b1);
}
2.2.5 LinkedList底层原理
1.凡是查询源码 ,我们都是从类的构造方法入手:
/**
* Constructs an empty list.
*/
public LinkedList() {
}
该类的构造方法内是空的,没有任何的代码。 但是该类中有三个属性。
transient int size = 0; //索引
transient Node<E> first; //第一个元素对象
transient Node<E> last; //表示最后一个元素对象。
================ add的源码=====E:理解为Object类型==========================。
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
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++;
}
==================Node的源码 内部类=======================================
private static class Node<E> { //<E>泛型--object
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;
}
}
==================== get(1)-----获取元素========================
public E get(int index) {
checkElementIndex(index); //检查index下标是否正确。
return node(index).item; //李四Node对象
}
========================node(index)=============================
Node<E> node(int index) {
//>> 位运算二进制运算 ----- size >> 1 一半的意思size/2
if (index < (size >> 1)) { //前半部分
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else { //后半部分
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
2.2.6LinkedList特点
LinkedList底层原理是双向链表结构,添加和删除效率高
,但是他的查询效率低,因为它要一个节点一个节点的往后找。
3.set集合
3.1.1HashSet添加元素
// HashSet hashSet = new HashSet(16);默认长度
HashSet hashSet = new HashSet();
//添加元素
hashSet.add("java01");
hashSet.add("java02");
hashSet.add("java03");
hashSet.add("java04");
//添加多个元素
HashSet hashSet1 = new HashSet();
hashSet1.add("java05");
hashSet1.add("java06");
hashSet.addAll(hashSet1);
System.out.println(hashSet);
3.1.2HashSet删除
//删除元素
hashSet.remove("java02");
System.out.println(hashSet);
//清空元素
//hashSet.clear();
3.1.3HashSet查询
//查询:for each遍历
for (Object o :hashSet){
System.out.println(o);
}
//迭代器遍历
//获取迭代器对象
Iterator iterator = hashSet.iterator();
//判断指针是否能移动
while(iterator.hasNext()){
//指针移动并获取内容
Object next=iterator.next();
System.out.println(next);
}
3.1.4HashSet底层原理
public HashSet() {
map = new HashMap<>();
}
在创建一个HashSet的对象时,底层创建的是HashMap, 讲HashMap时给大家再说。
3.2TreeSet集合
TreeSet 基于TreeMap 实现。TreeSet可以实现有序集合,但是有序性需要通过比较器实现。
储存String类型
TreeSet treeSet = new TreeSet();
//储存String类型
treeSet.add("java01");
treeSet.add("java02");
System.out.println(treeSet);
储存对象类型(会报错)
public static void main(String[] args) {
TreeSet treeSet = new TreeSet();
//储存对象类型
treeSet.add(new Student("张三",18));
treeSet.add(new Student("李四",14));
treeSet.add(new Student("王五",18));
treeSet.add(new Student("赵六",20));
System.out.println(treeSet);
解决方案:
第一种: 让你的类实现Comparable接口
public class Student implements Comparable{
private String name;
private int age;
public Student(){};
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
//排序规则:return 大于0表示当前元素比o大 return -1表示当前元素比o小
// return 0 相同元素
@Override
public int compareTo(Object o) {
Student student=(Student) o;
if (this.age>student.age){
return 1;
}else if (this.age<student.age){
return -1;
}
return 0;
}
}
第二种解决方案:在创建TreeSet时设置排序规则
1.自己创建一个类,设置排序规则
import java.util.Comparator;
public class MyComparator 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;
}
return 0;
}
}
====================================================
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
====================================================
import java.util.TreeSet;
public class Test {
public static void main(String[] args) {
TreeSet treeSet = new TreeSet(new MyComparator());
treeSet.add(new Student("张三",18));
treeSet.add(new Student("李四",16));
treeSet.add(new Student("王五",16));
treeSet.add(new Student("赵六",20));
System.out.println(treeSet);
}
}
4.Map
map中的每一个值属于键值对模式,如果往map中添加元素需要添加key和value,key是唯一的。
它也是一个接口,该接口的实现类有:HashMap
4.1创建Map对象
Map map = new HashMap();
//默认初始化大小
//Map map1 = new HashMap(16);
//初始化大小 负载因子
//Map map2 = new HashMap( 16,0.78f);
4.2添加
//添加
map.put("k1", "v1");
map.put("k2", "v2");
map.put("k3", "v3");
//添加多个元素
Map map2 = new HashMap();
map2.put("k4","v5");
map2.put("k5","v5");
map.putAll(map2);
System.out.println(map);
//在map中有这个key则不添加,没有就添加进去
map.putIfAbsent("k1","v1");
System.out.println(map);
4.3删除
//删除
map.remove("k4");
System.out.println(map);
4.4修改
//修改
map.replace("k5","v1");//替换元素
System.out.println(map);
4.5查询
//查询
Object o = map.containsKey("k2");//判断map中是否存在指定key
System.out.println(o);
Object o2 = map.get("k3");//根据指定key获取value值
System.out.println(o2);
Set set = map.keySet();//返回map中所有key
System.out.println(set);
//遍历
for (Object k:set){
Object value = map.get(k);
System.out.println(k +""+value);
}
4.6HashMap底层原理
Jdk1.7和jdk1.8的区别:
jdk1.7使用的数据结构:数组+链表 链表的插入模式为头部模式(会造成死循环)
jdk1.8使用的数据结构:数组+链表+红黑二叉树 链表的插入模式为尾部插入
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
//如果key得hash值相同,判断key得equals是否相同,替换原来得元素
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
// 判断链表得长度是否超过8个
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
// 把链表转换为红黑树结构
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
4.7总结
jdk1.8HashMap原理:
储存元素使用put(key,value),根据key的hash计算出hash值,并根据相应的算法计算出在数组中的位置,如果hash值相同则称为哈希冲突,会用equals来判断元素是否一致,不一致则存入单项链表上,如果哈希冲突>=8,则把链表转为红黑二叉树。
Jdk1.7和jdk1.8的区别:
jdk1.7使用的数据结构:数组+链表 链表的插入模式为头部模式(会造成死循环)
jdk1.8使用的数据结构:数组+链表+红黑二叉树 链表的插入模式为尾部插入
5.泛型
5.1什么是泛型
泛型就是限制我们得数据类型
5.2如何使用泛型
public static void main(String[] args) {
//限制了集合中每个元素得类型,只能添加String类型
List<String> list = new ArrayList<>();
list.add("java01");
list.add("java02");
// K:表示键得泛型为String V:表示值得泛型为Integer
HashMap<String,Integer> hashMap = new HashMap<>();
hashMap.put("张三",18);
}
5.3自己定义泛型
public class 类名<标识,标识....> {
标识 变量名;
public 标识 方法名(){
}
public void 方法名(标识 参数名){
}
.........
}
public static void main(String[] args) {
Point<Integer> p = new Point<>();
p.setX(28);
p.setY(10);
p.show();
}
}
class Point<T>{
private T x;
private T y;
public void show(){
System.out.println(x+""+y);
}
public T getX() {
return x;
}
public void setX(T x) {
this.x = x;
}
public T getY() {
return y;
}
public void setY(T y) {
this.y = y;
}
}