黑马程序员05 集合 和hashmap引发的内存泄露Annotation(注释)

本文深入探讨Java集合框架中的HashMap和HashSet,特别是它们可能导致的内存泄露问题,强调对象的hashCode()和equals()方法的重要性。同时,介绍了Java的注解(Annotation)功能,包括基本的Override、Deprecated和SuppressWarnings注解,并讲解了自定义注解及其元注解的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

---------------------- android培训java培训、期待与您交流!----------------------


集合

    1.什么是集合

        集合是一种容器, 可以用来存储对象, 和数组类似, 但最大的区别在于长度可变.

    2.什么是List

        List是集合中的一种, 元素可重复, 有存储顺序.

        List接口下的所有子类都是可重复且有序的, 我们主要使用三种: ArrayList, LinkedList, Vector

    3.List常用方法

        添加对象到最后:

        添加对象到指定位置:

        通过索引删除:

        通过对象删除:

        设置指定位置上的元素:

        获取指定位置上的元素:

        将一个集合中的所有元素添加到另一个集合:

        获取集合的大小:

        清空集合:

    4.List的迭代

所有的list都可以用一下三种方式进行迭代

privatestatic void demo2() {

       List list = new ArrayList();

       list.add(new Person("张三",24));

       list.add(new Person("王五",25));

       list.add(new Person("李四",26));

       //for循环

       for(int i=0;i<list.size();i++){

              System.out.println(list.get(i));

       }

       //增强for循环

       for(Object obj  :list){

              System.out.println(obj);

       }

 

       //迭代器

       Iterator it = list.iterator();

       while(it.hasNext()){

              System.out.println(it.next());

       }

Set集合

     1.什么是Set集合

         Set集合和List集合类似, 都是可以用来存储对象,长度可变.

         Set集合和List不同的是: 没有重复元素, 没有存储顺序.

     2.Set集合的常用方法

         添加元素:boolean add(E e)

         删除元素:boolean remove(E e)

         获取长度:int size();

         获取迭代器:iterator

         判断是否包含:boolean contains

         将另一个集合中的全部添加:addAll

         转换成数组:toArray(T[] a)

     3.HashSet

         HashSet是Set的一个实现类, 它可以做到去重复,效率非常高.

         当我们需要把多个对象去重复的时候, 就可以使用HashSet.

         存储原理:

              使用HashSet存储对象的时候, 会先调用对象的hashCode()方法, 计算一个哈希值.

              查找集合中是否有哈希值相同的对象

                   如果没有就直接存入

                   如果有哈希值相同的对象, 和相同的对象逐个进行equals比较

                       如果比较结果全部为false就存入

                       如果比较结果包含true就不存

         用HashSet存储自定义对象:

              对象需要重写hashCode()和equals()方法

              hashCode方法要保证属性相同, 返回值相同, 属性不同尽量不同.

              equals方法要确保属性相同的返回true, 不同的返回false.

:HashSet采用哈希值算法存取对象的集合,内部采用对某个数字n进行取余的方式对哈希码进行分组和划分对象的存储区域,Object类中定义一个hashCode()方法来返回每个java对象的哈希码,当从hashset集合查找某个对象时,java系统首先调用对象的hashcode方法获取该对象的哈希码,然后根据哈希码找到相应的存储区域,最后取出该存储区域的的每个元素对该对象进行equals方法比较,如此就不需要遍历集合中的所有元素,

还可以防止内存泄露,如果两个对象equals相等

如果一个对象被存入hashset集合中,就不要删除其参与运算的哈希值字段,因为修改后的哈希值跟最初的哈希值就不同了,要删除原来的对象在另外一个区了,就找不到,没法删掉,出现了内存泄露。

 

     4.LinkedHashSet

         是HashSet的子类, 实现原理相同, 但可以保留存储顺序.

     5.TreeSet

         TreeSet是Set的一个实现类, 可以去掉重复元素,可以指定顺序. 按照指定的比较方式比较出元素的大小, 以二叉树形式进行存储.

         自然顺序:

              TreeSet默认按照compareTo方法中的顺序来排序.

             compareTo是Comparable接口中的抽象方法, 如果想将自定义类的对象装入TreeSet排序, 那么就需要在类上实现Comparable, 重写compareTo方法

              compareTo方法返回一个int值, TreeSet在存储对象的时候就会调用compareTo方法, 根据int值构建二叉树.

              返回的int值如果是负数就代表小于, 正数代表大于, 0代表相等.

         比较器顺序:

              在创建TreeSet的时候, 可以在构造函数中传入一个Comparator接口的实现类.

              传入Comparator之后, 再向TreeSet中添加元素时, 就会调用Comparator中的compare方法来比较了.


 

Map集合

     1.什么是Map集合

         Map集合可以存储键值对, 在存储的时候存一个键对象和一个值对象.

         集合中的键是唯一的, 可以根据键获取值.

     2.Map集合常用方法

         添加记录:  V put(K key, V value)

         删除记录:  V remove(Object key)

         根据键获取值:  V get(Object key)

         获取长度:  int size()

         判断是否包含某个键: boolean containsKey(Object key)

         判断是否包含某个值: boolean containsValue(Object value)

         获取所有键组成的集合: Set<K> keySet()

         获取所有键值对组成的集合: Set<Map.Entry<K,V>> entrySet()

     3.迭代方式

         a.keySet:

代码演示:private static void iter1(Map<Person,Integer> map) {

     // 获取Map中的所有键对象组成的Set集合

         Set<Person>keySet = map.keySet();                              

         // 迭代Set集合获取每一个键对象

for(Person key : keySet)  

// 通过键对象获取值

System.out.println(key+ " = " + map.get(key));   

              }

 

         b.entrySet:

private static void iter2(Map<Person, Integer>map) {

         // 获取Map中所有Entry(键值对)对象组成的Set集合

         Set<Entry<Person,Integer>> entrySet = map.entrySet(); 

         // 迭代Set集合获取每一个Entry对象

          for (Entry<Person, Integer> e:entrySet)      

         // 通过Entry对象获取键和值

         System.out.println(e.getKey()+ " = " + e.getValue()); 

     }

        

     4.HashMap

         在存储键值对的时候, 使用哈希算法对键对象去重复, 效率高,没有顺序.

         当存储一个键值对的时候, 先调用键对象的hashCode()方法计算一个哈希值, 在集合中查找是否有哈希值相同键对象.

              如果没有哈希值相同的键对象, 直接将键值对存入.

              如果有哈希值相同键对象, 则和哈希值相同的键对象进行equals()比较.

                   比较结果为false就存入键值对.

                   比较结果为true则用新的值覆盖旧的值.

     5.TreeMap

         在存储键值对的时候, 通过指定的比较算法, 对键对象进行排序, 以二叉树形式进行存储.

         创建TreeMap时如果没有传入比较器, 则会按照键对象的自然顺序来排序. 自然顺序就是实现了Comparable接口之后compareTo方法中定义的顺序.

         如果我们不希望使用自然顺序排序, 还在TreeMap的构造函数中可以传入一个比较器. 比较器就是实现了Comparator接口的对象, 按照其中compare方法排序.

     6.LinkedHashMap

         HashMap的子类, 实现原理和HashMap相同, 但是可以保留存储顺序.

     7.Hashtable  

         Hashtable和HashMap原理相同, 都是基于哈希算法的, Hashtable是线程安全的, 而且Hashtable不允许存储空的键和空的值.

     8.Properties

         Hashtable的子类, 通常用来操作配置文件.    

hashCode方法与HashSet可能引发的内存泄露

通常比较是逐一取出每个元素要查找的对象进行比较,当发现某个元素与要查找的对象进行equals方法比较的结果相等是,则停止继续查找并返回肯定的信息,否则返回否定的信息,如果有1万个元素就要从集合中取出一万个元素进行逐一比较才能得到结论雨伞有人发明了哈希算法来提高元素的效率

      哈希算法:提高从集合中查找元素的效率。这种方式将集合分成若干个存储区域,每个对象可以计算出一个哈希码。可以将哈希码分组,每组分别对应某个存储区域,根据一个对象的哈希码就可以确定该对行啊应该存储在哪个区域。

     
       HashSet
就是采用哈希算法存取对象的集合,它内部采用对某个数字n进行取余的方式对哈希码进行分组和划分对象的存储区域,、
       Object
类中定义了一个hashCode()方法来返回每个Java对象的哈希码,当从HashSet集合中查找某个对象时,Java系统首先调用对象的hashCode()方法获得该对象的哈希吗,然后根据哈希吗找到相应的存储区域,最后取出该存储区域内的每个元素对该对象进行equals方法比较,这样不用遍历集合中的所有元素就可以得到结论。可见,HashSet集合具有很好的对象检索性能,但是,HashSet集合存储对象的效率相对要低些,因为向HashSet集合添加一个对象时,要先计算出对象的哈希码和根据这一个哈希码确定对象在集合中的存放位置。

      为了保证一个类的实例对象能在HashSet正常存储,要求这个类的两个实例对象用equals()方法比较的结果相等时,它们的哈希码也必须相等,也就是说,如果obj1.equals(obj2)的结果为true,那么一下表达式的结果页要为true

      obj1.hashCode() == obj2.hashCode()

      如果一个类的hashCode()方法没有遵循上述要求,那么,当这个的两个实例对象用equals()方法比较的结果相等时,它们本来应该无法同时存储进Set集合中,但是,如果将它们存储进HashSet集合中时,由于它们的hashCode()方法的返回值不同,第二个对象首先按哈希码计算可能会被放进与第一个对象不同的区域中,这样,它就不可能与第一个对象进行equals方法比较了,也就可能被存储进HashSet集合中了。Object类的hashCode()方法不能满足对象被存入到HashSet中的要求,因为它的返回值是通过对象的内存地址推算出来的,同一个对象在程序运行期间的任何时候返回的哈希值都是始终不变的,所以,只要是两个不同的实例对象,即使它们的equals方法比较结果相等,它们默认的hashCode方法的返回值是不同的。

      只有类的实例对象被采用哈希算法进行存储和检索时,这个类才需要按要求覆盖hashCode方法。即使程序可能暂时不会用到当前类的hashCode方法,但是为它提供一个hashCode方法也不会有什么不好,没准以后什么时候又用到这个方法了,所以,通常要求hashCode方法和equals方法一并被同事覆盖。

提示:

      1)通常来说,一个类的两个实例对象用equals()方法比较的结果相等时,它们的哈希码也必须相等,但反之则不成立,即euqlas方法比较结果不相等的对象可以有相同的哈希码,或者说哈希码相同的两个对象的equals方法比较的结果可以不等,例如,字符串“BB”"Aa"euqals方法比较结果肯定不相等,但它们的hashCode方法返回值却相等。

      2)当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在cantains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果。这也会导致从HashSet集合中单独删除当前对象,从而造成内存泄露。

