目录
List接口概述:
List实现类之一:ArrayList
单例 (Singleton)设计模式
单例(Singleton)设计模式-懒汉式
说到懒汉式的线程安全方式:
List接口的理解:
List接口方法:
List实现类之二:LinkedList
新增方法:
List 实现类之三:Vector
List总结一下:
Collection子接口之二:Set接口
总结:
Set实现类之一:HashSet
这文章里边有单独的说明hashset
Set实现类之二:LinkedHashSet
Set实现类之三:TreeSet
理解:
自然排序:
排序—定制排序:
*一Map接口
Map接口概述:
hashMap底层:数组 加 链表 jdk7数组 加 链表 加 红黑树 jdk8
二:Map机制的理解:
三Hashmap的底层实现原理:
Map接口:常用方法
结果1
Map实现类之二:LinkedHashMap
Map实现类之三:TreeMap
Properties 类
Collections工具类:
鉴于Java中数组用来存储数据的局限性,我们通常使用List替代数组,我们一般说的动态数组。
List集合类中元素有序、且可重复,集合中的每个元素都有其对应的顺序索引。
List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据 序号存取容器中的元素。
JDK API中List接口的实现类常用的有:ArrayList、LinkedList和Vector。
ArrayList 是 List 接口的典型实现类、主要实现类
本质上,ArrayList是对象引用的一个”变长”数组
ArrayList的JDK1.8之前与之后的实现区别?
JDK1.7:ArrayList像饿汉式,直接创建一个初始容量为10的数组
JDK1.8:ArrayList像懒汉式,一开始创建一个长度为0的数组,当添加第一个元 素时再创建一个始容量为10的数组
Arrays.asList(…) 方法返回的 List 集合,既不是 ArrayList 实例,也不是 Vector 实例。 Arrays.asList(…) 返回值是一个固定长度的 List 集合
设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、 以及解决问题的思考方式。设计模免去我们自己再思考和摸索。就像是经典 的棋谱,不同的棋局,我们用不同的棋谱。”套路”
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。 如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象, 静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象 的变量也必须定义成静态的。
单例(Singleton)设计模式-饿汉式
class Singleton { // 1.私有化构造器
private Singleton() { }
// 2.内部提供一个当前类的实例
// 4.此实例也必须静态化 private static Singleton single = new Singleton();
// 3.提供公共的静态的方法,返回当前类的对象
public static Singleton getInstance() {
return single; } }
class Singleton {
// 1.私有化构造器 private Singleton() { }
// 2.内部提供一个当前类的实例
// 4.此实例也必须静态化
private static Singleton single;
// 3.提供公共的静态的方法,返回当前类的对象
public static Singleton getInstance() {
if(single == null)
{
single = new Singleton();
} return single;
}
}
加个线程安全的同步监视器,让这个单例的懒汉式线程更加安全:
public class danli {
}
class Bank{
private Bank(){
}
private static Bank instance=null;
public static Bank getInstance() {
// synchronized (Bank.class) {//效率低
// if (instance==null){
// instance=new Bank();
//
// }
// }
// return instance;
//
// }
if (instance == null) {//效率高 立个牌子告诉后来线程不能过来,直接出去
synchronized (Bank.class) {
if (instance == null) {
instance = new Bank();
}
}
}
return instance;
}
}
list接口 有序的可重复 动态数组 替换原有的数组 * ArrayList * Linkedlist * vector 早于list接口出现了一般不使用 * 它们之间的异同点 * 相同:都是实现list接口的实现类而出现的 都实现了list接口 * 存储数据特点相同 * 不同点: * 1.ArrayList 作为list接口的主要实现类 数组底层Object[]顺序的结构 * 2.ArrAyList 删除数据 需要覆盖的方式 后边的数据向前移动 插入元素需要往后移动 效率低 * 3.vector 作为list接口出现的古老实现类jdk出現最早,比是实现接口list还早 * 4. Arraylist 是一个线程不安全的但是效率高 * 5.vector 是一个线程安全的 同步 效率差点 * 6.底层Arraylist 底层使用的是Object 中数组的存储结构 Object []elementData * 7. LinkedList 底层使用的是双向存储的链表 对于频繁的插入和删除操作 效率比较高 * 8.双向链表的方式存储 删除一个元素 前一个元素的next地址给下一个元素地址 下一个元素的地址指向上一个元素的地址 * 双向链表没有数组 是数据结构中的一个数据存储 *9.常用方法 * 增:add();末尾添加 * 删:remove(Index) remove(obj) * 改:set(index ele) * 查:get(index) * 插:add(int index ,object obj) * 长度:size()
List除了从Collection集合继承的方法外,List 集合里添加了一些根据索引来 操作集合元素的方法。 1.void add(int index, Object ele):在index位置插入ele元素
2.boolean addAll(int index, Collection eles):从index位置开始将eles中 的所有元素添加进来
3.Object get(int index):获取指定index位置的元素
4.int indexOf(Object obj):返回obj在集合中首次出现的位置
5.int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
6.Object remove(int index):移除指定index位置的元素,并返回此元素
7.Object set(int index, Object ele):设置指定index位置的元素为ele
8.List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex 位置的子集合
@Test
public void test1(){
ArrayList list=new ArrayList();
list.add(123);
list.add(456);
list.add("aa");
list.add(new Person("tom",12));
list.add(456);//可重复
System.out.println(list);
//void add(int index ,object ele)在index的位置插入元素
list.add(1,"bb");
System.out.println(list);
//addAll(int index ,collection ele)
List list1 = Arrays.asList(1, 2, 3);
list.addAll(0,list1);
System.out.println(list);
//get(int index )根据索引获取数据
System.out.println(list.get(0));
//indexof()首次出现的位置
int index = list.indexOf(4567);//
System.out.println(index);
System.out.println(list.lastIndexOf(456));
//remove(int index)按照索引删除
Object obj = list.remove(0);//删除位置的元素
System.out.println(obj);
System.out.println(list);//删除往前移动
// set(int index ,Object ele) 设置指定位子的元素ele
list.set(0,"aa");
System.out.println(list);
//list sublist(int fromindex ,int toindext)左闭右和
List subList = list.subList(0, 2);
System.out.println(subList);//要确定的返回值
System.out.println(list);//本身的list不变
}
* 遍历:1,Itersator迭代器
* 2.增强for循环
3.用普通的循环
Test
public void test5(){
ArrayList list=new ArrayList();
list.add(123);
list.add(456);
list.add("aa");
list.add(new Person("tom",12));
list.add(456);//可重复
Iterator iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
//方式二增强for循环
for (Object obj:list){
System.out.println(obj);
}
for (int i = 0; i
对于频繁的插入或删除元素的操作,建议使用LinkedList类,效率较高
LinkedList:双向链表,内部没有声明数组,而是定义了Node类型的first和last, 用于记录首末元素。同时,定义内部类Node,作为LinkedList中保存数据的基 本结构。Node除了保存数据,还定义了两个变量: prev变量记录前一个元素的位置 next变量记录下一个元素的位置
底层的双链表形式:
底层双链表的是实现图:
1.void addFirst(Object obj) 在第一个位置添加
2. void addLast(Object obj) 最后一个位置添加
3. Object getFirst() 得到第一位置集合的对象
4.Object getLast() 得到最后一个位置集合的对象
5. Object removeFirst() 移除第一个位置集合的对象
6. Object removeLast()移除最后一个位置集合的对象
以上比较简单就不做代码演示了。
Vector 是一个古老的集合,JDK1.0就有了。大多数操作与ArrayList 相同,区别之处在于Vector是线程安全的。
在各种list中,最好把ArrayList作为缺省选择。当插入、删除频繁时, 使用LinkedList;Vector总是比ArrayList慢,所以尽量避免使用。
新增方法: void addElement(Object obj)
1 void insertElementAt(Object obj,int index)
2 void setElementAt(Object obj,int index)
3 void removeElement(Object obj)
4 void removeAllElements()
ArrayList和LinkedList的异同 二者都线程不安全,相对线程安全的Vector,执行效率高。 此外,ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。对于 随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。对于新增 和删除操作add(特指插入)和remove,LinkedList比较占优势,因为ArrayList要移动数据。
ArrayList和Vector的区别 Vector和ArrayList几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized),属于 强同步类。因此开销就比ArrayList要大,访问要慢。正常情况下,大多数的Java程序员使用 ArrayList而不是Vector,因为同步完全可以由程序员自己来控制。Vector每次扩容请求其大 小的2倍空间,而ArrayList是1.5倍。Vector还有一个子类Stack。
Set 接口概述
*set接口 无序的,不可重复的 ;类似于高中我们所学的集合,无序的性,确定性,互异性
这个确定性向党羽判断是否有误。
对于过滤问题,过滤相同的数据,使用HashSet lIinkedset treeset解决此问题。
Set接口是Collection的子接口,set接口没有提供额外的方法
Set 集合不允许包含相同的元素,如果试把两个相同的元素加入同一个 Set 集合中,则添加操作失败。
Set 判断两个对象是否相同不是使用 == 运算符,而是根据 equals() 方法。
* Hashset:set接口的主要实现类 线程不安全的
* linkedhashset :是hashset的子类,在hashset的基础上加上链条,
* 使得遍历其内部数据时,按照添加的顺序去遍历
* treeset:存储结构是一个树的结构 红黑树的方式存储的
* 要求存入treeset的数据必须是一个类建立的
* 可以按照添加队形的指定属性,进行排序
* set没有添加新的方法用的都是collection中的方法
HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类。
HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取、查找、删除 性能。
HashSet 具有以下特点:
不能保证元素的排列顺序
HashSet 不是线程安全的
集合元素可以是 null
HashSet 集合判断两个元素相等的标准:两个对象通过 hashCode() 方法比较相 等,并且两个对象的 equals() 方法返回值也相等。
对于存放在Set容器中的对象,对应的类一定要重写equals()和hashCode(Object obj)方法,以实现对象相等规则。即:“相等的对象必须具有相等的散列码”。
点击此链接查看 HashSet的实现过程,以及hash表的说明
LinkedHashSet 是 HashSet 的子类 linkedhashset :是hashset的子类,在hashset的基础上加上链条, 使得遍历其内部数据时,按照添加的顺序去遍历。
LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置, 但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入 顺序保存的。
LinkedHashSet插入性能略低于 HashSet,但在迭代访问 Set 里的全部元素时有很好的性能。 LinkedHashSet 不允许集合元素重复。
/** * LinkedHashSet的使用 作为hashset的子类 再添加数据的同时,每个数据还维护了两个变量 * 记录数据和后一个数据,linkedhashset 的效率高于hashset频繁遍历操作 */
上代码:
@Test
public void test2(){
Set set=new LinkedHashSet();
set.add(456);
set.add(123);
set.add("aa");
set.add("cc");
set.add("cc");//set接口中有int hashCode();重写
set.add(new User("tom",12));
set.add(new User("tom",12));
set.add(123);
//无序性不等于随机性 根据hash值来判断的相较于list有序是无序的
//hashset的底层是数组存的通过hash值在数组中添加存储位置
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
结果图:
HashSet底层结构:
TreeSet 是 SortedSet 接口的实现类,TreeSet 可以确保集合元素处于排序状态。
TreeSet底层使用红黑树结构存储数据 新增的方法如下: (了解)
Comparator comparator()
Object first()
Object last()
Object lower(Object e)
Object higher(Object e)
SortedSet subSet(fromElement, toElement)
SortedSet headSet(toElement)
SortedSet tailSet(fromElement) TreeSet 两种排序方法:自然排序和定制排序。默认情况下,TreeSet 采用自然排序。
1.向treeset中添加的数据 要求是相同类的对象 才可以比较大小,不能添加不同类的对象
* 自动从小到大排序 *
2两种排序方式
* 1.自然排序 比较两个对象是否相同的标准为compareto()返回0,不再是equals()实现compareto的接口
* treeset 不能放相同的数据
treeset和treemap 采用红黑树的形式
定制排序 comprartor打交道
在定制排序中 比较两个对象是否相同的标准为:compare()返回0不再是equals();
TreeSet和TreeMap 采用红黑树的存储结构
特点:有序,查询速度比List快
红黑树:
排 序—自然排序
自然排序:TreeSet 会调用集合元素的 compareTo(Object obj) 方法来比较元 素之间的大小关系,然后将集合元素按升序(默认情况)排列
如果试图把一个对象添加到 TreeSet 时,则该对象的类必须实现 Comparable 接口。
实现 Comparable 的类必须实现 compareTo(Object obj) 方法,两个对象即通过 compareTo(Object obj) 方法的返回值来比较大小。
Comparable 的典型实现: BigDecimal、BigInteger 以及所有的数值型对应的包装类:按它们对应的数值大小 进行比较 Character:按字符的 unicode值来进行比较 Boolean:true 对应的包装类实例大于 false 对应的包装类实例 String:按字符串中字符的 unicode 值进行比较 Date、Time:后边的时间、日期比前面的时间、日期大。
向 TreeSet 中添加元素时,只有第一个元素无须比较compareTo()方法,后面添加的所有元素都会调用compareTo()方法进行比较。
因为只有相同类的两个实例才会比较大小,所以向 TreeSet 中添加的应该是同 一个类的对象。 对于 TreeSet 集合而言,它判断两个对象是否相等的唯一标准是:两个对象通compareTo(Object obj) 方法比较返回值。 当需要把一个对象放入 TreeSet 中,重写该对象对应的 equals() 方法时,应保 证该方法与 compareTo(Object obj) 方法有一致的结果:如果两个对象通过 equals() 方法比较返回 true,则通过 compareTo(Object obj) 方法比较应返回 0。 否则,让人难以理解。
@Test
public void test1(){
TreeSet set = new TreeSet();
set.add(new User("tom",12));
set.add(new User("jiso",50));
set.add(new User("xiao",60));
set.add(new User("da",40));
set.add(new User("duo",11));
set.add(new User("duo",15));
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
class User implements Comparable{
private String name;
private int age;
public User(){}
public User(String name,int age){
this.name=name;
this.age=age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
if (age != user.age) return false;
return name != null ? name.equals(user.name) : user.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
// 按照姓名从小到大 从大到小 填个-负号
@Override
public int compareTo(Object o) {
if (o instanceof User) {
User user = (User) o;
//return -this.name.compareTo(user.name);
int compare = -this.name.compareTo(user.name);
if (compare !=0){
return compare;
}else {
return Integer.compare(this.age, user.age);//从小到大
}
} else {
throw new RuntimeException("输入类型不匹配");
}
}
结果按照姓名排序:
TreeSet的自然排序要求元素所属的类实现Comparable接口,如果元素所属的类没 有实现Comparable接口,或不希望按照升序(默认情况)的方式排列元素或希望按照 其它属性大小进行排序,则考虑使用定制排序。定制排序,通过Comparator接口来 实现。需要重写compare(T o1,T o2)方法。
利用int compare(T o1,T o2)方法,比较o1和o2的大小:如果方法返回正整数,则表 示o1大于o2;如果返回0,表示相等;返回负整数,表示o1小于o2。
要实现定制排序,需要将实现Comparator接口的实例作为形参传递给TreeSet的构 造器。
此时,仍然只能向TreeSet中添加类型相同的对象。否则发生ClassCastException异 常。
使用定制排序判断两个元素相等的标准是:通过Comparator比较两个元素返回了0。
@Test
public void test2(){
Comparator com=new Comparator() {//实现这个接口 比較器
//按照年龄从小到大排序 id一样的就不要了
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof User && o2 instanceof User){
User u1= (User) o1;
User u2= (User) o2;
return Integer.compare(u1.getAge(),u2.getAge());
}else {
throw new RuntimeException("输入数据类型不匹配");
}
}
};
TreeSet set = new TreeSet(com);
set.add(new User("tom",12));
set.add(new User("jiso",50));
set.add(new User("xiao",60));
set.add(new User("da",11));
set.add(new User("duo",11));
set.add(new User("duo",15));
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
class User implements Comparable{
private String name;
private int age;
public User(){}
public User(String name,int age){
this.name=name;
this.age=age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
if (age != user.age) return false;
return name != null ? name.equals(user.name) : user.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
// 按照姓名从小到大 从大到小 填个-负号
@Override
public int compareTo(Object o) {
if (o instanceof User) {
User user = (User) o;
//return -this.name.compareTo(user.name);
int compare = -this.name.compareTo(user.name);
if (compare !=0){
return compare;
}else {
return Integer.compare(this.age, user.age);//从小到大
}
} else {
throw new RuntimeException("输入类型不匹配");
}
}
名字相同按照年龄定制排序:这个两个duo是相同的,然后再自然排序中并不是 这样的。这是相同对象并没有相同元素。
结果图如下:
* Map接口 存儲双列数据 键值对的形式 类似于高中的函数的形式y=f(x) 1.2
* HashMap:作为map的主要实现类 线程不安全 效率高 可以存储 null的key和value
* ---linkedHashMap 1.4 增删改查 速度快 保证在遍历map元素时可以按照添加的顺序进行遍历,
* 原因 在原有的hashmap底层的结构基础上 添加一个键值对,指向前一个或后一个 找前后更方便 * 使用:对于平凡的遍历操作,此类执行效率高于hashmap
* -----treeMap:1.2有序键值对 可以按照添加的key value 进行排序 进行排序遍历,按照key的值进行排序,底层是个树形结构
* 此时考虑可以的自然排序 或定制排序
* 底层结构不同 使用的是红黑树 特殊的树型结构 treeset treemap用的是红黑树
* ----* Hashtable :作为一个古老的实现类:1.1 线程安全效率低 一般不适用 线程安全用connections
* --- --hashtable的子类Properties;常用来处理配置文件:key value都是string类型都是字符类型 *
Map与Collection并列存在。用于保存具有映射关系的数据:key-value
Map 中的 key 和 value 都可以是任何引用类型的数据
Map 中的 key 用Set来存放,不允许重复,即同一个 Map对象所对应的类,须重写hashCode()和equals()方法。
常用String类作为Map的“键”。
key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到 唯一的、确定的 value
Map接口的常用实现类:HashMap、TreeMap、LinkedHashMap和 Properties。其中,HashMap是 Map 接口使用频率最高的实现类。
键值对 key->value 对应的方式
map的键值对 :value可以重复,key不能重复 相当于是用set存储的,value可以重复,无序的用collection集合存储单列数据存储
* put(key ,value);两个两个放,但是是一个一个放的 entry 中前边一个k后边一个v,entry的特点是无序的,不可重复的用set来装 * * 可以多对一 不能一对多
* map中的key是无序的不可重复的,使用set存储所有key --- 要求我们的key要重写equals()方法和hashcode方法
*针对hashmap来说的
* map中的value是无序的,但是是可重复 使用collection来存储value---values所在的类重写equals(),存的时候一般用
* 一个键值对key--value 构成了以entry对象
* map中的entry是无序的,不可重复的,使用set存储所有entry
* 以jdk7为例说明 : * HashMap map=new HashMap(); * 在实例化话后,底层创建了一个长度为16的以为数组 entry[] table key entry value * 可能已经执行过多次put * map.put(key1,value1); * 首先调用key1所在类的hashcode()计算key1哈希值,此哈希值经过某种算法计算以后,得到entry数组是一个数组中的存放位置 * 如果此位置上的数据为空 key1 value1直接添加成功 添加到entry中 情况1 * 如果此位置数据不为空,意味着此位置上存在一个或多个数据(以链表的形式存在),比较我们key1和已经存在的一个或多个哈希值 * 如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1--value1添加成功。情况2 * 如果key1的哈希值与某一个数据(key2-value2)已存在的哈希值相同,继续比较:调用key1所在类的equals()方法,比较 * 如果equals()返回false 意味着key1-value1添加成功 情况3 * 如果equals()返回true :使用value1替换value2.:eg put("tom",23) put("tom",45),修改的作用 * * 情况2 情况3 有数据,链表的方式存储 拉链法 七上八下 * 添加过程中,会涉及扩容问题 默认扩容方式:扩容为原来的二倍,将原有的数据复制过来。 * jdk8 * new HashMap():底层没有出创建一个长度我16的数组 * 2.jdk8底层的数组是 node[] 而非entry[] * 首次调用put()方法式,底层创建长度为16的长度 * 原理jdk7的底层结构只有 数组加链表,jdk8中底层结构:数组加链表加红黑树 * 当数组的某一个索引位置上的元素以链表形式存在的数据个数>8,且当前数组的长度>64时,此时索引位置上的所有数据改为红黑树存储 * 方便查找,二叉树相当于折半查找 二叉树排序树,红黑树,优化
HashMap的内部存储结构其实是数组和链表的结合。当实例化一个HashMap时, 系统会创建一个长度为Capacity的Entry数组,这个长度在哈希表中被称为容量 (Capacity),在这个数组中可以存放元素的位置我们称之为“桶”(bucket),每个 bucket都有自己的索引,系统可以根据索引快速的查找bucket中的元素。
每个bucket中存储一个元素,即一个Entry对象,但每一个Entry对象可以带一个引 用变量,用于指向下一个元素,因此,在一个桶中,就有可能生成一个Entry链。 而且新添加的元素作为链表的head。
添加元素的过程: 向HashMap中添加entry1(key,value),需要首先计算entry1中key的哈希值(根据 key所在类的hashCode()计算得到),此哈希值经过处理以后,得到在底层Entry[]数 组中要存储的位置i。如果位置i上没有元素,则entry1直接添加成功。如果位置i上 已经存在entry2(或还有链表存在的entry3,entry4),则需要通过循环的方法,依次 比较entry1中key和其他的entry。如果彼此hash值不同,则直接添加成功。如果 hash值不同,继续比较二者是否equals。如果返回值为true,则使用entry1的value 去替换equals为true的entry的value。如果遍历一遍以后,发现所有的equals返回都 为false,则entry1仍可添加成功。entry1指向原有的entry元素。
添加、删除、修改操作: 1 Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中 2 void putAll(Map m):将m中的所有key-value对存放到当前map中 3 Object remove(Object key):移除指定key的key-value对,并返回value 4 void clear():清空当前map中的所有数据 元素查询的操作: 1 Object get(Object key):获取指定key对应的value 2 boolean containsKey(Object key):是否包含指定的key 3 boolean containsValue(Object value):是否包含指定的value 4 int size():返回map中key-value对的个数 5 boolean isEmpty():判断当前map是否为空 boolean equals(Object obj):判断当前map和参数对象obj是否相等 元视图操作的方法: 1 Set keySet():返回所有key构成的Set集合 2 Collection values():返回所有value构成的Collection集合 3 Set entrySet():返回所有key-value对构成的Set集合
代码展示:
package Map;
import jihe.Person;
import org.junit.Test;
import java.util.*;
/**
* Map接口 存儲双列数据 键值对的形式 类似于高中的函数的形式y=f(x) 1.2
* HashMap:作为map的主要实现类 线程不安全 效率高 可以存储 null的key和value
* ---linkedHashMap 1.4 增删改查 速度快 保证在遍历map元素时可以按照添加的顺序进行遍历,
* 原因 在原有的hashmap底层的结构基础上 添加一个键值对,指向前一个或后一个 找前后更方便
* 使用:对于平凡的遍历操作,此类执行效率高于hashmap
* -----treeMap:1.2有序键值对 可以按照添加的key value 进行排序 进行排序遍历,按照key的值进行排序,底层是个树形结构
* 此时考虑可以的自然排序 或定制排序上
* 底层结构不同 使用的是红黑树 特殊的树型结构 treeset treemap用的是红黑树
* ----* Hashtable :作为一个古老的实现类:1.1 线程安全效率低 一般不适用 线程安全用connections
* --- --hashtable的子类Properties;常用来处理配置文件:key value都是string类型都是字符类型
*
*
* hashMap底层:数组 加 链表jdk7
* 数组 加 链表 加 红黑树 jdk8
* 1.HashMap的底层原理:
* 2.Hashtable 和hashtable的异同?
* 3.CurrentHashMap 与hashtable的异同?有分段锁的形式线程安全如买票分时段买票
*
*
* 二:Map机构的理解
* map的键值对 :value可以重复,key不能重复 相当于是用set存储的,value可以重复,无序的用collection集合存储单列数据存储
* put(key ,value);两个两个放,但是是一个一个放的 entry 中前边一个k后边一个v,entry的特点是无序的,不可重复的用set来装
*
* 可以多对一 不能一对多
* map中的key是无序的不可重复的,使用set存储所有key --- 要求我们的key要重写equals()方法和hashcode方法
*针对hashmap来说的
* map中的value是无序的,但是是可重复 使用collection来存储value---values所在的类重写equals(),存的时候一般用
* 一个键值对key--value 构成了以entry对象
* map中的entry是无序的,不可重复的,使用set存储所有entry
*三 hashmap的底层实现原理?
* 以jdk7为例说明 :
* HashMap map=new HashMap();
* 在实例化话后,底层创建了一个长度为16的以为数组 entry[] table key entry value
* 可能已经执行过多次put
* map.put(key1,value1);
* 首先调用key1所在类的hashcode()计算key1哈希值,此哈希值经过某种算法计算以后,得到entry数组是一个数组中的存放位置
* 如果此位置上的数据为空 key1 value1直接添加成功 添加到entry中 情况1
* 如果此位置数据不为空,意味着此位置上存在一个或多个数据(以链表的形式存在),比较我们key1和已经存在的一个或多个哈希值
* 如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1--value1添加成功。情况2
* 如果key1的韩系值与某一个数据(key2-value2)已存在的哈希值相同,继续比较:调用key1所在类的equals()方法,比较
* 如果equals()返回false 意味着key1-value1添加成功 情况3
* 如果equals()返回true :使用value1替换value2.eg put("tom",23) put("tom",45),修改的作用
*
* 情况2 情况3 有数据,链表的方式存储 拉链法 七上八下
* 添加过程中,会涉及扩容问题 默认扩容方式:扩容为原来的二倍,将原有的数据复制过来。
* jdk8
* new HashMap():底层没有出创建一个长度我16的数组
* 2.jdk8底层的数组是 node[] 而非entry[]
* 首次调用put()方法式,底层创建长度为16的长度
* 原理jdk7的底层结构只有 数组加链表,jdk8中底层结构:数组加链表加红黑树
* 当数组的某一个索引位置上的元素以链表形式存在的数据个数>8,且当前数组的长度>64时,此时索引位置上的所有数据改为红黑树存储
* 方便查找,二叉树相当于折半查找 二叉树排序树,红黑树,优化
*
*四:linekdehashmap底层实现原理
* //频繁
*五。第一层面:
* Map接口中定义的方法
* 添加、删除、修改操作:
* Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
* void putAll(Map m):将m中的所有key-value对存放到当前map中
* Object remove(Object key):移除指定key的key-value对,并返回value
* void clear():清空当前map中的所有数据
* 元素查询的操作:
* Object get(Object key):获取指定key对应的value
* boolean containsKey(Object key):是否包含指定的key
* boolean containsValue(Object value):是否包含指定的value
* int size():返回map中key-value对的个数
* boolean isEmpty():判断当前map是否为空
* boolean equals(Object obj):判断当前map和参数对象obj是否相等
* 元视图操作的方法:
* Set keySet():返回所有key构成的Set集合
* Collection values():返回所有value构成的Collection集合
* Set entrySet():返回所有key-value对构成的Set集合
*
*
*
* 常用方法:
* 添加 put(Object key,Object value)
* 删除 remove
* 修改 put(Object key,Object value) 名称相同
* 查询 get(Object key)
* 长度 size()
* 遍历 keyset() values() entrySet()
* */
public class MapTest {
/*
元视图操作的方法:
Set keySet():返回所有key构成的Set集合
Collection values():返回所有value构成的Collection集合
Set entrySet():返回所有key-value对构成的Set集合
*/
//value可以重复,key不能重复 相当于是用set存储的 获取这个key
// value可以重复,无序的用collection集合存储单列数据存储 获取这个value 得到
//key=>value 构成 一个entry对象 set.entry()方法 或collection.entry()方法
@Test
public void test6(){
//遍历哈希map
Map map = new HashMap();
map.put("aa",123);//key的值一般都是确定
map.put("45",56);//key的值一般都是确定
map.put(12,35);//key的值一般都是确定
map.put("cc",123);//key的值一般都是确定
//遍历一下key集合 :keySet()方法
Set set = map.keySet();//一个set集合
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next()); //输出 aa cc 45 12按照hash值得顺序
}
System.out.println("============================");
//遍历所有的value集合;键值对的value :用values()方法 //遍历 找这个value都是通过键值对的hash值 找到entry 来取的
Collection values = map.values();//键值对的value的所有集合
for (Object obj:values){
System.out.println(obj);
}
System.out.println("============================");
//遍历所有的key-value
//Set entrySet():返回所有key-value对构成的Set集合
Set entrySet = map.entrySet();
Iterator iterator1 = entrySet.iterator();
while (iterator1.hasNext()){
Object obj = iterator1.next();
Map.Entry entry= (Map.Entry) obj;
System.out.println(entry.getKey()+"-->"+entry.getValue());//entry执行的key entry执行一个value
}
System.out.println("============================");
//方式二 拼凑一个
Set Keyset = map.keySet();//一个set集合
Iterator iterator2 = Keyset.iterator();
while (iterator2.hasNext()){
Object key = iterator2.next();
Object value=map.get(key);
System.out.println(key+"-->"+value);//entry执行的key entry执行一个value
//System.out.println(iterator.next()); //输出 aa cc 45 12按照hash值得顺序
}
}
/* 元素查询的操作:
* Object get(Object key):获取指定key对应的value
* boolean containsKey(Object key):是否包含指定的key
* boolean containsValue(Object value):是否包含指定的value
* int size():返回map中key-value对的个数
* boolean isEmpty():判断当前map是否为空
* boolean equals(Object obj):判断当前map和参数对象obj是否相等
*/
@Test
public void test5(){
Map map = new HashMap();
map.put("aa",123);//key的值一般都是确定
map.put("45",56);//key的值一般都是确定
map.put(12,35);//key的值一般都是确定
map.put("cc",123);//key的值一般都是确定
//Object get(Object key):获取指定key对应的value
System.out.println(map.get("aa"));//不存在返回null
//boolean containsKey(Object key):是否包含指定的key
boolean isexist = map.containsKey("cc");//找到hash值
System.out.println(isexist);
//boolean containsValue(Object value):是否包含指定的value
boolean b = map.containsValue(1234);
System.out.println(b);//找到一个就是true
//int size():返回map中key-value对的个数 获取对数
map.clear();
System.out.println(map.isEmpty());//boolean isEmpty():判断当前map是否为空
//oolean equals(Object obj):判断当前map和参数对象obj是否相等
Map map1 = new HashMap();
map1.put("aa",123);//key的值一般都是确定
map1.put("45",56);//key的值一般都是确定
map1.put(12,35);//key的值一般都是确定
map1.put("cc",123);//key的值一般都是确定
Map map2 = new HashMap();
map2.put("aa",123);//key的值一般都是确定
map2.put("45",56);//key的值一般都是确定
map2.put(12,35);//key的值一般都是确定
map2.put("cc",123);//key的值一般都是确定
boolean equals = map1.equals(map2);
System.out.println(equals);
}
// 添加、删除、修改操作:
// * Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
// * void putAll(Map m):将m中的所有key-value对存放到当前map中
// * Object remove(Object key):移除指定key的key-value对,并返回value
// * void clear():清空当前map中的所有数据
// {aa=87, ff=123, zz=123, 45=56, 12=35} 等号区分左右 key与values
@Test
public void test3(){
Map map = new HashMap();
map.put("aa",123);//key的值一般都是确定
map.put("45",56);//key的值一般都是确定
map.put(12,35);//key的值一般都是确定
map.put("cc",123);//key的值一般都是确定
//修改的一个操作 替换原有的123
map.put("aa",87);//key的值一般都是确定
System.out.println(map);
Map map1 = new HashMap();
map1.put("ff",123);//key的值一般都是确定 value 可以相等的
map1.put("zz",123);//key的值一般都是确定
map.putAll(map1);
System.out.println(map);
//remove(Object key)
Object value = map.remove("cc");//移除cc =>123
Object value1 = map.remove("cccc");//没有返回null
System.out.println(value);
System.out.println(map);
//clear()
map.clear();//等同于把map中的数据清楚,不是变成了null 与map=null不同
System.out.println(map.size());
System.out.println(map);
}
@Test
public void tes2(){
// Map map = new LinkedHashMap();
Map map = new HashMap();
map=new LinkedHashMap();
map.put("123","aa");
map.put("456","cc");
map.put("12","dd");
map.put("13","ee");
System.out.println(map);
}
@Test
public void test1(){
Person person = new Person();
person.setName("小明");
Map map = new HashMap();//可以放null健壮性好一点
//map=new Hashtable();这样是不对的key value 健壮性差
//map.put(null,null);
map.put("22", new String[]{"sa", "as", "sf", "技术", "倒计时"});
map.put(person.getName(),new Person("龙",12));
System.out.println(map);
}
}
/* 元视图操作的方法: Set keySet():返回所有key构成的Set集合 Collection values():返回所有value构成的Collection集合 Set entrySet():返回所有key-value对构成的Set集合 */ //value可以重复,key不能重复 相当于是用set存储的 获取这个key // value可以重复,无序的用collection集合存储单列数据存储 获取这个value 得到 //key=>value 构成 一个entry对象 set.entry()方法 或collection.entry()方法
元素查询的操作: * Object get(Object key):获取指定key对应的value * boolean containsKey(Object key):是否包含指定的key * boolean containsValue(Object value):是否包含指定的value * int size():返回map中key-value对的个数 * boolean isEmpty():判断当前map是否为空 * boolean equals(Object obj):判断当前map和参数对象obj是否相等 */
// 添加、删除、修改操作: // * Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中 // * void putAll(Map m):将m中的所有key-value对存放到当前map中 // * Object remove(Object key):移除指定key的key-value对,并返回value // * void clear():清空当前map中的所有数据 // {aa=87, ff=123, zz=123, 45=56, 12=35} 等号区分左右 key与values
LinkedHashMap 是 HashMap 的子类
在HashMap存储结构的基础上,使用了一对双向链表来记录添加元素的顺序
与LinkedHashSet类似,LinkedHashMap 可以维护 Map 的迭代顺序:迭代顺序与 Key-Value 对的插入顺序一致。
TreeMap存储 Key-Value 对时,需要根据 key-value 对进行排序。 TreeMap 可以保证所有的 Key-Value 对处于有序状态。
TreeSet底层使用红黑树结构存储数据
TreeMap 的 Key 的排序: 自然排序:TreeMap 的所有的 Key 必须实现 Comparable 接口,而且所有 的 Key 应该是同一个类的对象,否则将会抛出 ClasssCastException
定制排序:创建 TreeMap 时,传入一个 Comparator 对象,该对象负责对 TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现 Comparable 接口
TreeMap判断两个key相等的标准:两个key通过compareTo()方法或 者compare()方法返回0。
User类实现重写equals()方法 重写 hashcode()方法 实现implements Comparable
package Map;
class User implements Comparable{
private String name;
private int age;
public User(){}
public User(String name, int age){
this.name=name;
this.age=age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
if (age != user.age) return false;
return name != null ? name.equals(user.name) : user.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
// 按照姓名从小到大 从大到小 填个-负号
@Override
public int compareTo(Object o) {
if (o instanceof User) {
User user = (User) o;
//return -this.name.compareTo(user.name);
int compare = -this.name.compareTo(user.name);
if (compare !=0){
return compare;
}else {
return Integer.compare(this.age, user.age);//从小到大
}
} else {
throw new RuntimeException("输入类型不匹配");
}
}
}
//向Treemap中添加key-value,要求必须由同一个类创建的对象 //因为要按照key进行排序,自然排序和定制排序
package Map;
import org.junit.Test;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeMap;
public class TreeMapTest {
//向Treemap中添加key-value,要求必须由同一个类创建的对象
//因为要按照key进行排序,自然排序和定制排序
@Test
public void test(){
TreeMap map = new TreeMap();
User u1=new User("tom",23);
User u2=new User("ross",30);
User u3=new User("pigui",58);
User u4=new User("zhu",25);
map.put(u1,88);
map.put(u2,100);
map.put(u3,66);
map.put(u4,77);
Set set = map.entrySet();
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
@Test
public void test2(){
TreeMap map = new TreeMap(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof User && o2 instanceof User){
User u1= (User) o1;
User u2= (User) o2;
return Integer.compare(u1.getAge(),u2.getAge());
}
throw new RuntimeException("输入类型不匹配");
}
});
User u1=new User("tom",23);
User u2=new User("ross",30);
User u3=new User("pigui",58);
User u4=new User("zhu",25);
map.put(u1,88);
map.put(u2,100);
map.put(u3,66);
map.put(u4,77);
Set set = map.entrySet();
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
结果截图:
自然排序:
在User类中实现implements Comparable 重写compareTo()方法,以姓名的降序排序。
定制排序:
按照年龄的升序排序:利用interface Comparator 接口比较器,实现定制,姓名相同的情况下以age实现排序,这样避免,键值对相同元素加入不进去。。
Properties 类是 Hashtable 的子类,该对象用于处理属性文件
由于属性文件里的 key、value 都是字符串类型,所以 Properties 里的 key 和 value 都是字符串类型
存取数据时,建议使用setProperty(String key,String value)方法和 getProperty(String key)方法
创建一个properties的文件
在jdbc中键入字符:等于 还有name都要挨着写,不能出现空格,换行必须定格写。不然使用容易出错
测试代码如下:
package Map;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
public class PropertiesTest {
/*
--hashtable的子类Properties;
常用来处理配置文件:key value都是string类型都是字符类型
*/
public static void main(String[] args) {
Properties pro = null;//物理上存在的文件 配置文件
try {
pro = new Properties();
FileInputStream fis = new FileInputStream("D:\\javaweb\\JSP\\nb\\jdbc.properties");
pro.load(fis);//加载流配置文件
} catch (IOException e) {
e.printStackTrace();
}
String name = pro.getProperty("name");
String password = pro.getProperty("password");
System.out.println("name="+name+",password"+password);
}
}
结果:与配置文件一致!
一般我们使用的ArrayList,Hashset HashMap 它们的效率高,但是线程是不安全的,相比线程安全的相对的效率比较低。那么我们使用Collection工具类就可以解决,并且非常简单。
操作数组的工具类:Arrays
Collections 是一个操作 Set、List 和 Map 等集合的工具类
Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作, 还提供了对集合对象设置不可变、对集合对象实现同步控制等方法
排序操作:(均为static方法)
reverse(List):反转 List 中元素的顺序
shuffle(List):对 List 集合元素进行随机排序
sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序 swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换
Collections常用方法
查找、替换
Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回 给定集合中的最大元素
Object min(Collection) 根据元素的自然顺序,返回给定集合中的最小元素
Object min(Collection,Comparator) 根据 Comparator 指定的顺序,返回 给定集合中的最小元素
int frequency(Collection,Object):返回指定集合中指定元素的出现次数
void copy(List dest,List src):将src中的内容复制到dest中
boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换 List 对象的所有旧
上代码:
package Collecttions;
import org.junit.Test;
import java.util.*;
/**
* collections:操作的collection,map的工具类
*
*
* Collections和collection的区别?
* collection 是集合的总体集合接口,是所有集合的总接口
* Collections工具类
*排序操作:(均为static方法)
* reverse(List):反转 List 中元素的顺序
* shuffle(List):对 List 集合元素进行随机排序
* sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
* sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
* swap(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 对象的所有旧值
*/
public class CollectionsTest {
@Test
public void test(){
//reverse(List):反转 List 中元素的顺序
List list = new ArrayList();
list.add(123);
list.add(123);
list.add(123);
list.add(456);
list.add(234);
list.add(78);
list.add(456);
list.add(98);
System.out.println(list);
Collections.reverse(list);
Collections.shuffle(list);
Collections.sort(list);//升序
Collections.swap(list,1,2);
Collections.sort(list, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return -Integer.compare((Integer) o1, (Integer) o2);
}
});
System.out.println(list);
int frequency = Collections.frequency(list, 123);
System.out.println(list);
System.out.println(frequency);
//void copy(List dest,List src):将src中的内容复制到dest中
//报异常Source does not fit in dest
// List list1 = new ArrayList(list.size());
// Collections.copy(list1,list);
List list1 = Arrays.asList(new Object[list.size()]);
Collections.copy(list1,list);
System.out.println(list);
System.out.println(list1);
//线程是安全的 map set 都可以实现线程安全的
List synchronizedList = Collections.synchronizedList(list);
}
}
结果:
//线程是安全的 map set 都可以实现线程安全的 List synchronizedList = Collections.synchronizedList(list);
看一下代码内部结构:
都是使用SynchronizedCollection 同步线程安全的形式存在,这样更加简单的解决了ArrayList,Hashset HashMap 它们的效率高,但是线程是不安全的这个问题。