集合框架
Collection(接口)
常用方法(也就是后面都有的)
注: 可能返回值有的不同 但大致一样
boolean add(E e); //添加
boolean remove(E e); //删除
void clear(); //清空集合
boolean contains(E e); //判断集合中是否包含某个元素
boolean isEmpty(); //判断集合是否为空
int size(); //获取长度
Object[] toArray(); //将集合转换成数组
Iterator(接口 迭代器)
通用取出集合中元素的方式
- 迭代:即Collection集合元素的通用获取方式。在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续再判断,如果还有就再取出来。一直把集合中的所有元素全部取出。
常用方法
public E next();
返回迭代的下一个元素public boolean hasNext();
如果还有元素可以迭代,则返回true
public class demo {
public static void main(String[] args) {
Collection<String> coll = new ArrayList<>();
coll.add("1");
coll.add("2");
coll.add("3");
coll.add("4");
coll.add("5");
//多态 接口 实现类对象
Iterator<String> it = coll.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
}
}
Collections集合工具类的方法
public static <T> boolean addAll (Collection<T> c,T...elements);
往集合中添加一些元素。
public static void shuffle(List<?> list) ;
打乱顺序:打乱集合顺序。
public static void main(String[] args){
ArrayList<String> list = new ArrayList<>();
//添加多个元素
list.add("a");
list.add("b");
list.add("c");
Collections.addAll(list, "a", "b", "c");//和上面添加一样的效果
Collections.shuffle(list);//打乱list集合
}
public static <T> void sort(List<T> list) ;
将集合中元素按照默认规则(升序)排序。
public static void main(String[] args){
ArrayList<Integer> list01 = new ArrayList<>();
list01.add(1);
list01.add(3);
list01.add(2);
System.out.println(list01);//输出为 [1, 3, 2]
Collections.sort(list01);//输出为 [1, 2, 3]
/*==============================================================*/
//假设有一个 有名字和年龄属性的Person类
ArrayList<Person> list02 = new ArrayList<>();
list02.add(new Person("A", 18));
list02.add(new Person("B", 12));
list02.add(new Person("C", 23));
System.out.println(list02);//输出一般自动重写为 [Person{name="A", age=18}, Person{name="B", age=12}, Person{name="C", age=23}]
// Collections.sort(list02);//直接的话 编译不通过 得先实现接口
// 注意:
// sort(List<T> list )使用前提
// 被排序的集合里边存储的元素,必须实现Comparable,重写接口中的方法compareTo定义排序的规则
// 所以 我们的Person类必须实现Comparable接口 并重写compareTo方法 如下
}
class Person implements Comparable<Person>{
public int compareTo({Person o){
//return 0;//认为元素都是相同的
//自定义比较规则,比较两人的年龄(this,参数Person)
return this.getAge() - o.getAge();//年龄升序 反过来降序
}
}
public static <T> void sort(List<T> list ,Comparator<? super T> ) ;
将集合中元素按照指定规则排序。
public static void mian(String[] args){
ArrayList<Person> list = new ArrayList<>();
list.add(new Person("A", 18));
list.add(new Person("B", 12));
list.add(new Person("aC", 23));
list.add(new Person("bC", 23));
Collections.sort(list, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
//按年龄升序排序
int result = o1.getAge() - o2.getAge();
//如果年龄一样 按名字排序
if (result == 0){
result = o1.getName().charAt(0) - o2.getName().charAt(0);
}
return result;
}
});
System.out.println(list);//[0, 1, 2, 3]
}
大致小结
public class demo05工具类 {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
//1. public static <T> boolean addAll (Collection<T> c,T...elements);往集合中添加一些元素。
Collections.addAll(list,1,3,2,0);
System.out.println(list);//[1, 3, 2, 0]
//2. public static void shuffle(List<?> list);打乱顺序:打乱集合顺序。
Collections.shuffle(list);
System.out.println(list);//乱排序
//3. public static <T> void sort(List<T> list); 将集合中元素按照默认规则(升序)排序。
Collections.sort(list);
System.out.println(list);//[0, 1, 2, 3]
Collections.sort(list, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;//升序 后减前 o2-o1降序 自定义的类也一样 o1.getAge() - o2.getAge()
}
});
System.out.println(list);//[0, 1, 2, 3]
}
}
List(接口 有序可重复)
- 有序的集合(存储和取出元素顺序相同)
- 允许存储重复的元素
- 有索引,可以使用普通的for循环遍历
ArrayList(常用 底层是数组实现的,查询快、增删慢
)
普通数组的长度不可以改变
ArrayList是大小可变的数组的实现
//<这里是泛型> 只能是引用类型,不能是基本类型
//也就是装在集合当中的所有元素统一成什么类型
ArrayList<String> list = new ArrayList<>();
System.out.println(list);//注:对于ArrayList集合 直接打印得到的不是地址 而是内容。 如果内容为空就是 []
//创建了一个ArrayList集合,集合名字为list,里面装的全是String字符串类型的数据
//备注 从JDK 1.7+ 开始 右边的<>里面可以不写,但是<>尖括号得写
常用方法
add 添加
public boolean add(E, e);
向集合中添加元素 参数类型和泛型一致
将指定元素添加到其尾部
list.add("AAA");
System.out.println(list);// [AAA]
list.add("BBB");
list.add("CCC");
System.out.println(list);//[AAA, BBB, CCC]
list.add(100);//出错 这里和上面代码连接起来的 泛型已经指定了是字符串类型
remove 删除
public E remove(int index);
从集合中删除元素,参数是索引编号,返回值就是被删除的元素
System.out.println("被删除的是"+list.remove(2));//被删除的是CCC
get 获取
public E get(int index);
从集合中获取元素,参数是索引编号,返回值就是对应位置的元素
System.out.println(list。get(0));//AAA
size 大小
public int size();
获取集合的长度,返回值是集合中包含的元素个数
System.out.println("现在集合大小为"+list.size());//现在集合大小为2
遍历
for (int i=0; i<list.size(); i++){
Sysouteem.out.println(list.get(i));
}
LinkedList(常用 底层是链表实现的,查询慢、增删快)
addFirst()
- public void addFirst(E e) :将指定元素插入此列表的开头。
- public void addlast(E e) :将指定元素添加到此列表的结尾。
- public void push(E e):将元素推入此列表所表示的堆栈。
public static void show01(){
LinkedList<String> list = new LinkedList<>();
//add添加
list.add("a");
list.add("b");
System.out.println(list);//[a, b]
//addFitst插入开头
list.addFirst("ww");//list.push("ww");效果一样
System.out.println(list);//[ww, a, b]
//addLast插入结尾 与add差不多
list.addLast("c");
System.out.println(list);//[ww, a, b, c]
}
removeFirst()
- public E removeFirst() :移除并返回此列表的第一个元素 。
- public E removeLast():移除并返回此列表的最后一个元素。
- public E pop():从此列表所表示的堆栈处弹出一个元素。此方法相当于removeFirst
public static void show01(){//接着上面的[ww, a, b, c]
。。。
System.out.println( list.removeFirst() );//ww
//这个时候第一个ww就被删除了
System.out.println( list.removeLast() );//c
//这个时候最后一个c被删除
System.out.println(list);//[a, b]
}
getFirst()
public static void show01(){//接着上面的[ww, a, b, c]
。。。
//假设集合中没有元素 会抛出异常 所以最好添加isEmpty 是否为空 方法
if( !list.isEmpty() ){//是否为空取反 是否不为空
System.out.println( list.getFirst() );// ww
System.out.println( list.getLast() );// c
}
}
Vector(了解 1.0)
底层也是数组 和ArrayList一样可以增长
但是他从1.2之后被取代了 因为他是单线程
Set(接口 存取无序不可重复)
- 不允许存储重复元素
- 没有索引(不能使用普通的for循环遍历)
HashSet(常用)
底层是哈希表+ (红黑树)实现的,无索引、不可以存储重复元素、存取无序
什么是哈希值?
哈希值:是一个十进制的整数,由系统随机给出
(就是对象的地址值,是一个逻辑地址, 是模拟出来得到地址,不是数据实际存储的物理地址)
在Object类中有一个方法int hashCode();
,可以获取到对象的哈希值
public class Demo{
public static void main(String[] args){
//假如写了一个Person类
Person p1 = new Person();
int h1 = p1.hashCode();
System.out.println(h1);//13672 | 1
//重写Person类中的hashCode方法 改为返回值1 就都是一了
Person p2 = new Person();
int h2 = p2.hashCode();
System.out.println(h2);//78937458 | 1
/*
toString方法的源码:
return getClass(). getName() + "@" + Integer. toHexString(hashCode());
*/
System.out.println(p1);// ...@(p1.hashCode())的十六进制
System.out.println(p2);// ...@(p2.hashCode())的十六进制
//即使重写之后他们都是1 但
System.out.println(p1 == p2);//false
//String类的哈希值 他也重写了hashCode方法
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1.hashCode());//96354
System.out.println(s2.hashCode());//96354
//特殊 巧合
System.out.println("重地".hashCode());//117939
System.out.println("通话".hashCode());//117939
}
}
什么是哈希表?
在JDK1.8之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。
但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。
而JDK1.8中,哈希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。
Set集合不允许重复的原理
注:必须重写hashCode和equals方法
public static main(String[] args){
HashSet<String> set = new HashSet<>();
String s1 = new String("abc");
String s2 = new String("abc");
set.add(s1);
set.add(s2);
set.add("重地");
set.add("通话");
set.add("abc");
System.out.println(set);//[重地, 通话, abc]
}
Set集合在调用add方法的时候 会调用hashCode方法和equals方法 来判断是否重复
add(s1);先调用hashCode方法计算字符串"abc"的哈希值(假设96354)发现集合中没有这个哈希值的元素 就将s1存储到集合中
接着add(s2);同样得到了哈希值是96354 接着在调用s2的equals与哈希值相同的s1比较 (s2.equals(s1) 返回true) 两个元素的哈希值相同 equals方法返回true 就不存储
接着add(“重地”);如add(s1);
然后add(“通话”); 通话与重地的哈希值一样 然后 “通话”.equals(“重地”) 返回false 还是存储进去 挂到重地下面
自定义的类型存储(没有重写hashCode和equals方法)
public static void main(String[] args) {
HashSet<Person> set = new HashSet<>();
Person p1 = new Person("A",18);
Person p2 = new Person("A",18);
Person p3 = new Person("A",19);
System.out.println(p1.hashCode());//284720968
System.out.println(p2.hashCode());//189568618
System.out.println(p1 == p2);//false
System.out.println(p1.equals(p2));//false
set.add(p1);
set.add(p2);
set.add(p3);
System.out.println(set);//[Person{name='A', age=18}, Person{name='A', age=19}, Person{name='A', age=18}]
}
这样就不能满足 不能存储相同的人(名字年龄一样)所以重写hashCode和equals方法
Alt+ins
System.out.println(p1.hashCode());//2994
System.out.println(p2.hashCode());//2994
System.out.println(p1 == p2);//false
System.out.println(p1.equals(p2));//true
System.out.println(set);//[Person{name='A', age=18}, Person{name='A', age=19}]
LinkedHashSet(有序 存储有序不可重复)
底层是哈希表(数组+链表/红黑树)+链表(多了一条链表(记录元素的存储顺序),保证元素有序)实现的,无索引、不可以存储重复元素
TreeSet(了解)
底层是二叉树实现。一般用于排序
Map
Map<K,V>(接口)
Key 键 Value 值
- Map集合是一个双列集合,一个元素包含两个值(Key Value)
- Key 和 Value的数据类型可以不同也可以相同
- Key不能重复 Value可以重复
- 键和值 —— 映射关系 (一个键对应一个值)再来一个直接被替换
添加
public V put(K key, V value)
把指定的键与指定的值添加到Map集合中。
private static void show01() {
/*
public V put(K key, V value)` 把指定的键与指定的值添加到Map集合中。
存储键值对的时候,key不重复,返回值V是null
存储键值对的时候,key重复,会使用新的value替换map中重复的value,返回被替换的value值
*/
//创建Map 多态
Map<String, String> map = new HashMap<>();
String v1 = map.put("第一个","AAA");
System.out.println(v1);//没有重复的key 所以输出 null
String v2 = map.put("第一个","BBB");
System.out.println(v2);//有重复的key 告诉你被覆盖的是AAA 所以输出 AAA
System.out.println(map); //{第一个=BBB}
map.put("第二个","BBB");//value重复没事
System.out.println(map);//{第二个=BBB, 第一个=BBB}
}
删除
public V remove(object key)
把指定的键所对应的键值对元素在Map集合中删除,返回被删除元素的值。
/*
public V remove(object key) 把指定的键所对应的键值对元素在Map集合中删除,返回被删除元素的值。
返回值:V
key存在, v返回被删除的值
key不存在,v返回null
*/
private static void show02() {
//创建Map 多态
Map<String, Integer> map = new HashMap<>();
map.put("长度1",123);
map.put("长度2",456);
map.put("长度3",444);
System.out.println(map);//{长度3=444, 长度1=123, 长度2=456}
Integer v1 = map.remove("长度1");//返回的就是被删除的值 是Integer类型
System.out.println(v1);//123
Integer v2 = map.remove("长度9999");
System.out.println(v2);//null
System.out.println(map);//{长度3=444, 长度2=456}
}
根据K获取对应V
public V get(object key)
根据指定的键,在Map集合中获取对应的值。
private static void show03(){
//创建Map 多态
Map<String, Integer> map = new HashMap<>();
map.put("长度1",123);
map.put("长度2",456);
map.put("长度3",444);
Integer v1 = map.get("长度1");
System.out.println(v1);//123
Integer v2 = map.get("长度9999");
System.out.println(v2);//null
}
根据K/V判断是否存在
boolean containsKey(object key)
判断集合中是否包含指定的键。
/*
boolean containsKey(object key) 判断集合中是否包含指定的键。
boolean containsValue(object value) 判断集合中是否包含指定的键。
包含返回true,不包含返回false
*/
private static void show04(){
//创建Map 多态
Map<String, Integer> map = new HashMap<>();
map.put("长度1",123);
map.put("长度2",456);
map.put("长度3",444);
boolean b1 = map.containsKey("长度1");
boolean b2 = map.containsKey("长度999");
System.out.println("b1:" + b1 + " b2:" + b2);//b1:true b2:false
boolean b11 = map.containsValue(123);
boolean b22 = map.containsValue(213213);
System.out.println("b11:" + b11 + " b22:" + b22);//b11:true b22:false
}
Map遍历
public Set<K> keySet()
获取Map集合中所有的键,存储到Set集合中。
public static void main(String[] args) {
//创建Map 多态
Map<String, Integer> map = new HashMap<>();
map.put("长度1",123);
map.put("长度2",456);
map.put("长度3",444);
//1.使用Map集合中的方法keySet(),把Map集合所有的key取出来,存储到一个Set集合中
Set<String> set = map.keySet();
//2.遍历set集合,获取Mop集合中的每一个key,
for (String key: set) {
//3.通过Map集合中的方法get(key),通过key找到value
System.out.println(key + " " + map.get(key));
}
System.out.println("==============================");
//2.遍历set集合,获取Mop集合中的每一个key,
Iterator<String> it = set.iterator();
while (it.hasNext()){
String key = it.next();
//3.通过Map集合中的方法get(key),通过key找到value
Integer value = map.get(key);
System.out.println(key +" "+ value);
}
}
public Set<Map. Entry<K,V>> entrySet()
获取到Map集合中所有的键值对对象的集合(Set集合)。
/*
Map集合遍历的第二种方式:使用Entry对象遍历
Map集合中的方法:
Set<Map. Entry<K,V>> entrySet() 返回此映射中包含的映射关系的Set视图。
实现步骤:
1.使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中
2.遍历Set集合,获取每一个Entry对象
3.使用Entry对象中的方法getKey( )和getValue( )获取键与值
*/
public class Demo06SetEntryset {
public static void main(String[] args) {
//创建Map 多态
Map<String, Integer> map = new HashMap<>();
map.put("长度1",123);
map.put("长度2",456);
map.put("长度3",444);
//1.使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中
Set<Map.Entry<String, Integer>> set = map.entrySet();
//2.遍历Set集合,获取每一个Entry对象
Iterator<Map.Entry<String, Integer>> it = set.iterator();
while (it.hasNext()){
Map.Entry<String, Integer> entry = it.next();
//3.使用Entry对象中的方法getKey( )和getValue( )获取键与值
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println(key + " " + value);
}
System.out.println("=============================");
for(Map.Entry<String, Integer> entry : set){
String Key = entry.getKey();
Integer Value = entry.getValue();
System.out.println(Key + " " + Value);
}
}
}
HashMap(重点)
- HashMap集合底层是哈希表,查询特别快
JDK1.8之前:数组+单向链表
JDK1.8之后:数组+单向链表/红黑树(链表的长度超过8) :提高查询的速度
- hashMap集合是一个无序的集合, 存储元素和取出元素的顺序有可能不一致
详解
应用(使用HashMap存储自定义类型的键值)
/*
HashMap存储自定义类型键值
Map集合保证key是唯一的:
作为key的元素, 必须重写hashCode方法和equals方法,以保证key唯一
*/
public class Demo06自定义类型 {
public static void main(String[] args) {
// HashMap<String,Person> map = new HashMap<>();
// map.put("第一个", new Person("A", 12));//13
// map.put("第二个", new Person("B", 13));//14
// map.put("第三个", new Person("A", 12));//15
// map.put("第一个", new Person("C", 19));//16
//
// System.out.println(map);//{第二个=Person{name='B', age=13}, 第三个=Person{name='A', age=12}, 第一个=Person{name='C', age=19}}
//输出因为16和13的key相同 16覆盖了13 但是相同的value就没事(这个时候还没有重写Person的equals和hashCode方法)
//即使重写了equals和hashCode方法相同的还是可以存进去 因为主要是看key
HashMap<Person, String> map = new HashMap<>();
map.put(new Person("A", 12), "value1");
map.put(new Person("B", 19), "value2");
map.put(new Person("A", 12), "value3");
map.put(new Person("A", 100), "value4");
System.out.println(map);//假设没有重写equals和hashCode方法 就4个都存入输出了 不满足key相同这个问题
//重写之后 value3就覆盖了value1
}
}
LinkedHashMap(extends HashMap)
- LinkedHashMap集合底层是哈希表 + 链表(保证迭代的顺序)
- LinkedHashMap集合是一个有序的集合,存储元素和取出元素的顺序是一致的
/*
java.util.LinkedHashMap<K, V> extends HashMap<K, V>
Map接口的哈希表和链接列表实现,具有可预知的迭代顺序。
底层原理:
哈希表+链表(记录元素的顺序)
*/
public class Demo06Linked {
public static void main(String[] args) {
LinkedHashMap<String, String> linked = new LinkedHashMap<>();
linked.put("1", "a");
linked.put("2", "b");
linked.put("3", "c");
linked.put("1", "d");
System.out.println(linked);//{1=d, 2=b, 3=c} 有序的
}
}
Hashtable
- java.util.Hashtable<K, V>集合 implements Map<K, V>接口
- Hashtable:底层也是一个哈希表,是一个线程安全的集合;是单线程集合,速度慢
- HashMap:底层是一个哈希表,是一个线程不安全的集合,是多线程的集合,速度快
- HashMap集合(以及之前学的所有的集合):可以存储null值, null键
- Hashtable集合,不能存储null值, null键
- Hashtable和vector集合一样,在jdk1.2版本之后被更先进的集合(HashMap,Arraylist )取代了
- Hashtable的子类Propert ies依然再用
- Properties集合是一个唯一和IO流相结合的集合
集合的补充
JDK9的新特性:
List接口, Set接口,Map接口:里边增加了一个静态的方法of,可以给集合一次性添加多个元素
static <E> List<E> of (E... elements)
使用前提:
当集合中存储的元素的个数已经确定了,不在改变时使用
注意:
1.of方法只适用于List接口, Set接口,Map接口,不适用于接接口的实现类
2. of方法的返回值是一个不能改变的集合,集合不能再使用add, put方法添加元素,会抛出异常
3. Set接口和Map接口在调用of方法的时候,不能有重复的元素,否则会抛出异常
快速创建不可变的集合
List<String> list = List.of("a","b","c");
Set<String> set = Set.of("A","B","c");
Map<Integer,Integer> map = Map.of(1,1 , 2,2 );