ArrayListHashSet的存储ArrayList 有序的数组(位置顺序)将对象的引用放进去
HashSet
先判断里面是否有这个对象,如果没有就不放这个对象,如果想要放就把原来的remove Set不允许重复 equals值相等但是内存地址不同hashcode不同就算有值相等但是hashcode不在同一片区域不在那片区域找为了按相等的对象放在相同的区域所以人家就有这样子的说法如果equals相等的话你应该让他们的hashcode也相等就要覆写hash方法

2,内省

内省是 Java 语言对 Bean 类属性的一种缺省处理方法。例如类 A 中有属性 name, 可以通过 getName,setName 来得到其值或者设置新的值。通过getName/setName 来访问 name 属性,这是默认的规则。 Java 中提供了一套 API 来访问某个属性的 getter/setter 方法。

一般的做法是通过类 Introspector 来获取某个对象的 BeanInfo 信息,然后通过 BeanInfo 来获取属性的描述器( PropertyDescriptor ),通过这个属性描述器就可以获取某个属性对应的getter/setter 方法,然后通过反射机制来调用这些方法

操作:使用PropertyDescriptor 对象调用getWriteMethod 就可以得到Setter方法然后ObjectsetVal=methodSetX.invoke(obj, value);对属性进行修改,通过PropertyDescriptor实例对象 .getReadMethod();就可以获取Getter方法然后ObjectretVal=methodGetX.invoke(obj);就可以获取对象的属性,

