深拷贝与浅拷贝
- ##### 引用拷贝:
引用拷贝不会在堆上创建一个新的对象,只会在栈上生成一个新的引用地址,最终指向依然是堆上的同一个对象。
- ##### 浅拷贝 :
浅拷贝会在堆上创建一个新的对象,新对象和原对象不等,但是新对象的属性和老对象相同。
其中:
- 如果属性是基本类型(int,double,long,boolean等),拷贝的就是基本类型的值。
- 如果属性是引用类型(除了基本类型都是引用类型),拷贝的就是引⽤数据类型变量的地址值,⽽对于引⽤类型变量指向的堆中的对象不会拷贝。
- ##### 深拷贝 :
完全拷贝⼀个对象,在堆上创建一个新的对象,拷贝被拷贝对象的成员变量的值,同时堆中的对象也会拷贝。
- ##### 区别:
浅拷贝只是简单的复制,对于对象里面的对象属性和数组属性只是复制了地址,并没有创建新的相同对象或者数组。而深拷贝是完完全全的复制一份,空间大小占用一样但是位置不同!!
Set
Set接口基本介绍:
- 无序(添加和取出的顺序不一致),没有索引
- 不允许重复元素,所以最多包含一个null
Set接口的常用方法:
和List接口一样,Set接口也是Collection的子接口,因此,常用方法和Collection接口一样
- add:添加单个元素
- remove:删除指定元素
- contains:查找元素是否存在
- size:获取元素个数
- isEmpty:判断是否为空
- clear:清空
- addAll:添加多个元素
- containsAll:查找多个元素是否都存在
- removeAll:删除多个元素
Set接口的遍历方式:
同Collection的遍历方式一样,因为Set接口是Collection接口的子接口。
- 可以使用迭代器
- 增强for
- 不能使用索引的方式来获取,所以不能用普通的for循环遍历
HashSet:
- HashSet实现了Set接口
-
HashSet实际上是HashMap,源码如下:
public HashSet(){ map = new HashMap<>(); }
- 可以存放null值,但是只能由一个null
-
HashSet不保证元素是有序的,取决于hash后,再确定索引的结果
-
不能有重复元素/对象
例1:
/*
1、执行add方法后,会返回一个boolean值
2、添加成功,返回true,否则返回false
3、如果通过remove指定删除哪个对象
*/
HashSet set = new HashSet();
System.out.print(set.add("join")); //T
System.out.print(set.add("lucy")); //T
System.out.print(set.add("join")); //F
System.out.print(set.add("jack")); //T
System.out.print(set.add("Rose")); //T
set.remove("join");
System.out.print("set=" + set);
/*
输出结果:
T
T
F
T
T
set=[Rose,lucy,jack]
*/
例2:
HashSet set = new HashSet();
set.add("lucy"); //添加成功
set.add("lucy"); //添加失败
set.add(enw Dog("tom")); //添加成功
set.add(enw Dog("tom")); //添加成功
System.out.print("set=" + set);
set.add(new String("hsp")); //添加成功
set.add(new String("hsp")); //添加失败
//String常量池里面的地址一样
class Dog{
……
}
HashSet扩容机制:
- HashSet底层是HashMap
- 添加一个元素时,先得到hash值,会转成->索引值
- 找到存储数据表table,看到这个索引位置是否已经存放的有元素
- 如果没有,直接加入
- 如果有,调用equals比较,
如果相同,就放弃添加,
如果不相同,则添加到最后
- 在java8中,如果一条链表的元素个数到达TREEIFY_THRESHOLD(默认是8),并且table的大小>=MIN_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树)
添加元素底层机制:
- 先获取元素的哈希值(hashCode方法)
- 对哈希值进行运算,得出一个索引值即为要存放在哈希表中的位置号
- 如果该位置上没有其他元素,则直接存放,
如果该位置上已经有其他元素,则需要进行equals判断,
如果相等,则不再添加。如果不相等,则以链表的方式添加
HashSet实践:
/*
定义一个Employee类,包含private成员属性name,age
要求:
创建3个Employee对象放入HashSet中
当name和age的值相同时,认为是同一个员工,不能添加到HashSet中
*/
class Employee{
private String name;
private int age;
//构造器
//get、set方法
//重写toString
/*
如果name和age值相同,则返回相同的hash值
idea快捷键:alt + insert -> 选择equals and hashCode()
*/
@Override
public boolean equals(Object o){
if(this == o)return true;
if(o == null || getClass() != o.getClass())return false;
Employee employee = (Employee) 0;
return age == employee.age &&
Object.equals(name,employee.name);
}
@Override
public int hashSet(){
return Object.hash(name,age);
}
}
public static void main(String[] args) {
HashSet hashSet = new HashSet();
hashSet.add(new Employee("milan",18)); //可以
hashSet.add(new Employee("smith",28)); //可以
hashSet.add(new Employee("milan",18)); //不可以
/*没重写equals和hashSet之前,加入了3个,
因为new了3个不同的对象,hash值不相同,得到的索引位置不同
重写之后,加入了2个
*/
}
LinkedHsahSet:
- LinkedHsahSet是HashSet的子类
- LinkedHsahSet底层是一个LinkedHsahMap,底层维护了一个数组 + 双向链表
- LinkedHsahSet根据元素的hashCode值来决定元素的存储位置,同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存的
- LinkedHsahSet不允许添重复元素
说明:
- 在LinkedHsahSet中维护了一个hash表和双向链表(LinkedHsahSet 有head和tail)
- 每一个节点有pre和next属性,这样可以形成双向链表
- 在添加一个元素时,先求hash值,再求索引,确定该元素在hashtable的位置,然后将添加的元素加入到双向链表(如果已经存在,不添加[原则和hashsset一样])
tail.next = newElement; //简单指定 newElement.pre = tail; tail = newElement;
- 这样的话,我们遍历LinkedHsahSet也能确保插入顺序和遍历顺序一致
练习:
Car类(属性:name,price),如果name 和 price一样,则认为是相同元素,就不能添加
public class Car{
private String name;
private double price;
//构造器
//set,get
//重写toString
}
public static void main(String[] args) {
LinkedHsahSet linkedHsahSet = new LinkedHsahSet();
linkedHsahSet.add(new Car("奥托",1000));
linkedHsahSet.add(new Car("奥迪",300000));
linkedHsahSet.add(new Car("法拉利",10000000));
linkedHsahSet.add(new Car("奥迪",300000));
linkedHsahSet.add(new Car("保时捷",70000000));
linkedHsahSet.add(new Car("奥迪",300000));
//重写之前,全部都是new的新对象,所以都可以加入
//重写equals和hashCode之后不
Map接口和常用方法:
Map接口实现类的特点:
- Map与Collection并列存在,用于保存具有映射关系的数据:Key-Value
- Map中的key和value可以是任何引用类型的数据,会封装到HashMap$Node对象中
- Map中的key不允许重复,原因和HashSet一样(当有相同的key,就等价于替换)
- Map中的value可以重复
- Map中的key可以为null,value也可以为null,注意key为null只能有一个,value为null可以有多个
- 常用String类作为Map的key
- key和value之间存在单向一对一关系,即通过指定的key总能找到对应的vaule
Map接口常用方法:
- put 添加
- remove 根据键删除映射关系
- get 根据键获取值
- size 获取元素个数
- isEmpty 判断个数是否为0
- clear 清除
- containsKey 查找键是否存在
Map六大遍历方式:
- containsKey:查找键是否存在
- keySet:获取所有的键
- entrySet:获取所有关系
-
values:获取所有的值
public static void main(String[] args) {
Map map = new HashMap();
map.put(“邓超”,”孙俪”);
map.put(“王宝强”,”马蓉”);
map.put(“宋喆”,”马蓉”);
map.put(“刘令博”,”null”);
map.put(“null”,”刘亦菲”);
map.put(“鹿晗”,”关晓彤”);//第一组,先取出所有的Key,通过Key取出相应的Value Set keySet = map.KeySet(); //方式一、增强for for (Object Key : keySet) { System.out.print(key + "-" +map.get(Key)); } //方式二:迭代器 Iterator iterator = keyset.Iterator(); while(Iterator.hasNext()){ Object key = iterator.next(); System.out.print(key + "-" +map.get(Key)); } //第二组:把所有的value取出 Collection values = map.values(); //可以使用所有Collection使用的遍历方法 /* 方式一、增强for 方式二、迭代器 */ //第三组:通过EntrySet来获取 K-V Set entrySet = map.entrySet(); //方式一、增强for for (Object entry : entrySet) { //将entry 转成 Map.Entey Map.Entry m = (Map.Entry) entry; System.out.print(m.getKey() + m.getValue()); } //方式二、迭代器
}
Map例题:
使用HashMap添加3个员工对象,要求:键:员工id;值:员工对象
并遍历显示工资>18000的员工
员工类:姓名、工资、员工id
class Emp{
private String name;
private double sal;
private int id;
/*
构造器
set
get
重写toString
*/
}
public static void main(String[] args) {
Map hashMap = new HashMap();
//添加对象
hashMap.put(1,new Emp("jack",300000,1));
hashMap.put(2,new Emp("tom",1000,2));
hashMap.put(3,new Emp("milan",12000,3));
//两种遍历方式
//1、使用keySet -> 增强for
Set keySet = hashMap.keySet();
for (Object key : keySet) {
//先获取value
Emp emp = (Emp) hashMap.get(key);
if(emp.getSal() > 18000){
System.out.print(emp);
}
}
//2、使用EntrySet -> 迭代器
Set entrySet = hashMap.entrySet();
Iterator iterator = entrySet.iterator();
while(iterator.hasNext()){
Map.Entry entry = (Map.Entry)iterator.next();
//通过entry取得key和value
Emp emp = (Emp) entry.getValue();
if(……){
……
}
}
}
HashMap小结:
- Map接口的常用实现类:HashMap、Hashtable和Properties
- HashMap是Map接口使用频率最高的实现类
- HashMap是以key-val对的方式来存储数据
- key不能重复,但是值可以重复,允许使用null键和null值
- 如果添加相同的key,则会覆盖原来的key-val,等同于修改(key不会替换,value会替换)
- 与HashSet一样,不保证映射的顺序,因为底层是以hash表的方式来存储的(jdk8的hashMap底层 数组+链表+红黑树)
- HashMap没有实现同步,因此是线程不安全的,方法没有做同步互斥的操作,没有synchronized
HashMap扩容机制(与HashSet相同):
- HashMap底层维护了Node类型的数组table,默认为null
- 当创建对象时,将加载因子(loadfactor)初始化为0.75,table表大于临界值(table*加载因子),就要开始扩容
- 当添加key-val时,通过key的哈希值得到在table的索引,然后判断该索引处是否有元素,
如果没有元素直接添加,
如果改索引处有元素,继续判断该元素的key是否和准备加入的key相等,
如果相等,则直接替换val;
如果不相等需要判断是树结构还是链表结构,做出相应处理。如果添加时发现容量不够,则需要扩容
- 第1次添加,则需要扩容table容量为16,临界值(threshold)为12(16*0.75)
-
以后再扩容,则需要扩容table容量为原来的2倍,临界值为原来的两倍,即24,依次类推
-
在java8中,如果一条链表的元素超过TREEIFY_THRESHOLD(默认是8),并且table的大小 > MIN_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树)
HashTable的基本介绍:
- 存放的元素是键值对,即:K-Y
- hashtable的键和值都不能为null,否则抛出NullPointerException
- hashTable使用方法基本上和HashMap一样
- hashTable是线程安全的,hashMap是线程不安全的
案例:
Hashtable table = new Hashtable(); //可以
table.put("join",100); //可以
table.put(null,100); //异常
table.put("join",null); //异常
table.put("lucy",100); //可以
table.put("lic",100); //可以
table.put("lic",88); //替换
Hashtable和HashMap对比:
HashMap:
版本:1.2
线程安全(同步):不安全
效率:高
允许null键bull值:可以
Hashtable:
版本:1.0
线程安全(同步):安全
效率:较低
允许null键bull值:不可以
Properties:
- Properties类继承自Hashtable类并且实现了Map接口,也是使用一种键值对的形式来保存数据
- 他的使用特点和Hashtable类似
- Properties还可以用于从xxx.properties文件中,加载数据到Properties类对象,并进行读取和修改
- xxx.properties文件通常作为配置文件,这个知识点在IO流列举
集合选型规则:
- 先判断存储的类型(一组对象或一组键值对)
- 一组对象:Collection接口
允许重复:List
增删多:LinkedList(底层维护了一个双向列表)
改查多:ArrayList(底层维护Object类型的可变数组)
不允许重复:Set
无序:HashSet(底层是HashMap,维护了一个哈希表,即(数组+链表+红黑树))
排序:TreeSet
插入和取出顺序一致:LinkedHashSet,维护数组,双向链表
- 一组键值对:Map
-
键无序:HashMap(底层是:哈希表;jdk7:数组+链表,jak8:数组+链表+红黑树)
-
键排序:TreeMap
-
键插入和取出顺序一致:LinkedHashMap
-
读取文件:Properties
-
Collections工具类:
Collections工具类介绍:
- Collections是一个操作Set、List和Map等集合的工具类
- Collections中提供了一系列静态的方法对集合元素进行排序,查询和修改等操作
排序操作(均为static方法)
- reverse(List):反转List中元素的顺序
- shuffle(List):对List集合元素进行随机排序
- sort(List):根据元素的自然顺序对指定List集合元素按升序排序
- sort(List,Comparator):根据指定的Comparator产生的顺序对List集合元素进行排序
- swqp(List,int,int):将指定list集合中的i处元素和j处元素进行交换
查找、替换:
- Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
- Object max(Collection,Comparator):根据Comparator指定的顺序,返回给定集合中的最大元素
- Object min(Collection)
- Object min(Collection,Comparator)
- int frequency(Collection,Object):返回指定集合中指定元素的出现次数
- void copy(List dest,List src):将src中的内容复制到dest中
- boolean replaceAll(List list,Object oldVAl,Object newVal):使用新值替换List对象的所有旧值