1.1Collection
集合可以理解为数据结构的封装,根据不同的特性及操作性能进行分类
1.2 继承体系
1.3Collection中常用的方法
public static void main(String[] args) {
// 创建一个集合对象
Collection c1 = new ArrayList();
// 判断是否为空 , 就是个数是否为0
System.out.println(c1.isEmpty());
// 已添加元素的个数
System.out.println(c1.size());
// 添加
// int 类型 先自动装箱为integer类型 然后发生多态
c1.add(1);
c1.add("a");
System.out.println(c1.size());
// 根据元素内容 删除元素
c1.remove(1);
System.out.println(c1);
// 没有提供查询功能和修改功能
// 遍历
// 把集合转换为数组
Object[] arr = c1.toArray();
// 清空集合
c1.clear();
// 判断是否包含某个元素
System.out.println(c1.contains("a"));
}
1.4 迭代器
1.4.1概述
迭代器:是一种设计模式,可以使我们进行数据结构操作的时候,可以无需关心其底层实现
提供了一种通用的遍历模式
获取迭代器对象 : Iterator it = 集合对象.iterator();
有三个方法 :
hasNext 返回布尔型, 判断是否还有元素,如果true 就说明还有元素
next 返回Object类型 , 返回下一个元素(当前指向的元素)
remove 删除指向的元素
迭代器对象一旦创建,那么我们就不能操作集合对象了,尤其是添加和删除操作
如果操作了集合对象,那么需要重新生成迭代器,否则会报错
1.4.2使用
public static void main(String[] args) {
Collection c1 = new ArrayList();
c1.add(1);
c1.add(2);
c1.add("a");
c1.add("v");
c1.add(31);
System.out.println(c1.size());
// 创建迭代器对象
Iterator it = c1.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
// 遍历完之后,如果想要再次遍历,必须重新生成迭代器
// while (it.hasNext()) {
// System.out.println(it.next());
// }
it = c1.iterator();
// 在循环中想要删除集合中的数据,必须使用迭代器的remove
while (it.hasNext()) {
System.out.println(it.next());
// c1.remove("a");
it.remove();
}
System.out.println(c1);
}
1.4.3 forEach
增强for循环,foreach 是迭代器的简写方式
可用于遍历操作,但是不能进行删除,如果要删除,还是要用迭代器
public static void main(String[] args) {
Collection c1 = new ArrayList();
c1.add(1);
c1.add(2);
c1.add(3);
c1.add(4);
// for(数据类型 变量名 : 集合/数组){}
// 会把集合中的每个元素,依次赋值给变量object
for (Object object : c1) {
// c1.remove(1);
System.out.println(object);
}
}
1.5 List
1.5.1 概述
List : 有序 , 可重复
可重复 : 可以保存重复数据,比如可以添加两个"a"
有序 : 添加顺序和取出顺序是一致的
ArrayList : 底层数数组,所以查询修改效率极高
LinkedList : 底层是双向链表,所以随机添加和删除效率极高
Vector : 已经过时,ArrayList是它的升级版,vector属于线程安全,效率较低
1.5.2 ArrayList
ArrayList 底层是 Object[] elementData; Object数组
并且可以自动扩容,默认容量是10,扩大容量是1.5倍,非线程安全,效率极高
如果创建对象后,不添加数据,则容量为0.添加第一个数据的时候,容量为10
public static void main(String[] args) {
// 创建ArrayList
List list = new ArrayList();
// 添加
// 尾部添加
list.add(1);
list.add("b");
// 插入到指定下标中
list.add(0, "a");
// 删除
// 根据内容删除
list.remove("a");
// 如果传入的是int类型,则为根据下标删除
list.remove(1);
// 如果要根据内容删除,但是内容又是整数
// 就需要转换一下,装箱
list.remove(Integer.valueOf(1));
list.add("a");
list.add("b");
list.add("c");
// 查询
System.out.println(list.get(1));
// 修改
// 索引 , 要修改的内容
list.set(1, "bb");
// 遍历
System.out.println(list);
// 迭代器遍历
for (Object object : list) {
System.out.println(object);
}
// for循环
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
1.5.3 LinkedList
LinkedList 底层是一个双向链表
链表中 保存的叫节点 , 一个节点中保存三个数据
1 要添加的数据, 2 上一个节点的对象 , 3 下一个节点的对象
另外注意链表其实是没有下标的,之所以可以这样访问,是因为人家封装了循环函数
使我们可以通过下标的形式访问而已,本质还是循环
public static void main(String[] args) {
// 创建ArrayList
LinkedList list = new LinkedList();
// 尾部添加
list.add(1);
// 头部添加
list.push(2);
// 头部添加
list.addFirst(3);
// 尾部添加
list.addLast(4);
// 尾部添加
list.offer(5);
// 头部添加
list.offerFirst(6);
// 尾部添加
list.offerLast(7);
// 以上方法虽然挺多,有的返回true 有的没有返回值
// 本质 就两个方法 linkLast 和 linkFirst
// ---------- 下面方法和ArrayList一样
// 添加
// 尾部添加
list.add(1);
list.add("b");
// 插入到指定下标中
list.add(0, "a");
// 删除
// 根据内容删除
list.remove("a");
// 如果传入的是int类型,则为根据下标删除
list.remove(1);
// 如果要根据内容删除,但是内容又是整数
// 就需要转换一下,装箱
list.remove(Integer.valueOf(1));
list.add("a");
list.add("b");
list.add("c");
// 查询
System.out.println(list.get(1));
// 修改
// 索引 , 要修改的内容
list.set(1, "bb");
// 遍历
System.out.println(list);
// 迭代器遍历
for (Object object : list) {
System.out.println(object);
}
// for循环
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
1.6 Set
1.6.1 特性
set特性 : 无序 不可重复
不可重复 : 不能添加相同的数据
无序 : 添加和取出顺序 不保证一致
HashSet : 底层是散列表
TreeSet : 底层是红黑树,元素必须按照特定的规则进行排序
数字 : 从小到大 , 日期 : 自然日期(昨天,今天,明天) , 字符串 : 每一位的ASCII码进行排序
1.6.2 TreeSet
public static void main(String[] args) {
TreeSet set = new TreeSet();
// 添加
set.add(1);
set.add(3);
set.add(9);
set.add(2);
// 删除,根据内容删除
set.remove(3);
// 个数
System.out.println(set.size());
System.out.println(set);
// 遍历
for (Object object : set) {
System.out.println(object);
}
}
1.6.3 HashSet
public static void main(String[] args) {
HashSet set = new HashSet();
// 添加
set.add(1);
set.add(3);
set.add(9);
set.add(2);
set.add(22);
set.add(23);
set.add(12);
// 删除,根据内容删除
set.remove(3);
// 个数
System.out.println(set.size());
System.out.println(set);
// 遍历
for (Object object : set) {
System.out.println(object);
}
}
1.7 排序
1.7.1 概述
为什么treeSet可以进行自动排序?
因为要添加的元素,都有比较器,他们都实现了Comparable接口
并且都实现了 public int compareTo(T o); 方法
如何进行排序呢?
自动调用compareTo方法,根据返回值进行排序
0 说明相等,则不添加 , 返回大于0的,说明要添加的元素比里面的元素大,就会放到后面
返回小于0的,说明要添加的元素比里面的元素小,就放到前面
意味着,要想存储其他元素在treeSet中,那么该元素必须要实现Comparable接口并覆写compareTo方法
1.7.2 Comparable
public class TreeSet1 {
public static void main(String[] args) {
TreeSet set = new TreeSet();
set.add(1);
set.add(3);
set.add(2);
// 因为treeSet会比较,所以必须添加相同类型元素
// set.add("a");
System.out.println(set);
set = new TreeSet();
// java.lang.ClassCastException: set.User cannot be cast to java.lang.Comparable
// 因为添加的时候,会调用compareTo方法,所以必须实现comparable接口
set.add(new User(18));
set.add(new User(16));
set.add(new User(11));
set.add(new User(20));
System.out.println(set);
}
}
class User implements Comparable{
int age;
public User(int age){
this.age=age;
}
@Override
public int compareTo(Object o) {
// this.age 是要添加的元素
int age1 = this.age;
// o.age 是集合中的元素
int age2 = 0;
if (o instanceof User) {
User user = (User)o;
age2 = user.age;
}
// 0 说明相等,则不添加
// 返回大于0的,说明要添加的元素比里面的元素大,就会放到后面
// 返回小于0的,说明要添加的元素比里面的元素小,就放到前面
return age2-age1;
}
@Override
public String toString() {
return age+"";
}
}
1.7.3 Comparator
比如我们现在的Integer类型来说,已经实现了Comparable接口并覆写了comparTo方法
而且 是进行升序排序
假如 我们现在的 需求是降序排序,我们可以去修改Integer的源代码吗? 肯定不行
comparator 可以解决 , 优先级大于Comparable
Comparable 是 要添加的数据,实现comparable接口
comparator 是 当排序不满足我们需求的时候,可以 通过comparator进行扩展
如果 要添加的元素,是我们写的,例如 刚才的User ,是我们写的,那么我们应该使用comparable来进 行排序
如果不是不是我们写的类,比如Integer,String等 , 则需要通过 comparator来修改排序规则
public class TreeSet2 {
public static void main(String[] args) {
// 把比较器类传入 TreeSet 即可
// TreeSet set = new TreeSet(new SortInteger());
// 匿名内部类写法
TreeSet set = new TreeSet(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
// o1 是要添加的元素
// o2 是集合中的元素
Integer i1 = (Integer) o1;
Integer i2 = (Integer) o2;
// 0 相等
// 大于0 往后放
// 小于0 往前放
return i2 - i1;
}
});
set.add(1);
set.add(12);
set.add(3);
set.add(5);
set.add(6);
System.out.println(set);
}
}
// 比较器类
class SortInteger implements Comparator {
@Override
public int compare(Object o1, Object o2) {
// o1 是要添加的元素
// o2 是集合中的元素
Integer i1 = (Integer) o1;
Integer i2 = (Integer) o2;
// 0 相等
// 大于0 往后放
// 小于0 往前放
return i2 - i1;
}
}
1.7.4 Collections
public class ListSort1 {
public static void main(String[] args) {
ArrayList li = new ArrayList();
li.add(1);
li.add(12);
li.add(3);
li.add(7);
// API排序,本质 还是调用compartTo方法 , 还是Comparable接口
// 想要使用list的排序,和treeSet是一样的,都需要实现comparable接口
// 或者 就要使用 comparator 比较器
Collections.sort(li);
// 修改排序规则 : 通过comparator
Collections.sort(li, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
Integer i1 = (Integer) o1;
Integer i2 = (Integer) o2;
return i2-i1;
}
});
System.out.println(li);
}
}
1.8 散列表
1.8.1 概述
散列表 : 又叫hash表 , 谐音 哈希表, 数组中保存链表
hash算法 : 是一种安全的加密算法,把不定长数据变成定长数据,不能保证其唯一性
直接寻址法,数字分析法,平方取中法,折叠法,随机数法,除留余数法
hash冲突 : 对同一个对象hash多次,值一定是相同的, 但是对多个对象进行hash,可能生成相同的值
添加实现原理 :
1 添加是K-V映射关系 , 比如 姓名 : 张三 , 此时姓名可以是固定的属性,而张三是可变的值
所以我们一般是通过属性去获取值的,也就意味着通过key获取value
2 用K调用hashCode()生成hash值,然后对这个值进行hash算法hash,得到对应的下标
3 判断下标对应的数组中是否有数据,如果没有,就保存这个映射关系(K-V)即可
4 如果有数据,那么就调用K的equals方法,和数组中对应下标的所有数据进行比较
5 如果equals进行判断时,有相同的,就不添加此映射关系,新的value值替换原来的value值
6 如果没有相同的,就把该映射关系,添加到数组对应的链表中的尾部
意味着,想要使用散列表,我们需要根据需求对hashCode方法和equals方法进行重写
包装类Integer等 还有String 都覆写了hashCode和equals方法了
hashMap中 数组长度默认为16 , 默认加载因子是0.75(16*0.75=12) 就是当达到12个的时候,就开始扩容
1.9 Map
1.9.1 HashMap
map key不可重复, value可重复,保存键值对映射关系
public class HashMap1 {
public static void main(String[] args) {
HashMap map = new HashMap();
// 尾部添加
map.put(new Test(18, "张三"), 21);
// 存在后,key不添加,value值替换
map.put(new Test(20, "张三"), 56);
map.put(new Test(20, "张三2"), 56);
map.put("a", 56);
map.put("b", 56);
// 根据key 删除映射关系
map.remove(new Test(20, "张三2"));
// 根据key 查询value
System.out.println(map.get("a"));
// 修改 : key不能修改,只能修改value
// 和添加一样,如果是已有的key则为修改value值
// 如果是不存在的key则为添加映射关系
map.put("a", 66);
// 个数
System.out.println(map.size());
// 判断是否为空(个数是否为0)
map.isEmpty();
// 是否包含某个key
map.containsKey("");
// 是否包含某个value
map.containsValue("");
System.out.println(map);
// 获取map中所有的value,并保存在集合中返回
Collection values = map.values();
for (Object object : values) {
System.out.println(object);
}
// 获取map中所有的key,并保存在集合中返回
Collection keys = map.keySet();
for (Object object : keys) {
System.out.println(object + " ==> " + map.get(object));
}
// 把map中的key和value封装到entry对象中,并保存在集合中返回
Set entrys = map.entrySet();
for (Object object : entrys) {
Map.Entry entry = (Entry) object;
System.out.println(entry.getKey()+" --> "+entry.getValue());
}
}
}
class Test {
@Override
public String toString() {
return name + ":" + age;
}
int age;
String name;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
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;
Test other = (Test) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
public Test(int age, String name) {
super();
this.age = age;
this.name = name;
}
}
1.9.2 TreeMap
public class Map_01 {
public static void main(String[] args) {
TreeMap map = new TreeMap();
// 使用TreeMap时,添加的key必须是可以排序的
// 要么添加的key实现了comparable接口
// 要么创建TreeMap的时候,传入comparator比较器
map.put(2, 4);
// 必须保存的是同类型的数据,因为不同类型没有可比性
// map.put("a",4);
map.put(2, 1);
map.put(12, 21);
map.put(3, 6);
System.out.println(map);
// String 是按照每位的ASCII码进行比较
map = new TreeMap();
map.put("a", 1);
map.put("t", 1);
map.put("f", 1);
map.put("b", 1);
// 注意 {1=1, 15=1, 2=1, 4=1, 6=1, 9=1}
// 2 > 1999999 因为先比较第一位ASCII码,相同再比较第二位
// 但是在比较第一位的时候 2 > 1 所以 2 > 19999999
map = new TreeMap();
map.put("1", 1);
map.put("2", 1);
map.put("9", 1);
map.put("4", 1);
map.put("15", 1);
map.put("6", 1);
// 面试题
// 以下程序默认按照ASCII码排序,需求:使他们按照数值大小排序 1 2 4 6 9 15
map = new TreeMap(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
// o1 是要添加的元素
// o2 是集合中的元素
String s1 = (String) o1;
String s2 = (String) o2;
// 转换为数值型
int i1 = Integer.parseInt(s1);
int i2 = Integer.parseInt(s2);
return i1 - i2;
}
});
map.put("1", 1);
map.put("2", 1);
map.put("9", 1);
map.put("4", 1);
map.put("15", 1);
map.put("6", 1);
System.out.println(map);
}
}
1.10 泛型
1.10.1 概述
泛型 : 在编译过程中进行类型检查,检查类型是否匹配
如果类型不一致,则无法添加
某种意义上来讲,泛型统一了集合中元素的类型
如果不指定泛型,默认为Object类型,什么都能放,因为什么类型都可以转型为Object(多态)
注意 : 泛型只能写引用类型,不能写基本类型,如果需要保存基本类型,需要些对应的包装类
int --> Integer
优点 : 统一类型,减少强制类型转换,使用更方便
缺点 : 只能存储单一类型
1.10.2 使用
public static void main(String[] args) {
ArrayList arr1 = new ArrayList();
arr1.add(1);
arr1.add("aa");
arr1.add(new Object());
// 遍历时,只能写Object
for (Object object : arr1) {
}
ArrayList<String> arr2 = new ArrayList<String>();
arr2.add("a");
// 无法添加,只能添加String
// arr2.add(1);
// 指定泛型后,遍历时可以写泛型类型,操作更方便,不需要类型转换
for (String string : arr2) {
}
}
1.10.3 自定义泛型
? : 表示不确定的类型
T : type 说明是一个java类型
E : element 元素,一般集合中保存的都是元素,所以一般使用E
K : key 键,一般在map散列中
V : value 值,一般在map散列中
N : number 数字
一般使用的是大写字母,愿意写什么就写什么,还是要注意望文知义
如果不传递泛型值,默认为Object
<T> 就等于 <T extends Object> 可以传入一个任意Object的子类类型,默认是Object
<T extends Number> 可以传入一个任意的Number的子类类型,不传递默认是Number类型
public class Generic {
public static void main(String[] args) {
// 没有指定泛型
MyClass myClass = new MyClass();
myClass.m1(1);
myClass.m1("ss");
myClass.m1(1.5);
MyClass<Integer> myClass2 = new MyClass<Integer>();
myClass2.m1(2);
// myClass2.m1(3.2);
// myClass2.m1("xx");
MyClass<Integer> myClass3 = new MyClass();
// myClass3.m1("");
}
}
class MyClass<Z> {
public void m1(Z t) {
System.out.println(t);
}
}
1.10.4 练习
/**
* Map转List并以value值进行排序
*
* map没有办法按照value排序,因为treeMap中的排序规则是按照key排序
*
* 我们没办法修改代码,所以只能按照key排序,所以要想按照value排序,必须转换为list
*
*/
public class Test {
public static void main(String[] args) {
Map<String, Integer> map = new TreeMap<String, Integer>();
map.put("a1", 1);
map.put("a2", 2);
map.put("a3", 7);
map.put("a4", 5);
map.put("a5", 3);
// System.out.println(map);
// 因为map中保存键值对映射关系
// 而list中保存的是单个数据,所以没有办法直接保存KV
// 因此需要将map中的数据转换为entry对象,然后保存在list中即可
Set set = map.entrySet();
// 把set保存到list中
List<Entry<String, Integer>> list = new ArrayList<Entry<String, Integer>>(
set);
// 排序
Collections.sort(list, new Comparator<Entry<String, Integer>>() {
@Override
public int compare(Entry<String, Integer> o1,
Entry<String, Integer> o2) {
return o1.getValue() - o2.getValue();
}
});
System.out.println(list);
}
}