beanutils工具包Apache组织开发了一套用于操作JavaBeanAPI,这套API考虑到了很多实际开发中的应用场景,因此在实际开发中很多程序员使用这套API操作JavaBean,以简化程序代码的编写

beanUtils支持多种转换类型java.util.Date是不被支持的,而它的子类java.sql.Date是被支持的。因此如果对象包含时间类型的属性,且希望被转换的时候,一定要使用java.sql.Date类型。否则在转换时会提示argument mistype异常,beanUtils以字符串的形式对javabean进行操作同时支持属性链的操作BeanUtils.setProperty(obj,"birthday.time", "10")其中PropertyUtils方法javabean本身的类型转换!

3Annotation

 

 JDK5.0 开始, Java 增加了对元数据(MetaData) 的支持, 也就是 Annotation(注解)Annotation 其实就是代码里的特殊标记, 它用于替代配置文件,也就是说,传统方式通过配置文件告诉类如何运行,有了注解技术后,开发人员可以通过注解告诉类如何运行。在Java技术里注解的典型应用是:可以通过反射技术去得到类里面的注解,以决定怎么去运行类。 这些标记可以在编译, 类加载, 运行时被读取, 并执行相应的处理. 通过使用 Annotation, 程序员可以在不改变原有逻辑的情况下, 在源文件中嵌入一些补充信息

