很多朋友对于Java编程基础教程第22讲:深入理解Java集合框架和不太懂,今天就由小编来为大家分享,希望可以帮助到大家,下面一起来看看吧!
集合不能直接存储基本数据类型,集合也不能直接存储Java对象。集合中存储的是Java对象的内存地址。 (换句话说,引用存储在集合中。)
注意:集合本身是Java中的容器和对象。集合随时存储“参考”。
集合中保存的是对象的内存地址。png Java中每一个不同的集合,底层都会对应不同的数据结构。将元素存储在不同的集合中相当于将数据放入不同的数据结构中。什么是数据结构?数据存储的结构就是数据结构。不同的数据结构有不同的数据存储方式。
例如:数组、二叉树、链表、哈希表……这些都是常见的数据结构。
当您将数据放入集合c1时,您可以将其放入数组中。
当你将数据放入集合c2时,它可能会被放置在二叉树上。
…………
当您使用不同的集合时,您将使用不同的数据结构。
这里我们需要掌握的不是数据结构的掌握。数据结构已经用Java实现了,这些常用的集合类也已经写好了。您只需要掌握如何使用它们?只需选择合适的套件即可在任何情况下使用。
新的ArrayList();创建一个集合,下面有一个数组。
新的链表();创建一个底部带有链表的集合对象。
新的树集();创建一个以二叉树为底层的集合对象。
.
该集合位于java.util.* 下; java JDK 中的包。
所有集合类和集合接口都在java.util包下。
Java中的集合分为两类:
一种是以单一方式存储元素:
以单一方式存储元素,此类集合的超级父接口:java.util.Collection;
一种是以键值对的形式存储元素
以键值对的形式存储元素。该类型集合中的超级父类接口:java.util.Map;
集合的继承结构
集合继承结构图。 pngList下的几个类。 pngArrayList 集合底层采用数组数据结构。
ArrayList 集合不是线程安全的。
LinkedList集合使用双向链表数据结构。
Vector集合的底层数据结构使用数组。
矢量集合是线程安全的。
Vector的所有方法都用synchronized关键字修饰,因此是线程安全的,但效率较低。现在还有其他方案来保证线程安全,所以Vector用的比较少。
Set.pngSet下的类和接口Set存储元素特点:无序、不可重复。无序是指存储的顺序不一定是取出的顺序。
另外,Set集合中的元素没有下标,Set集合中的元素不能重复。
事实上,当HashSet集合是新的时,dungeon实际上创建了一个新的HashMap集合。 HashSet集合中存储元素实际上是存储在HashMap集合中。
HashMap集合是一种哈希表数据结构。
TreeSet集合的底层实际上是一个TreeMap。当创建一个新的TreeSet集合时,底层实际上创建了一个新的TreeMap集合。
当您将数据放入TreeSet 集合时,实际上是将数据放入TreeMap 集合中。
TreeMap集合的底层使用二叉树数据结构。
补充:SortedSet集合存储元素的特点:
由于它继承了Set集合,所以它的特点也是无序、不可重复,但是放入SortedSet集合中的元素可以自动排序。我们称其为可排序集。放置在此集合中的元素会自动按大小顺序排序。
集合继承结构图.pngMap 集合继承结构图.png 总结:
ArrayList:底层是一个数组。
LinkedList:底层是双向链表。
Vector:底层是数组,线程安全,效率较低,使用较少。
HashSet:底层是HashMap。放置在HashSet集合中的元素相当于放置在HashMap集合的key部分。
TreeSet:最底层是TreeMap。放置在TreeSet集合中的元素相当于放置在TreeMap集合的关键部分中。
HashMap:底层是哈希表。
Hashtable:底层也是哈希表,但是是线程安全的,效率较低,使用较少。
属性:线程安全,key和value只能存储字符串。
TreeMap:底层是二叉树。 TreeMap集合的键可以自动按大小顺序排序。
List集合存储元素的特点:
有序且可重复。
Ordered:存储的顺序与取出的顺序相同,每个元素都有下标。
可重复:存储1个,可以再存储1个。
Set(Map)集合存储元素的特点:
无序且不可重复。
乱序:存储的顺序不一定与取出的顺序相同。另外,Set集合中的元素没有下标。
不可重复:如果存储了1,则不能再次存储1。
SortedSet(SortedMap)集合存储元素的特点:
首先,它是无序且不可重复的,但是SortedSet集合中的元素是可排序的。
乱序:数据存储的顺序不一定与数据取出的顺序相同。另外,Set集合中的元素没有下标。
不可重复:如果存储了1,则不能再次存储1。
可排序:可以按大小顺序排序。
Map集合的键是Set集合。
将数据放入Set集合中,实际上就是将数据放入Map集合的关键部分。
Collection接口
Collection中可以存储哪些元素?
在使用“泛型”之前,Collection可以存储Object的所有子类型。
使用“泛型”后,Collection 中只能存储特定类型。
集合稍后将学习“通用”语法。暂时不用担心。任何东西都可以存储在Collection 中,只要它是Object 的子类型。 (集合中不能直接存储基本数据类型,也不能存储Java对象,只存储Java对象的内存地址。)
Collection中的常用方法
关于java.util.Collection接口中的常用方法。
boolean add(E e) 向集合中添加元素(E是泛型,目前可以视为Object)
//创建集合对象
//多态性
集合c=new ArrayList();
c.add(1200);//自动装箱(java5的新特性)
c.add(3.14);//自动装箱
c.add(new Object());
c.add(true);//自动装箱int size() 获取集合中元素的个数
//获取集合中元素的个数
System.out.println("集合中的元素个数为:" + c.size());//4voidclear() 清除集合
//清空集合
c.clear();
System.out.println("集合中元素个数为:" + c.size());//0boolean contains(Object o) 判断当前集合是否包含元素o,如果包含则返回true,则返回如果没有,则为false。
//将元素添加到集合中
c.add("hello");//将"hello"对象的内存地址放入集合中
c.add("世界");
c.add("绿巨人");
c.add("绿巨人");
添加(1);
//判断集合中是否包含“浩克”
bollean flag=c.contains("浩克");
bollean flag1=c.contains("浩克2");
System.out.println(flag);//true
System.out.println(flag1);//假
System.out.println(c.contains(1));//true
//获取集合中元素的个数
System.out.println("集合中的元素个数为:" + c.size());//5bollean remove(Object o) 删除集合中的一个元素
c. 删除("1");
//获取集合中元素的个数
System.out.println("集合中元素个数为:" + c.size());//4boolean isEmpty()判断集合中元素个数是否为0
//判断集合是否为空(集合中是否有元素)
System.out.println(c.isEmpty());//假
c.clear();//清除
System.out.println(c.isEmpty());//trueObject[] toArray() 调用该方法将集合转换为数组。
c.add("abc");
c.add("def");
添加(100);
c.add("helloworld");
//转换为数组(了解,没怎么用过)
Object[] obj=c.toArray();
for(int i=0; i obj.length; i++){
//遍历数组
对象o=obj[i];
System.out.println(o);
}
关于集合遍历/迭代
下面的遍历方法/迭代一般是所有Collections通用的方法。
不能在Map 集合中使用。用于所有集合和子类。
//创建集合对象
Collection c=new ArrayList();//下面的集合没关系,主要看前面的Collection接口以及如何遍历/迭代。
//添加元素
c.add("abc");
c.add("def");
添加(100);
c.add(new Object());
//遍历/迭代集合Collection
//第一步:获取集合对象的迭代器对象Iterator
迭代器it=c.iterator();
//第2步:通过上面得到的迭代器对象开始迭代/遍历集合。
/*
下面两个方法是迭代器对象Iterator中的方法:
boolean hasNext() 如果仍有元素要迭代则返回true
Object next() 返回迭代的下一个元素
*/
while(it.hasNext){
//不管你原来保存的是什么,取出来的时候永远是Object。
对象obj=it.next();
System.out.println(obj);
}迭代器执行原理.png迭代原理.png深入Collection集合的contains方法:
布尔值包含(对象o)
判断集合中是否包含对象o
如果包含则返回true,如果不包含则返回false
contains方法是用于判断集合是否包含元素的方法。那么它如何判断一个集合是否包含底层元素呢?
调用equals 方法进行比较。
equals方法返回true,表示包含该元素。
存储在集合中的类型必须重写equals 方法。
Collection接口中的remove方法和contains方法都调用equals在底层。
关于删除集合元素:
重要提示:当集合的结构发生变化时,必须重新获取迭代器。如果仍然使用旧的迭代器,则会出现异常:java.ConcurrentModificationException
重要提示:在迭代集合元素的过程中,不能调用集合对象的remove方法来删除元素:c.remove(o);这不能在迭代过程中完成。
异常的根本原因是:集合中的元素被删除,但迭代器没有更新(迭代器不知道集合发生了变化)。
直接通过集合删除元素而不通知迭代器会导致迭代器的快照与原始集合状态不同。
当迭代器被删除时,会自动更新迭代器并更新集合(删除集合中的元素)。
在迭代元素的过程中,一定要使用Iterator的remove方法来删除元素。不要使用集合自带的remove方法来删除元素。
List接口的常用方法
LIst集合存储元素特性:有序且可重复
有序:List集合中的元素都有下标。从0 开始,递增1
可重复:存储一个1,可以存储另一个1。
既然List是Collection接口的子接口,那么List接口一定有自己的“特色”方法:
下面仅列出List接口特有的常用方法:
(现在,将E 视为对象)
void add(int 索引,E 元素)
E get(int 索引)
int indexOf(对象o)
int LastIndexOf(对象o)
E 删除(整数索引)
E集合(int索引,E元素)
这些方法不需要死记硬背。建议自己编写代码来测试和理解。以后开发的时候,还是需要阅读帮助文档。
//创建List类型的集合。
列表myLIst=new ArrayList();
//添加元素
myList.add("A");
myList.add("B");
myList.add("C");
myList.add("C");
myList.add("D");
//在列表中指定位置插入指定元素(第一个参数为下标)
//这个方法用的不多,因为对于ArrayList集合来说效率比较低。
myList.add(1,"KING")
//迭代
迭代器it=myList.itrator();
while(it.hasNext()){
对象elt=it.next();
System.out.println(elt);
}
//根据下标获取元素
对象firstObj=myList.get(0);
System.out.println(firstObj);
//因为有下标,List集合有自己特殊的遍历方法。
//按下标遍历。 [List集合特有的方法,不是Set]
for(int i=0; i myList.size(); i++){
对象obj=myList.get(i);
System.out.println(obj);
}
//获取指定对象第一次出现的索引。
System.out.println(myList.indexOf("KING"));//1
//获取指定对象最后一次出现的索引
System.out.println(myList.lastIndexOf("C"));//4
//删除指定下标位置的元素
myList.remove(0);//删除索引为0的元素
System.out,println(myList.size());//5
System.out.println("==================");
//修改指定位置的元素
myList.set(2,"软");
//遍历集合
for(int i=0; i myList.size(); i++){
对象obj=myList.get(i);
System.out.println(obj);
}计算机英语:
添加、删除、修改、查找都需要知道这些词
增加:添加、保存、新建
删除:删除、删除、除去
变更:更新、设置、修改
检查:查找、获取、查询、选择
ArrayList集合
ArrayList集合初始容量为10。(底层首先创建一个长度为0的数组,当添加第一个元素时,初始容量为10)
ArrayList集合的底层是一个Object类型Object[]的数组。
施工方法:
新的ArrayList();
新的数组列表(20);
//默认初始化容量10
列表myList1=new ArrayList();
//指定初始化容量20
列表myList2=new ArrayList(20); ArrayList集合的扩展:增加到原来容量的1.5倍。
ArrayList底层是一个数组,如何优化呢?
尽可能减少扩展。由于数组扩展效率比较低,因此建议在使用ArrayList集合时预估元素数量并给出一个初始容量。这是ArrayList集合的一个重要的优化策略。
数组的优点:检索效率比较高(每个元素占用相同的空间,内存地址连续,已知第一个元素的内存地址,然后已知下标,计算该元素的内存地址通过数学表达式,所以检索效率最高。)
数组的缺点:随机添加和删除元素效率相对较低。另外,数组无法存储大量数据。 (很难找到非常大的连续内存空间。)
但需要注意的是,在数组末尾添加元素仍然非常高效。
面试官经常问的一个问题:
这么多合集里,你用得最多的是哪一个呢?
答:ArrayList集合。
因为将元素添加到数组末尾,效率不受影响。此外,我们还有许多操作来检索/查找元素。
ArrayList集合有另一个构造函数
//创建HashSet集合
集合c=new HashSet();
//将元素添加到Set集合中
添加(100);
添加(200);
添加(900);
添加(50);
//通过这个构造方法,可以将HashSet集合转换为List集合。
列表myList=new ArrayList(c);
for(int i=0; i myList.size(); i++){
System.out.println(myList.get(i));
}
位运算
右移
左移
右移n 位即除以2 的n 次方。
左移n 位等于乘以2 的n 次方。
LinkedList集合
单向链表数据结构
对于链表数据结构来说:基本单位是节点Node。
对于单向链表:任何节点Node都有两个属性:
第一:存储数据。第二:下一个节点的内存地址。
单向链表.png 链表优点:随机添加和删除元素效率更高。 (因为添加和删除元素并不涉及大量的元素位移)
因为链表上的元素的内存地址在空间存储上是不连续的。因此,在添加和删除元素时不会出现大量的元素位移,因此添加和删除元素的效率较高。在以后的开发中,如果遇到很多涉及到集合中随机添加、删除元素的业务,建议使用LinkedList。
链表的缺点:查询效率低。每次查找元素时,都需要从头节点向下遍历。
被查找元素的内存地址无法通过数学表达式计算出来。每次查找都是从头节点开始遍历,直到找到为止。因此,LinkedList集合检索/搜索效率较低。
ArrayList集合之所以有比较高的检索效率并不是简单的因为下标。这是因为底层数组的作用。
LinkedList集合仍然有下标,但是检索/查找元素时的效率比较低,因为它从头节点开始智能地一一遍历。
ArrayList 集合不是线程安全的。 (不是线程安全的集合。)
双向链表.pngLinkedList 集合没有初始化容量。
最初这个链表中没有元素。第一个和最后一个引用均为空。
无论是LinkedList还是ArrayList,以后写代码的时候都不需要关心它是哪个集合。
因为我们是面向接口编程,所以我们调用的方法都是接口中的方法。
LinkedList集合的底层数据结构是双向链表。
对于链表数据结构来说,随机增删更高效。检索效率低。
链表中的元素存储在空间中,内存地址不连续。
Vector集合
Vector的底层也是一个数组。
默认初始容量为10。
扩容后,容量是原来的两倍。
Vector中的所有方法都是线程同步的,并且带有synchronized关键字,使得它们是线程安全的。效率比较低,使用较少。
如何将线程不安全的ArrayList集合转换为线程安全的集合?
使用集合工具类:java.util.Collections;
java.util.Collection 是集合接口。
java.util.Collections是一个集合工具类。
List myList=new ArrayList();//不是线程安全的。
//成为线程安全的
Collections.synchronizedList(myList);
//此时myList集合是线程安全的。
myList.add("111");
myList.add("222");
myList.add("333");
泛型
JDK5之后的新特性:泛型
泛型的语法机制始终在程序编译阶段起作用,仅供编译器参考。 (运行时泛型是没用的!)
使用泛型有什么好处?
第一:集合中存储的元素类型是统一的。
第二:从集合中取出的元素类型是泛型指定的类型,不需要大量的“向下转型”。
泛型有哪些缺点?
导致集合中存储的元素缺乏多样性!
在大多数企业中,集合中元素的类型仍然是统一的。所以这个通用的功能是大家都认可的。
//创建集合对象
//使用泛型List后,意味着List集合中只允许存储Animal类型的数据。
//使用泛型指定集合中存储的数据类型。
ListmyList=new ArrayList();
//准备对象
猫c=new Cat();
鸟b=new Bird();
myList.add(c);
myList.add(b);
//获取迭代器
//这表明迭代器迭代的是Animal类型。
Iteratorit=myList.iterator();
while(it.hasNext()){
//使用泛型后,每次迭代返回的数据都是Animal类型。
动物a=it.next();
//这里不需要强制类型转换。直接打电话。
a. 移动();
//调用子类特定的方法仍然需要向下转换!
动物a=it.next();
if(猫的一个实例){
猫x=(猫)a;
x.catchMouse();
}
if(Bird 的一个实例){
鸟y=(鸟)a;
y.fiy();
}
}
动物类{
公共无效移动{
System.out.println("动物在动");
}
}
类Cat 扩展了Animal{
公共无效catchMouse(){
System.out.println("猫捉老鼠");
}
}
鸟类扩展动物{
公共无效飞行(){
System.out.println("鸟儿飞翔了");
}
}JDK8之后,引入了自动类型推断机制(也称为菱形表达式)
//ArrayList这里的类型会自动推断,前提是JDK8之后才允许
//自动类型推断,菱形表达式
ListmyList=new ArrayList();我可以定制泛型吗?能
自定义泛型时,尖括号里的是标识符,想怎么写就怎么写。
java源代码中经常出现的有:和
E是单词Element的第一个字母。
T 是单词Type 的第一个字母。
增强for循环(foreach)
语法:
for(元素类型变量名:数组或集合){
System.out.println(变量名);
}
foreach 有一个缺点:没有下标。需要时使用
HashSet集合
存放时的顺序与取出时的顺序不同。
不可重复。
放置在HashSet集合中的元素实际上放置在HashMap集合的关键部分中。
TreeSet集合
TreeSet集合存储元素特点:
无序且不可重复,但是存储的元素可以自动按照大小顺序排序!
称为:可排序集合。
这里的乱是指存储的顺序和取出的顺序不同。并且没有下标。
Map集合
Map 和Collection 没有继承关系。
Map集合以键和值的形式存储数据:键值对
键和值都是引用数据类型。
Key和Value都是存储对象的内存地址。
key起到主导地位,value是key的一个附属品。Map集合的常用方法
V put(K key , V value) 向Map集合中添加键值对 V get(Object key) 通过key获取value void clear() 清空Map集合 boolean containsKey(Object key) 判断Map中是否包含某个key boolean containsValue(Object value) 判断Map中是否包含某个value boolean isEmpty() 判断Map集合中元素个数是否为0 SetkeySet() 获取Map集合中元素个数是否为0 V remove(Object key) 通过key删除键值对 int size() 获取Map集合中键值对的个数 Collectionvalues() 获取Map集合中所有的value,返回一个Collection Set>entrySet() 将Map集合转换成Set集合 import java.util.Collection; import java.util.HashMap; import java.util.Map; public class Text { public static void main(String[] args) { //创建map集合对象 Mapmap = new HashMap<>(); //向map集合中添加键值对 map.put(1,"zhangsan");//这里1进行了自动装箱 map.put(2,"lisi"); map.put(3,"wangwu"); map.put(4,"zhaoliu"); //通过key获取value String value = map.get(2); System.out.println(value); //获取键值对的数量 System.out.println("键值对的数量:" + map.size());//4 //通过key删除key-value map.remove(2); System.out.println("键值对的数量:" + map.size());//3 //判断是否包含某个key //contains方法底层调用的都是equals进行比对的,所以自定义类型需要重写equals方法。 System.out.println(map.containsKey(4));//true //System.out.println(map.containsKey(new Integer(4)));//true //判断是否包含某个value System.out.println(map.containsValue("wangwu"));//true //System.out.println(map.containsValue(new String("wangwu")));//true //获取所有的value Collectionvalues = map.values(); //foreach for (String s : values) { System.out.println(s); } //清空map集合 map.clear(); System.out.println("键值对的数量:" + map.size());//0 //判断是否为空 System.out.println(map.isEmpty());//true } }重要的两个方法。 import java.util.*; /* Map集合的遍历 */ public class Text { public static void main(String[] args) { //第一种方式:获取所有的key,通过遍历key,来遍历value Mapmap = new HashMap<>(); map.put(1,"zhangsan"); map.put(2,"lisi"); map.put(3,"wangwu"); map.put(4,"zhaoliu"); //遍历Map集合 //获取所有的key,所有的key是一个Set集合 Setkeys = map.keySet(); //遍历key,通过key获取value //迭代器可以 /*Iteratorit = keys.iterator(); while (it.hasNext()){ //取出其中一个key Integer key = it.next(); //通过key获取value String value = map.get(key); System.out.println(key + "=" + value); }*/ //foreach也可以 for (Integer key: keys) { System.out.println(key + "=" + map.get(key)); } //第二种方式:Set>entrySet() //以上这个方法是把Map集合直接全部转换成Set集合 //Set集合中元素的类型是:Map.Entry Set>set = map.entrySet(); //遍历Set集合,每一次取出一个Node //迭代器 /* Iterator>it2 = set.iterator(); while (it2.hasNext()){ Map.Entrynode = it2.next(); Integer key = node.getKey(); String value = node.getValue(); System.out.println(key + "=" + value); }*/ //foreach //这种方式效率比较高,因为获取key和value都是直接从node对象中获取的属性值。 //这种方式比较适合于大数据量 for (Map.Entrynode: set) { System.out.println(node.getKey() + "---->" + node.getValue()); } } }哈希表数据结构
HashMap集合底层是哈希表/散列表的数据结构。 哈希表是一个怎样的数据结构呢? 哈希表是一个数组和单向链表的结合体。 数组:在查询方面效率很高,随即增删方面效率很低。 单向链表:在随即增删方面效率很高,在查询方面效率很低。 哈希表将以上的两种数据结构融合在一起,充分发挥它们各自的优点。 HashMap集合底层的源代码: public class HashMap{ //HashMap底层实际上就是一个数组。(一维数组) transient Node[] table //静态的内部类HashMap.Node static class Nodeimplements Map.Entry{ final int hash;//哈希值(哈希值是key的hashCode()方法的执行结果。hash值通过哈希函数/算法,可以转换存储成数组的下标。) final K key;//存储到Map集合中的那个key V value;//存储到Map集合中的那个value Nodenext;//下一个节点的内存地址 } }哈希表/散列表:一维数组,这个数组中每一个元素是一个单向链表。(数组和链表的结合体。) 最主要掌握的是: map.put(k,v) v = map.get(k) 以上这两个方法的实现原理,是必须掌握的。 map.put(k,v)实现原理: 第一步:先将k,v封装到Node对象当中。 第二步:底层会调用k的hashCode()方法得出hash值,通过通过哈希函数/哈希算法,将hash值转换成数组下标,下标位置上如果没有任何元素,就把Node添加到这个位置上了。如果说下标对应的位置上有链表,此时会拿着k和链表上每一个节点中的k进行equals,如果所有的equals方法返回都是false,那么这个新节点将会被添加到链表的末尾。如果其中有一个equals返回了true,那么这个节点的value将会被覆盖。 v = map.get(k)实现原理: 先调用k的hashCode()方法得出哈希值,通过哈希算法转换成数组下标,通过数组下标快速定位到某个位置上,如果这个位置上什么都没有,返回null,如果这个位置上有单向链表,那么拿着参数k和单向链表上的每个节点中的k进行equals,如果所有equals方法返回false,那么get方法返回null,只要其中有一个节点的k和参数k equals的时候返回true,那么此时这个节点的value就是我们要找的value,get方法最终返回这个要找的value。 为什么哈希表的随即增删以及查询效率都很高? 增删是在链表上完成。 查询也不需要都扫描,只需要部分扫描。 HashMap集合的key,会先后调用两个方法,一个方法是hashCode(),一个方法是equals(),那么这两个方法都需要重写。 HashMap集合的key部分特点: 无序,不可重复。 为什么无序?因为不一定挂在哪个单向链表上。 不可重复是怎么保证的?equals方法来保证HashMap集合的key不可重复。 如果key重复了,value会覆盖。 放在HashMap集合key部分的元素其实就是放到HashSet集合中了。所以HashSet集合中的元素也需要同时重写hashCode()+equalis()方法。 注意:同一个单项链表上,所有节点的hash相同,因为他们的数组下标是一样的,但同一个链表上k和k的equals方法肯定返回的是false,都不相等。 哈希表HashMap使用不当时无法发挥性能! 假设将所有的hashCode()方法返回值固定为某个值,那么会导致底层哈希表变成了纯单向链表。这种情况我们称为:散列分布不均匀。 什么是散列分布均匀? 假设有100个元素,10个单向链表,那么每个单向链表上有10个节点,这是最好的,是散列分布均匀的。 假设将所有的hashCode()方法返回值都设定为不一样的值,可以吗,有什么问题? 不行,这样的话导致底层哈希表就成为一维数组了,没有链表的概念了。也是散列分布不均匀。 散列分布均匀需要你重写hashCode()方法时有一定的技巧。 重点:放在HashMap集合key部分的元素,以及放在HashSet集合中的元素,需要同时重写hashCode和equals方法。 HashMap集合的默认初始化容量是16,默认加载因子是0.75。 这个默认加载因子是当HashMap集合底层数组的容量达到75%的时候,数组开始扩容。 重点,记住:HashMap集合初始化容量必须是2的倍数,这也是官方推荐的,这是因为达到散列分布均匀,为了提高HashMap集合的存取效率,所必须的。 向Map集合中存,以及从Map集合中取,都是先调用key的hashCode方法,然后再调用equals方法! equals方法有可能调用,也有可能不调用。 拿put(k,v)举例,什么时候equals不会调用? k.hashCode()方法返回哈希值,哈希值经过哈希算法转换成数组下标。数组下标位置上如果是null,equals不需要执行。 拿get(k)举例,什么时候equals不会调用? k.hashCode()方法返回哈希值,哈希值经过哈希算法转换成数组下标。数组下标位置上如果是null,equals不需要执行。 注意:如果一个类的equals方法重写了,那么hashCode()方法必须重写。并且equals方法返回如果是true,hashCode()方法返回的值必须一样。 equals方法返回true表示两个对象相同,再同一个单向链表上比较。那么对于同一个单向链表上的节点来说,他们的哈希值都是相同的。所以hashCode()方法的返回值也应该相同。 hashCode()方法和equals()方法也不用研究了,直接使用IDEA工具生成,但是这两个方法需要同时生成。 终极结论:放在Map集合key部分的,以及放在HashSet集合中的元素,需要同时重写hashCode方法和equals方法。 在JDK8之后,如果哈希表单向链表中元素超过8个,单向链表这种数据结构会变成红黑树数据结构。当红黑树上的节点数量小于6时,会重新把红黑树变成单向链表数据结构。 这种方式也是为了提高检索效率,二叉树的检索会再次缩小扫描范围。提高效率。初始化容量16,默认加载因子.75。 对于哈希表数据结构来说: 如果o1和o2的hash值相同,一定是放到同一个单向链表上。 当然如果o1和o2的hash值不同,但由于哈希算法执行结束之后转换的数组下标可能相同,此时会发生“哈希碰撞”。 扩容之后的容量时原容量的2倍。Hashtable
HashMap集合key部分允许null吗? 允许。但是要注意:HashMap集合的key null值只能有一个。 Hashtable的key和value都是不能为null的。 HashMap集合的key和value都是可以为null的。 Hashtable方法都带有synchronized:线程安全的。 线程安全有其他的方案,这个Hashtable对线程的处理导致效率较低,使用较少了。 Hashtable和HashMap一样,底层都是哈希表数据结构。 Hashtable的初始化容量是11,默认加载因子是:0.75f Hashtable的扩容是:原容量 * 2 + 1属性类Properties类
目前只需要掌握Properties属性类对象的相关方法即可。 Properties是一个Map集合,继承Hashtable,Properties的key和value都是String类型。 Properties被称为属性类对象。 Properties是线程安全的。 //创建一个Properties对象 Properties pro = new Properties(); //需要掌握Properties的两个方法,一个存,一个取。 pro.setProperty("url","jdbc:mysql://localhost:3306/powernode"); pro.setProperty("driver","com.mysql.jdbc.Driver"); pro.setProperty("username","root“); pro.setProperty("password","123"); //通过key获取value String url = pro.getProperty("url"); String driver = pro.getProperty("driver"); String username = pro.getProperty("username"); String password = pro.getProperty("password"); System.out.println(url); System.out.println(driver); System.out.println(username); System.out.println(password);TreeSet
TreeSet集合底层实际上是一个TreeMap。 TreeMap集合底层是一个二叉树。 放到TreeSet集合中的元素,等同于放到TreeMap集合key部分了。 TreeSet集合中的元素:无序不可重复,但是可以按照元素的大小顺序自动排序。称为:可排序集合。 //创建对象 TreeSetts = new TreeSet<>(); //添加元素 ts.add("zhangsan"); ts.add("lisi"); ts.add("wangwu"); ts.add("zhangsi"); ts.add("wangliu"); //遍历 for(String s : ts ){ //按照字典顺序,升序! System.out.println(s); } TreeSetts2 = new TreeSet<>(); ts2.add(100); ts2.add(200); ts2.add(900); ts2.add(800); ts2.add(600); ts2.add(10); for(Integer elt : ts2){ //升序 System.out.println(elt); }对于自定义类型来说,TreeSet无法排序,因为你没有指定比较规则。没有实现java.long.Comparable接口。 指定规则就需要把Comparable接口的方法实现,编写出比较的逻辑,或者是比较的规则。 compareTo方法的返回值很重要: 返回0表示相同,value会覆盖。 返回>0,会继续在右子树上找。 返回<0,会继续在左子树上找。自平衡二叉树数据结构
TreeSet集合/TreeMap集合是自平衡二叉树,遵循左小右大原则存放。 遍历二叉树的时候有三种方式: 前序遍历:根左右 中序遍历:左根右 后序遍历:左右根 注意:前中后说的是“根”的位置,根在前面是前序,根在中间是中序,根在后面是后序。 TreeSet集合/TreeMap集合采用的是:中序遍历方式。 Iterator迭代器采用的是中序遍历方式。 左根右。 自平衡二叉树.png比较器实现java.util.Comparator接口。(Comparator是java.lang包下的。Comparator是java.util包下的。) 最终结论:放到TreeSet或者TreeMap集合key部分的元素要想做到排序,包括两种方式: 第一种:放在集合中的元素实现java.lang.Comparable接口。 第二种:在构造TreeSet或者TreeMap集合的时候给它传一个比较器对象。 Comparable和Comparator怎么选择? 当比较规则不会发生改变的时候,或者说当比较规则只有1个的时候,建议实现Comparable接口。 如果比较规则有多个,并且需要多个比较规则之间频繁切换,建议使用Comparator接口。 Comparator接口的设计符合OCP原则。Collections工具类
java.util.Collection 集合接口 java.util.Collections 集合工具类,方便集合的操作。 //ArrayList集合不是线程安全的 Listlist = new ArrayList<>(); //变成线程安全的 Collections.synchronizedList(list); //排序 list.add("abf"); list.add("abx"); list.add("abc"); list.add("abe"); Collections.sort(list); for(String s : list){ System.out.println(s); } //注意:对List集合中元素排序,需要保证List集合中的元素实现了:Comparable接口。 //对Set集合怎么排序呢? Setset = new HashSet<>(); set.add("king"); set.add("kingsoft"); set.add("king2"); set.add("king1"); //将Set集合转换成List集合 ListmyList = new ArrayList<>(set); Collections.sort(myList); for(String s : myList){ System.out.println(s); }【Java编程基础教程第22讲:深入理解Java集合框架】相关文章:
2.米颠拜石
3.王羲之临池学书
8.郑板桥轶事十则
用户评论
终于来到集合啦!我很久之前就听过它的名字了。
有18位网友表示赞同!
这个java学习系列越来越有意思了。
有17位网友表示赞同!
集合是编程中很重要的概念呀,一定要好好掌握它。
有11位网友表示赞同!
以前遇到过数据存储的问题,现在看看集合应该就能解决啦!
有17位网友表示赞同!
学习Java已经很久了,终于学到集合了,要好好的学习一下。
有12位网友表示赞同!
集合的应用场景很多吧?可以给我点例子吗?
有10位网友表示赞同!
感觉这部分内容会比较复杂,需要多花些时间去理解哦!
有12位网友表示赞同!
学习Java最关键就是掌握这些核心概念了!
有6位网友表示赞同!
看这个标题好期待啊,终于学到集合了。
有9位网友表示赞同!
集合有什么类型的?想了解一下。
有9位网友表示赞同!
是不是以后用集合就能写出更高级的程序呢?
有5位网友表示赞同!
学习Java真的很需要耐心,一步步来慢慢理解!
有13位网友表示赞同!
这个学习系列真的太棒了,能够让我系统地学Java!
有10位网友表示赞同!
我想了解一下如何使用集合去存储不同类型的对象。
有18位网友表示赞同!
学会了集合之后感觉自己的编程水平都可以上升一个档次啊!
有5位网友表示赞同!
不知道学习集合要用多久呢? 准备开始冲鸭!
有6位网友表示赞同!
集合的应用场景真是太广了,很有意思!
有15位网友表示赞同!
这部分内容应该可以用很多案例来加深印象吧?
有16位网友表示赞同!