集合框架
LinkedList源码
双向链表 : 查询慢,增删快
成员变量
transient Node<E> first; //链表头节点元素
transient Node<E> last; //链表尾节点元素
内部类Node
Node 节点对象
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;
}
}
add方法
void linkLast("abc") {
final Node<E> l = last; //记住最后一个节点
final Node<E> newNode = new Node<>(l, "abc", null);// 上一个,数据,下一个元素
last = newNode; //新建立节点对象,赋值给最后一个节点
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
get(1) 方法
Node<E> node(1) {
if (1 < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < 1; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
Collections工具类
java.util.Collections
集合操作的工具类,提供方法来操作集合,方法全部是静态方法
方法 :
static void shuffle(List list)
集合元素随机排列static void sort(List list)
集合中元素的自然顺序排序static void sort(List list,Comparator c)
比较器的顺序进行排序static 传递什么返回什么 synchronized开头(传递集合)
线程不安全集合,变成安全的集合- Comparator 是比较器接口, 自定义实现类,重写方法
public static void main(String[] args) {
List<Person> list = new ArrayList<Person>();
list.add(new Person("a",10));
list.add(new Person("b",9));
list.add(new Person("b",12));
list.add(new Person("b",11));
list.add(new Person("b",10));
System.out.println(list);
//static void sort(List list,Comparator c) 比较器的顺序进行排序
Collections.sort(list, new Comparator<Person>() {
public int compare(Person p1,Person p2) {
return p1.getAge() - p2.getAge();
}
});
System.out.println(list);
}
}
//接口的实现类,重写方法,泛型<T> 写要比较对象
class MyComparator implements Comparator<Person>{
//方法的参数是两个对象, 参数比较的对象
public int compare(Person p1,Person p2) {
//System.out.println(p1.getAge() - p2.getAge());
return p1.getAge() - p2.getAge();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1nDgpEm9-1595421932871)(images/比较器排序.jpg)]
增强for循环
增强型的for循环,出现在JDK1.5版本.
java.lang.Iterable
接口 实现这个接口允许对象成为 “foreach” 语句的目标。 含数组
谁是接口的实现类 : Collection继承接口Iterable
格式 :
for(数据类型 变量名 : 集合或者数组){
}
好处 : 减少代码量
弊端 : 无索引,不能修改容器中的元素内容
遍历数组
/*
* 增强for循环 遍历数组
*/
public static void array() {
int[] arr = {1,3,5,7,9};
for(int i : arr) {
System.out.println(i+1);
}
System.out.println(arr[0]);
}
遍历集合
/*
* 增强for循环 遍历集合
*/
public static void list() {
List<String> list = new ArrayList<String>();
list.add("how");
list.add("do");
list.add("you");
list.add("do");
for(String s : list) {
System.out.println(s);
}
System.out.println("==========");
List<Person> listPerson = new ArrayList<Person>();
listPerson.add(new Person("a1",101));
listPerson.add(new Person("a2",102));
listPerson.add(new Person("a3",103));
listPerson.add(new Person("a4",104));
for(Person p : listPerson) {
System.out.println(p);
}
}
增强for循环是个假象
属于编译特效 : javac搞事情
for遍历数组 : javac 编译为传统for形式
for遍历集合 : javac 编译为迭代器
泛型技术
JDK1.5出现的新特性(generic), 泛型技术是一种安全机制,保证程序的安全运行
不使用泛型,出现的安全问题
public static void main(String[] args) {
List list = new ArrayList(); // 没有使用泛型
list.add("a") ; //什么类型都可以接收
list.add("cc");
list.add("yretrdas");
list.add("32erg");
list.add("hgdf");
list.add(1);
Iterator it = list.iterator();
while(it.hasNext()) {
Object obj = it.next();
String s = (String)obj;
System.out.println(s.length());
}
}
泛型的写法格式
类名<要存储的数据类型> 变量名 = new 类名<要存储的数据类型>();
注意 : 数据类型,必须是引用类,不能写基本类型 Integer
注意版本 : JDK1.7开始,后面的类型可以省略不写 new 类名<>(); 建议写
作用 : 如何保证安全性 : 强制集合存储指定的数据类型
泛型的好处
- 强制集合存储指定的数据类型, 类型不匹配,编译失败
- 安全问题,由运行时期,提前到编译时期
- 避免了数据类型的强制转换
- 锁定了数据类型, for增强的写法
泛型中的 E, T , K , V含义
E : Element元素 , T : Type类型 , K : Key键, V : Value值
实际的表现 : 是一个未知的数据类型而已,等待程序人员指定类型
例子 : ArrayList
ArrayList<String> al = new ArrayList<String>();
源码 : public class ArrayList<E> 继承父类 实现接口...
public boolean add(E e)
public class ArrayList<String> 继承父类 实现接口...
public boolean add(String e)
泛型类和泛型方法(自定义)
/*
* <Q> 等待传递数据类型
*/
public class Factory<Q> {
private Q qq;
/*
* 静态方法,泛型不能和类上的相同
* 静态不能调用非静态的问题
*/
public static <T> void print(T q) {
System.out.println(q);
}
/*
* 方法中使用的泛型,和类上的不同
* 单独定义这个类型
*/
public <T> void printT(T t) {
System.out.println(t);
}
public void printQ(Q q) {
System.out.println(q);
}
public Q getQq() {
return qq;
}
public void setQq(Q qq) {
this.qq = qq;
}
}
public static void main(String[] args) {
//创建对象Factory
Factory<Integer> fi = new Factory<Integer>();
fi.printQ(123);
Factory<String> fs = new Factory<String>();
fs.printQ("abc");
fi.setQq(1);
fi.getQq();
fi.printT(true);
Factory.print(1.5);
}
泛型接口
-
实现类实现接口,不实现泛型
public interface MyInterface <E>{ public abstract void inter(E e); } //实现类,实现接口,不实现泛型 class MyImpl<E> implements MyInterface<E>{ public void inter(E e) { System.out.println(e); } }
-
实现类实现接口,实现泛型
public interface MyInterface <E>{ public abstract void inter(E e); } //实现类,实现接口,实现泛型 class MyImpl2 implements MyInterface<Integer>{ public void inter(Integer i) { System.out.println(i); } }
泛型通配符
泛型的通配符是 ? 匹配任何数据类型. 泛型使用通配符? ,什么都可以接收
但是遍历取出的时候, 方法next()返回Object类型,不能强制转换
适合于遍历输出
public static void main(String[] args) {
// 定义2个集合,1个存储字符串,1个存储整数
List<String> stringList = new ArrayList<String>();
stringList.add("a");
stringList.add("b");
List<Integer> integerList = new ArrayList<Integer>();
integerList.add(1);
integerList.add(2);
iterator(stringList);
iterator(integerList);
}
// 一个方法,同时迭代器2个集合
public static void iterator(List<?> list) {
//迭代器的泛型,和集合泛型一致
Iterator<?> it = list.iterator();
while(it.hasNext()) {
//迭代器取出元素: 方法 next() 返回值是什么类型
Object obj = it.next();
System.out.println(obj);
}
}
泛型限定
需求 : 面向对象课程中,曾经完成过一个例子 (抽象类 abstract class) 公司类 : 开发部,财务部
定义2个集合, 分别存储开发部对象和财务部对象.
使用1个方法,同时遍历2个集合, 遍历的同时可以调用对象的方法 work()
-
? extends E 泛型可以是E类型,或者是E子类, 泛型的上限限定
-
? super E 泛型可以是E类型,或者是E父类, 泛型的下限限定
public static void main(String[] args) { // 定义2个集合, 分别存储开发部对象和财务部对象. List<Development> devList = new ArrayList<Development>(); List<Finance> finList = new ArrayList<Finance>(); //创建开发部对象 Development d1 = new Development(); d1.setName("张三"); d1.setId("开发部001"); Development d2 = new Development(); d2.setName("李四"); d2.setId("开发部002"); //创建财务部对象 Finance f1 = new Finance(); f1.setName("翠花"); f1.setId("财务部001"); Finance f2 = new Finance(); f2.setName("翠花2"); f2.setId("财务部002"); //对象存储到集合 devList.add(d1); devList.add(d2); finList.add(f1); finList.add(f2); iterator(devList); iterator(finList); } /* * 使用1个方法,同时遍历2个集合, 遍历的同时可以调用对象的方法 work() * ? 表示通配符,可以接收任意类型, 但是 ? 接收的类型必须是 Company的子类 */ public static void iterator(List<? extends Company> list) { Iterator<? extends Company> it = list.iterator(); while(it.hasNext()) { //next() 方法取出就不会是Object, 是确定的类型Company Company c = it.next(); c.work(); } }
数据结构红黑树 (Red - Black Tree)
平衡二叉树
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lttL1FCD-1595421932876)(images/平衡二叉树.jpg)]
红黑树
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yAvNxjtA-1595421932878)(images/红黑树.png)]
Set集合
java.util.Set
接口, 继承父接口Collection
Set集合特点:
- 不包含重复元素
- 没有索引
Set接口方法
Set接口的方法和父接口Collection方法,完全一样
public static void main(String[] args) {
//Set接口和实现类HashSet创建对象
Set<String> set = new HashSet<String>();
set.add("abc");
set.add("how");
set.add("are");
set.add("you");
//迭代器遍历
Iterator<String> it = set.iterator();
while(it.hasNext()) {
String str = it.next();
System.out.println(str);
}
System.out.println("======");
//增强for循环遍历
for(String str : set) {
System.out.println(str);
}
}
Set接口实现类HashSet
java.util.HashSet
类,实现接口Set
HashSet集合特点:
- 底层数据结构是哈希表
- 是由HashMap支持的
- HashSet集合,所有功能,都是由HashMap提供
- 无序的集合, 元素存储的顺序和取出的顺序不一致
- 线程不安全集合,运行速度快
public static void main(String[] args) {
//Set接口和实现类HashSet创建对象
Set<String> set = new HashSet<String>();
set.add("d");
set.add("c");
set.add("a");
set.add("b");
System.out.println(set);
}
对象的哈希值
Object类是所有类的父类 : Object类里面定义方法
public native int hashCode();
所有类都继承Object, 所有类都拥有此方法.
hashCode() 返回对象的哈希值
直接输出对象 ,调用toString()
看到结果 : 改口, 对象的哈希值 (不要在说地址), 对象的哈希值和内存地址无关!!
对象的哈希值 : 理解为通过某种计算方法得到的一个 , 十进制数字而已
public int hashCode() {
return 9527;
}
子类Person,重写了方法hashCode() 自定义自己的哈希值
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TxbesXO6-1595421932882)(images/哈希值.jpg)]
字符串对象的哈希值
String类继承Object,重写了父类的方法 hashCode()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZX6D5AMA-1595421932886)(images/String对象自定义的哈希值.jpg)]
哈希计算, *31 目的是为了降低,字符串不同,算出相同哈希值的概率
出现字符串不一样,计算出的哈希值是一样的
哈希表
哈希表数据结构 : Node 节点对象
数组 + 链表组合, HashSet集合,是单向链表,不记录上一个元素是谁
class Node{
E item;
Node next;
}
哈希表 : private Node[] node ;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zf1zkiL8-1595421932887)(images/哈希表的结构.jpg)]
哈希表存储对象的过程
public static void main(String[] args) {
Set<String> set = new HashSet<String>();
set.add("abc");
set.add("bbc");
set.add(new String("abc"));
set.add("重地");
set.add("通话");
System.out.println(set);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IcXDplYO-1595421932888)(images/哈希表存储对象的过程.jpg)]
哈希表存储自定义的对象
去掉重复的元素,被存储到哈希表的对象,应该重写hashCode()和equals()方法
public static void main(String[] args) {
//HashSet集合,存储自定义对象
Set<Person> set = new HashSet<Person>();
set.add(new Person("a",11));
set.add(new Person("b",12));
set.add(new Person("c",13));
set.add(new Person("c",13));
set.add(new Person("d",14));
System.out.println(set);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
关于hashCode()和equals()
两个对象的哈希值相等,equals()方法永远返回一个值
两个对象哈希值不等, 但是equals方法返回true
Sun : hashCode协定
- 一个对象,多次调用hashCode(),结果相同
- x对象.equals(y对象)=true, x对象的哈希值和y对象的哈希值,必须一致
HashMap源码
-
初始化哈希表的容量的计算方法
static final int tableSizeFor(int cap) { int n = cap - 1; n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16; return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; } 得到我们指定容量,大于他的 8421码