Annotation(注释)功能

Annotation 可以像修饰符一样被使用, 可用于修饰包,, 构造器, 方法, 成员变量, 参数, 局部变量的声明, 这些信息被保存在 Annotation “name=value” 对中.

Annotation 能被用来为程序元素(, 方法, 成员变量等) 设置元数据

3个基本的 Annotation:

@Override: 限定重写父类方法, 该注解只能用于方法

@Deprecated:用于表示某个程序元素(, 方法等)已过时

@SuppressWarnings:抑制编译器警告.

 

自定义 Annotation定义新的 Annotation类型使用 @interface 关键字

Annotation指修饰AnnotationAnnotationJDK中定义了如下元Annotation

@Retention:只能用于修饰一个 Annotation定义, 用于指定该 Annotation 可以保留的域, @Rentention 包含一个 RetentionPolicy 类型的成员变量, 通过这个变量指定域。

RetentionPolicy.CLASS:编译器将把注解记录在 class 文件中. 当运行 Java 程序时, JVM 不会保留注解. 这是默认值

RetentionPolicy.RUNTIME:编译器将把注释记录在 class 文件中. 当运行 Java 程序时, JVM 会保留注解. 程序可以通过反射获取该注解

RetentionPolicy.SOURCE:编译器直接丢弃这种策略的注释

@Target:指定注解用于修饰类的哪个成员. @Target 包含了一个名为 value,类型为ElementType的成员变量。

@Documented: 用于指定被该元 Annotation 修饰的 Annotation 类将被 javadoc 工具提取成文档.

@Inherited: 被它修饰的 Annotation 将具有继承性.如果某个类使用了被 @Inherited 修饰的 Annotation, 则其子类将自动具有该注解

 

 

---------------------- android培训java培训、期待与您交流!----------------------


 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值