一.equals和 == 以及 hashCode的区别与联系
0.概述
• ==:该操作符生成的是一个boolean结果,它计算的是 操作数的值 之间的关系;
• equals:Object的实例方法,比较两个对象的content是否相同;
• hashCode:Object的native方法, 获取对象的哈希值,用于确定该对象在哈希表中的索引位置,实际上是一个int型整数;
1.关系操作符 ==
1)基本数据类型的变量(操作数的值)
在java中有8种基本数据类型:
浮点型: float(32bit) 和 double(64bit)
整型: byte(8bit) short(16bit) int(32bit) long(64bit);
字符型:char(16bit)
布尔型:boolean(没有明确规定,只取字面值'true' 和 'false');
对于这八种基本数据类型的变量,变量直接存储的是值,故使用==来进行比较时,比较的就是值的本身;
需注意:浮点型和整型都是有符号类型(最高位仅用于表示正负,不参与计算,而char是无符号类型,所有位均参与计算);
2)引用类型变量
在java中,引用类型的变量存储的并不是"值"的本身,而是与其关联的对象在内存中的地址
String str=new String("hello");//引用变量str1存储的是它指向对象在内存中的存储地址,类似于指针;
3)小结
因此,对于关系操作符 ==:
*1.若操作数的类型是基本数据类型,则该关系操作符判断的是左右两边操作数的值是否相等;
*2.若操作数的类型是引用数据类型,则该关系操作符判断的是左右两边操作数的内存地址是否相同;//true为同一对象;
/*
* a==b==d!=c
*/
String a = "abc";
String b = "a" + "bc";
String c = new String("abc");
String d = b;
2.equals方法
1>来源
equals方法是基类Object中实例方法,因此对所有继承与Object的类都会有该方法;
public boolean equals(Object obj) {
return (this == obj);
}
2>在Object类中,equals方法是用来比较两个对象的引用是否相等,即是否指向同一个对象。但在String类中重写了equals方法:
public boolean equals(object anObject){ //方法签名与Object类中一致
if(this==anObject) return true; //先判断引用是否相同(是否为同一对象);
if(anObject instanceof String){ 再判断类型是否相同
String anotherString=(String)anObject;
int n=length();
if (n == anotherString.length()) { //最后判断内容是否相同
int i=0;
while(n--!=0){
if(charAt(i)!=antherString.charAt(i))
return false;
i++;
}
return true;
}
}
return false;
}
3>字符串使用equals方法的 三个步骤
1)先 比较引用是否相同(是否为同一对象);
2)在 判断类型是否一致(原理为同一类型);
3)最后比较内容是否一致;
java中所有内置的类的equals方法的实现步骤均是如此,特别诸如 integer,Double等包装类;
4>equals 重写原则
1)对称性: 如果x.equals(y)返回是"true",那么y.equals(x)也应该返回是"true";
2)自反性: x.equals(x)必须返回是"true";
3)类推性: 如果x.equals(y)返回是"true",而且y.equals(z)返回是"true",那么z.equals(x)也应该返回是"true";
4)一致性: 如果x.equals(y)返回是"true",只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是"true";
5)任何情况下:x.equals(和x不同类型的对象)永远返回是"false";
3.hashCode
1>hashCode简述
在 Java 中,由 Object 类定义的 hashCode 方法会针对不同的对象返回不同的整数。
(这是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧)。
其目的是生成一个hash码。hash码的主要用途就是在对对象进行散列的时候作为key输入,(拉链法,详见数据结构导论)
2>equals 与 hashCode
前提:谈到hashCode就不得不说equals方法,二者均是Object类里的方法。所以一切类里都可以重写这两个方法。
原则 1:如果 x.equals(y) 返回 “true”,那么 x 和 y 的 hashCode() 必须相等 ;
原则 2:如果 x.equals(y) 返回 “false”,那么 x 和 y 的 hashCode() 有可能相等,也有可能不等 ;
原则 3:如果 x 和 y 的 hashCode() 不相等,那么 x.equals(y) 一定返回 “false” ;
原则 4:一般来讲,equals 这个方法是给用户调用的,而 hashcode 方法一般用户不会去调用 ;
原则 5:当一个对象类型作为集合对象的元素时,该对象应拥有自己的equals()和hashCode()设计,且要遵守上述原则;
3>小结
1)hashcode是系统用来快速检索对象而使用的,比如在hashMap或hashTable等集合中使用;
2)equals方法本意是用来判断引用的对象是否一致;
3)重写equals方法和hashcode方法时,equals中用到的成员变量也必须在hashcode方法中用到,
后者作为生成摘要的信息项本质上所用到的数据是一样的,从而保证二者的一致性;
二.Java之对象的多态性
0>JAVA面向对象
1>含义:是指任何可重用的软件组件,从单个方法,到包含多个包的复杂系统,都可以是一个模块;
2>模块划分符合结构良好要求 (功能单一, 规模小, 模块之间耦合少)
1)代码应该被重用,而不是被拷贝;
2)模块的功能在逻辑上尽可能 单一化,明确化,最好做到一一对应;(功能单一,明确)
3)模块之间的联系尽可能的少,应当尽量避免逻辑耦合,仅限于 数据耦合;(模块间耦合少)
4)模块的规模应当足够 小; (规模小)
5)错误应该尽早被检测出来,最好是在编译阶段;
2.API(Application Programming Interface)
1>含义:是指类、接口、构造器(constructor)、成员和序列化形式(serialized from),可以通过它们访问类,接口或包;
1>多态概述
1)多态是继封装,继承之后,面向对象的第三大特性;
2)多态现实意义理解
*1.现实事物经常会体现出多种形态,如学生, 学生是人的一种,则具体的同学张三既是学生也是人,即出现两种形态;
*2.Java作为面向对象的语言,同样可描述一个事务的多种形态,如 Student类继承Person类,一个Student的对象
既是Student,又是Person;多态优点:可替代性,可扩充性,灵活,简化;
3)多态体现为 父类引用变量可以指向子类对象,在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法;
4)前提条件:要有继承,要有重写,父类引用指向子类对象;
5)多态的定义格式: 父类类型 变量名=new 子类类型();
2>多态中成员变量的特点
public class Person {
public static final String TAG = "多态";
public String name="Person类";
public void eat(){
Log.d(TAG, "吃饭");
}
}
public class Student extends Person {
public static final String TAG = "多态";
public String name="Student类";
public void eat(){
Log.d(TAG, "吃龙虾");
}
public void study(){
Log.d(TAG, "好好学习");
}
}
public void run() {
Person p=new Student();
1)多态成员变量:编译运行时看左边;
Log.d(TAG, p.name);//p是Person中的值,只能取Person中的值;
2)多态成员方法:编译看左边,运行看右边
p.eat();//p的声明类型是Person但赋值类型为Student类,所以调用赋值类的重写后的方法;
}
3>instanceof关键字
1)作用:用来判断某个对象是否属于某种数据类型,返回类型为布尔类型
2)示例 if(p instanceof Student){Log.d(TAG,"p是Student类型")}
4>多态的转型
0)多态的转型分为 向上转型 和 向下转型 两种;
1)向上转型: 父类类型 变量名=new 子类类型();
适用场景: 当不需要面对子类类型时,通过提高扩展性,或使用父类的功能就能完成相应操作;
2)向下转型:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用类型转换为子类引用类型;
使用格式:子类类型 变量名=(子类类型)父类类型的变量;
适用场景:当要使用子类特有功能时:示例:((Student) p).study();//调用子类特有方法
5>多态的分类
编译时多态(静态):主要是指方法的重载,它是根据参数列表的不同来区分不同的函数,通过编辑之后变成两个不同的函数;
运行时多态(动态):它是通过动态绑定来实现的,即在执行期间判断所引用对象的实际类型,根据其实际类型调用相应方法;
三.String StringBuffer 和 StringBuilder的区别;
1>String类-----String字符串常量
Java中String是immutable(不可变)的,这就导致每次对String的操作都会生成新的String对象,这样不仅效率低下,
而且大量浪费有限的内存空间,例如String str="hello",每给str赋值一次,就会在堆内存中新开辟一个空间存放新值;
然后更新str在堆内存中的地址指向;为了应对经常性的字符串相关的操作.,谷歌引入了两个新类---StringBuffer和
StringBuild类来对此种变化字符串进行处理;
String为什么被设计为不可变
1)安全:不仅体现在应用中,也在JDK中,Java的类装载机制通过传递的参数(通常是类名)加载类,这些类名在类路径下,
想象一下,假设String是可变的,一些人通过自定义类装载机制分分钟黑掉应用;
2)性能:string不可变的设计出于性能考虑,的原理是string pool,不可变的string更好的提高性能。
3)线程安全:当多线程访问时,不可变对象是线程安全的,不需要什么高深的逻辑解释,如果对象不可变,线程也不能改变它。
2>StringBuilder & StringBuffer
当对字符串进行修改时:
需要使用StringBuffer(线程安全)和 StringBuilder类(线程不安全);
和String类不同的是,StringBuffer 和 StringBuilder类的对象能够被多次修改,并不产生新的未使用的对象;
StringBuilder类在Java5中被提出,它和StringBuffer之间最大不同在于StringBuilder的方法不是线程安全的
StringBuilder sbd = new StringBuilder();//不能用于对同一对象的多线程操作,相较于StringBuffer有速度优势;
StringBuffer sbf = new StringBuffer();//大多情况下使用StringBuilder,但若要求线程安全必须使用StringBuffer;
public void test(View view) {
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 100; j++) {
sbd.append("1");
sbf.append("1");
//打印结果 :9911-10000 故 StringBuffer线程安全
Log.d(TAG, sbd.length() + "-" + sbf.length());
}
}}).start();
}
}
3>三者的区别
String:不可变字符串,每次对String赋值都会生成新对象 故少用String
StringBuffer:可变字符串, 效率低, 线程安全, 多线程用StringBuffer
StringBuilder:可变字符序列、效率高,线程不安全,单线程推荐使用StringBuilder,主要方法 append()和insert();
四.java内部类详解
1>为什么要存在内部类
1) 内部类(inner class):
定义在另一个类中的类;
2)存在原因
*1.内部类方法可以访问该类定义所在作用域中的数据,包括被private修饰的私有数据;
*2.内部类可以对一包名下的其他类隐藏起来;
*3.内部类可以弥补java单继承的缺陷;
*4.当我们想要定义一个回调函数却不想写大量代码时我们可以选择使用匿名内部类来实现;
3)内部类可以访问外部类成员变量原理
当外部类的对象创建了一个内部类的对象时,内部类对象必定会持有一个指向外部类对象的引用,
当访问外部类的成员时,就是用哪个引用来选择外部类的成员的,这些会由编辑器帮我们实现;
另外注意内部类只是一种编译器现象,与虚拟机无关,编译器会将内部类编译成外部类名$内部类名的常规文件;
4)内部类对同一包名下的其他类隐藏的好处:
普通类不能用private或protected来修饰,但内部类可以,这就可以使内部类实现某个接口,在向上转型时,
对外部来讲完全隐藏接口的实现;示例如下: 使用内部类,很好的隐藏了Incrementable的具体实现;
public interface Incrementable { void increment();}
public class Example {
private class InsideClass implements Incrementable{
@Override
public void increment() {
Log.d("内部类", "这是一个测试");
}
}
public Incrementable getIn(){
return new InsideClass();
}
}
5)内部类弥补java单继承的缺陷
java不允许使用extends去继承多个类,而在内部类之前多继承方式是通过实现接口的方式来完成的;
但使用接口会有不便,如实现一个接口要实现它的所有方法,而内部类可使我们的类继承多个具体类或抽象类。
public class ClassA { public String name(){return "NorthStar"}}
public class ClassB { public int age(){return 25}}
public class Example {
private class Test1 extends ClassA{
public String name(){super.name();}
}
public class Test2 extends ClassB(){
public String age(){super.age();}
}
public String name(){ return new Test1().name();}
public int age(){ return new Test2().age();}
}
6)通过匿名内部类来"优化"简单的接口实现
view.setOnClickListener(new OnclickListener(){
@override
public void onClick( doSomething()){//...do xxx...}
});
view.setOnClickListener(v->doSomething());
2>内部类与外部类的关系
1)对于非静态内部类,内部类的创建依赖外部类的实例对象,在没有外部类实例之前是无法创建内部类的
2)内部类是一个相对独立的实体,与外部类不是is-a关系;
3)创建内部类的时刻并不依赖与外部类的创建;
对于静态内部类确实不依赖于外部类的创建,但非静态内部类必须依赖外部类的实例
创建非静态内部类的方法(2种)
*1. ClassOuter.InnerClass in=new ClassOuter().new InnerClass();
*2. ClassOuter outer=new ClassOuter();
ClassOuter.InnerClass in= outer.new InterClass();
3>内部的分类及几种分类的详细使用注意事项
1)内部类分为:
静态内部类和非静态内部类,非静态内部类又可分为: 成员内部类、方法内部类、匿名内部类;
2)静态内部类和非静态内部类的区别
*1.静态内部类可以有静态成员,而非静态内部类则不能有静态成员;
*2.静态内部类可以访问外部类的静态变量,而不可访问外部类的非静态变量;
*3.非静态内部类的非静态成员可以访问外部类的非静态变量;
*4.静态内部类的创建不依赖于外部类,而非静态内部类必须依赖于外部类的创建而创建;
public class ClassOuter {
private int noStaticInt = 1;
private static int STATIC_INT = 2;
public static final String TAG = "内部类";
class InnerClass {
//static int num = 1; //此时编辑器会报错,非静态内部类不能有静态成员变量;
void fun() {
//非静态成员变量可以访问外部类所有成员变量,包括静态,私有和非静态变量
Log.d(TAG, "InnerClass-fun--" + noStaticInt + "");
Log.d(TAG, "InnerClass-fun--" + STATIC_INT + "");
}
}
static class StaticInnerClass {
static int num = 1; //静态内部类可以有静态成员变量;
void fun() {
//Log.d(TAG, noStaticInt+"");//此时编辑器会报错,静态内部类不能访问外部类的非静态成员变量;
Log.d(TAG, "StaticInnerClass-fun--" + STATIC_INT + "");
}
}
}
//非静态内部类的创建
ClassOuter.InnerClass in=new ClassOuter().new InnerClass();
//静态内部类的创建
ClassOuter.StaticInnerClass innerClass = new ClassOuter.StaticInnerClass();
3)局部(成员)内部类
如果一个内部类只在一个方法中使用到了,那我们可将这个类定义在方法内部,
这种内部类被称为局部内部类。其作用域仅限于该方法。
注意事项:
*1.局部内类不允许使用访问权限修饰符 public private protected 均不允许
*2.局部内部类对外完全隐藏,除了创建这个类的方法可以访问它其他的地方是不允许访问的。
*3.局部内部类与成员内部类区别在于被他应用的局部变量必须声明为final,并内部不允许修改变量的值
public void testFunctionClass() {
final int params = 1;
class FunctionClass {
//static int num = 1; //此时编辑器会报错,非静态内部类不能有静态成员变量;
private void fun() {
Log.d(TAG, "InnerClass-fun--" + params + "");
//params++;// params 不可变所以这句话编译错误
}
}
FunctionClass functionClass = new FunctionClass();
functionClass.fun();
}
4)匿名内部类
*1.匿名内部类是没有访问修饰符的。
*2.匿名内部类必须继承一个抽象类或者实现一个接口
*3.匿名内部类中不能存在任何静态成员或方法
*4.匿名内部类是没有构造方法的,因为它没有类名,只需要使用唯一的一次。
*5.与局部内部相同匿名内部类也可以引用局部变量。此变量也必须声明为 final
public void click(final int params){
//匿名内部类,实现的是ActionListener接口
new ActionListener(){
@Override
public void onAction(){
System.out.println("click action..." + params);
}
};
}
//匿名内部类必须继承或实现一个已有的接口
public interface ActionListener{
public void onAction();
}
5)为什么局部变量需要final修饰
原因:因为局部变量和匿名内部类的生命周期不同
匿名内部类在创建后是存储在堆中的,而方法中的局部变量是存储在java栈中,当方法执行完毕后,就执行退栈,
同时局部变量就会消失,那么此时匿名内部类还有可能在堆中存储,那么匿名内部类就会找不到局部变量;
为解决此问题,编译器会自动在匿名内部类中创建一个局部变量的备份,为该备份与局部变量保持一致,
故用final修饰,使之成为常量;:从java 8+开始,只要局部变量事实不变,那么final关键字可以省略
4>实际开发中会遇到内部类的问题
1)内部类会造成程序的内存泄漏
*1.如果一个匿名内部类没有被任何引用持有,那么匿名内部类对象用完就有机会被回收。
*2.如果内部类仅仅只是在外部类中被引用,当外部类的不再被引用时,外部类和内部类就可以都被GC回收。
*3.当内部类的引用被外部类以外的其他类引用时,即使外部类没有被引用,但因为内部类持有指向外部类的引用,
就会造成内部类和外部类无法被GC回收的情况。
2)在Android 中 Handler 作为内部类使用的时候其对象被系统主线程的Looper持有(也可能是子线程手动创建的Looper),
掌管的消息队列 MessageQueue 中的 Handler 发送的 Message 持有,当消息队列中有大量消息需要处理或者延迟消息
需要执行的时候,创建该 Handler 的 Activity 已经退出了,Activity 对象也无法被释放,这就造成了内存泄漏。
那么Handler何时会被释放,当消息队列处理完 Handler 携带的 message 的时候就会调用msg.recycleUnchecked()
释放Message所持有的Handler引用。
处理办法:
*1.在关闭Activity/Fragment的onDestroy(),取消还在排队的Message;
onDestroy(){
mHandler.removeCallbacksAndMessages(null);
}
*2.将Handler创建为静态内部类并采用软引用方式
private static class MyHandler extends Handler {
private final WeakReference<MainActivity> mActivity;
public MyHandler(MainActivity activity) {
mActivity = new WeakReference<MainActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity=mActivity.get();
if (activity==null||activity.isFinishing()) return;
//doSomething...
}
}
五.Java泛型中extends 和 super的区别
/**
* 泛型方法
* @param <T> 声明此方法为泛型方法
* @param c 用来创建泛型T代表的类的对象
* @return 指明该方法的返回值为T
* Class<T> 作用是指明泛型T的具体类型
*/
public <T> T getObject(Class<T> c)throws InstantiationError,IllegalAccessError{
T t =c.newInstance();//创建对象
}
<? extends T>和<? super T>是Java泛型中的“通配符(Wildcards)”和“边界(Bounds)”的概念。
<? extends T>:是指 “上界通配符(Upper Bounds Wildcards)”
<? super T>:是指 “下界通配符(Lower Bounds Wildcards)”
频繁往外读取内容的,适合用上界Extends。
经常往里插入的,适合用下界Super
六.静态代理和动态代理的区别,什么场景使用?
1>代理Proxy
Proxy代理模式是一种结构型设计模式,目的是为其他对象提供一个代理以控制对某个对象的访问。
代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。
更通俗的说,代理解决的问题当两个类需要通信时,引入第三方代理类,将两个类的关系解耦,
让我们只了解代理类即可,而且代理的出现还可以让我们完成与另一个类之间的关系的统一管理,
但是切记,代理类和委托类要实现相同的接口,因为代理真正调用的还是委托类的方法。
2>使用场合
若需委托类处理某一业务,则我们可先在代理类中统一处理然后再调用具体实现类;
3>代理类的分类(按代理的创建时期)
静态代理:由程序员创建代理类或特定工具自动生成源代码在对其编译,在程序运行前代理类.class文件已经存在了;
动态代理:在程序运行时运用反射机制动态创建而成;
七. Java集合
一.概述
1.数组与集合
1)集合特点:长度不固定,只能存储引用类型数据,如果存入基本数据类型会自动封装成对象;
2)数组的特点:长度固定,遍历速度快,可存储基本数据类型和引用数据类型
3)数组与集合存储引用数据类型都存的是地址;
4)如果元素个数固定推荐使用数组,若不固定推荐使用集合;
2.集合继承体系
二.Collection接口(set和List)
1.概述:
接口Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements),
Java SDK不提供直接继承Collection的类,java SDK提供的是它的子接口,如List和Set;
2.遍历:
使用Iterator(迭代器)去逐一访问collection中每一个存在的元素,典型方法如下:
Iterator it=Collection.iterator();获取一个迭代器;
while(it.hasNext()){
Object obj=it.next();//得到下一个元素;
}
3.主要方法:
1)boolean add(Object o);用于添加对象到集合.
2)boolean remove(object o);用于删除指定的对象;
3)int size(); 用于返回当前集合中元素的数量.
4)boolean contains(Object o);用于查找集合中是否有指定对象;
5)boolean isEmpty();用于判断集合是否为空;
6)Iterator iterator();用于返回一个迭代器;
7)boolean addAll(Collection c);用于将集合c中的所有元素添加到该集合;
8)boolean containsAll(Collection c);用于查找集合中是否有集合c中的所有元素;
9)void clear();用于删除集合中所有的元素;
10)void removeAll(Collection c);用于从集合中删除集合c中的所有元素;
11)void retainAll(Collection c);从集合中删除集合c中不包括的元素;
三.List接口(ArrayList,LinkedList,Vector,Stack)
1.概述:
接口List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置.用户能够使用索引来访问list中的元素,
这类似于java的数组;list与set不同List中允许有相同的元素,除具有Collection接口必备的iterator()方法外,
List还提供了一个listIterator()方法,返回一个ListIterator接口,与标准的iterator接口相比,ListIterator多了
一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历;
2.主要方法:
1)void add(int index,object elemt);用于在指定位置上添加一个对象;
2)boolean addAll(int index,Collectioc);用于将集合c的元素添加到指定位置;
3)Object get(int inx);用于返回List中指定位置元素;
4)int indexOf(Objeco);用于返回第一个出现元素o的位置;
5)Object remove(int inx);用于删除指定位置的元素;
6)Object set(int index,Object element);用元素element取代index位置上的元素,返回被取代的元素;
3.ArrayList类 改查(copyArray)
1>概述:ArrayList类实现了可变大小的数组,他允许所有元素,包括null;
2>主要方法
1)boolean add(Object o);用于将指定元素添加到列表的末尾;
2)boolean add(int index,Object element);用于在列表中指定的位置加入指定的元素;
3)boolean addAll(Collection c);用于将指定集合添加到列表的末尾;
4)boolean addAll(int index,Collection c);用于在列表中指定的位置加入指定的集合;
5)boolean clear();用于删除列表中中所有的元素;
6)boolean contains(object o);用于判断列表中是否包含元素;
7)boolean ensureCapacity();用于增加列表的容量,如果必须,该列表能够容纳m个元素;
8)object get(int index);用于返回列表中指定位置的元素.
9)int indexOf(object elem);用于在列表中查找指定元素的下标;
10)int size();用于返回当前列表的元素个数;
3>代表长度可以改变的数组,可以对元素进行随机访问,向ArrayList插入与删除元素速度慢;
4.LinkedList类(双链表,双端队列) 排序与增删
在实现中采用链表数据结构,插入和删除速度快,访问速度慢;
四.set接口
1>概述:Set接口是一种不包含重复元素的Collection,也就是说,任意两个元素e1和e2都有e1.equals(e2)=false关系,
Set最多有一个null元素,很明显Set的构造函数有一个约束条件,即传入的Collection参数不能包含重复的元素;
2>实现类
HashSet: 按照哈希算法来存取集合中的对象,存取速度比较快;
TreeSet: 实现sortedSet接口,能够对集合中的对象进行排序;
五.Map接口:
1>概念:Map接口没有继承与接口Collection,Map提供key到Value的映射,一个Map中不能包含相同的key,每个key只能映射一个value,
Map接口提供三种集合的视图,Map的内容可被当做一组key集合,一组value集合,或者一组key-value映射;
2>主要方法:
1) boolean equals(Object o);用于比较对象;
2) boolean remove(object o);用于删除一个对象;
3) put(object key,Object value);用于添加key和value;
六.Hash类
Hashtable类继承于Map接口,实现一个key-value映射的哈希表,任何非空的对象都可作为key或value,添加数据使用put(key,value),
取出数据使用get(key),例如 Hashtable num=new Hashtable(); num.put("two",new Integer(2));Integer n=num.get("two")
HashMap:Map基于散列表的实现。插入和查询“键值对”的开销是固定的。可以通过构造器设置容量capacity
和负载因子load factor,以调整容器的性能:
jdk1.8之前 数组+单链表 扩容因子 0.75 超过threshold(阈值)*0.75 容量加倍
jdk1.8之后 数组+自平衡红黑二叉树 当链表长度大于8时, 自动转为自平衡红黑二叉树;
hash表性能安全
hashMap(线程不安全) hashTable 线程安全(对整个hash表加锁) ConcurrentHashMap(只对链表枷锁)(put和remove)