图片 2

Java集合总结,集合框架

最近工作时遇到个关于动画的问题,如下:

前言

声明,本文用的是jdk1.8

花了一个星期,把Java容器核心的知识过了一遍,感觉集合已经无所畏惧了!!(哈哈哈….),现在来总结一下吧~~

回顾目录:

  • Collection总览
  • List集合就这么简单【源码剖析】
  • Map集合、散列表、红黑树介绍
  • HashMap就是这么简单【源码剖析】
  • LinkedHashMap就这么简单【源码剖析】
  • TreeMap就这么简单【源码剖析】
  • ConcurrentHashMap基于JDK1.8源码剖析
  • Set集合就这么简单!

Java容器可分为两大类:

  • Collection
    • List
      • ArrayList
      • LinkedList
      • Vector(了解,已过时)
    • Set
      • HashSet
        • LinkedHashSet
      • TreeSet
  • Map
    • HashMap
      • LinkedHashMap
    • TreeMap
    • ConcurrentHashMap
    • Hashtable(了解,,已过时)

着重标出的那些就是我们用得最多的容器。

其实,我也不知道要怎么总结好,因为之前写每一篇的时候都总结过了。现在又把他们重新罗列出来好像有点水,所以,我决定去回答一些Java容器的面试题!

当然了,我的答案未必就是正确的。如果有错误的地方大家多多包含,希望不吝在评论区留言指正~~

一、内部类

[javascript] view
plain copy

一、ArrayList和Vector的区别

共同点:

  • 这两个类都实现了List接口,它们都是有序的集合(存储有序),底层是数组。我们可以按位置索引号取出某个元素,允许元素重复和为null

区别:

  • 同步性:
    • ArrayList是非同步的
    • Vector是同步的
    • 即便需要同步的时候,我们可以使用Collections工具类来构建出同步的ArrayList而不用Vector
  • 扩容大小:
    • Vector增长原来的一倍,ArrayList增长原来的0.5倍

 1.成员内部类。

 

二、HashMap和Hashtable的区别

共同点:

  • 从存储结构和实现来讲基本上都是相同的,都是实现Map接口~

区别:

  • 同步性:
    • HashMap是非同步的
    • Hashtable是同步的
    • 需要同步的时候,我们往往不使用,而使用ConcurrentHashMapConcurrentHashMap基于JDK1.8源码剖析
  • 是否允许为null:
    • HashMap允许为null
    • Hashtable不允许为null
  • contains方法
    • 这知识点是在牛客网刷到的,没想到这种题还会有(我不太喜欢)….
    • Hashtable有contains方法
    • HashMap把Hashtable的contains方法去掉了,改成了containsValue和containsKey
  • 继承不同:
    • HashMap<K,V> extends AbstractMap<K,V>
    • public class Hashtable<K,V> extends Dictionary<K,V>

(1)成员内部类的实例化:

  1. $(“div”).hover(  
  2.     function() {  
  3.         $(this).animate({“margin-top”:”100px”},1000);  
  4.     }, function() {  
  5.         $(this).animate({“margin-top”:”200px”},1000);  
  6.     });  

三、List和Map的区别

共同点:

  • 都是Java常用的容器,都是接口(ps:写出来感觉好像和没写一样…..)

不同点:

  • 存储结构不同
    • List是存储单列的集合
    • Map存储的是key-value键值对的集合
  • 元素是否可重复
    • List允许元素重复
    • Map不允许key重复
  • 是否有序
    • List集合是有序的(存储有序)
    • Map集合是无序的(存储无序)

外部类名.内部类名   变量名=外部类对象.new 内部类名();

 

四、Set里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用==还是equals()?

我们知道Set集合实际大都使用的是Map集合的put方法来添加元素

以HashSet为例,HashSet里的元素不能重复,在源码(HashMap)是这样体现的:

    // 1. 如果key 相等  
    if (p.hash == hash &&
        ((k = p.key) == key || (key != null && key.equals(k))))
        e = p;
    // 2. 修改对应的value
       if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
       }

添加元素的时候,如果key(也对应的Set集合的元素)相等,那么则修改value值。而在Set集合中,value值仅仅是一个Object对象罢了(该对象对Set本身而言是无用的)。

也就是说:Set集合如果添加的元素相同时,是根本没有插入的(仅修改了一个无用的value值)!从源码(HashMap)中也看出来,==和equals()方法都有使用

class Person{
  class Test{
   }
 }
 Person p=new Person();
 Person.Test t=p.new Test();

看代码就是一个简单的鼠标滑过的动画而已,但是当我测试的时候发现,当我发神经似的来回滑动时,事件就被触发了多次,动画也就重复了多次,怎么才能不重复出现,即动画过程中,鼠标滑过这个div,不会触发该事件那?

五、Collection和Collections的区别

  1. Collection是集合的上级接口,继承它的有Set和List接口
  2. Collections是集合的工具类,提供了一系列的静态方法对集合的搜索、查找、同步等操作

(2)在内部类中访问外部类属性或方法。

带着疑问,google之,

六、说出ArrayList,LinkedList的存储性能和特性

ArrayList的底层是数组,LinkedList的底层是双向链表。

  • ArrayList它支持以角标位置进行索引出对应的元素(随机访问),而LinkedList则需要遍历整个链表来获取对应的元素。因此一般来说ArrayList的访问速度是要比LinkedList要快的
  • ArrayList由于是数组,对于删除和修改而言消耗是比较大(复制和移动数组实现),LinkedList是双向链表删除和修改只需要修改对应的指针即可,消耗是很小的。因此一般来说LinkedList的增删速度是要比ArrayList要快的

  外部类名.this.属性名
   Person.this.name
(3)成员内部类声明的注意事项
  ①内部类不能与外部类重名。
  ②成员内部类中不能出现静态属性,静态方法和静态内部类。
     但是静态常量例外。
 private static final String name=”1″;//此情况可以使用

发现,原来为元素绑定hover事件之后,如果光标移入移出的速度太快,导致移入的动画还没执行完,就移出光标,则移出的动画效果就会被放到队列,等移入的动画完成后在执行。因此如果光标的移入移出速度太快,就会导致动画效果与移动光标不一致,出现重复出现的情况。

6.1扩展:

ArrayList的增删未必就是比LinkedList要慢。

  • 如果增删都是在末尾来操作【每次调用的都是remove()和add()】,此时ArrayList就不需要移动和复制数组来进行操作了。如果数据量有百万级的时,速度是会比LinkedList要快的。(我测试过)
  • 如果删除操作的位置是在中间。由于LinkedList的消耗主要是在遍历上,ArrayList的消耗主要是在移动和复制上(底层调用的是arraycopy()方法,是native方法)。
    • LinkedList的遍历速度是要慢于ArrayList的复制移动速度的
    • 如果数据量有百万级的时,还是ArrayList要快。(我测试过)
class Test{//成员内部类
        private  String  name;
        private int age;
        public void say(){
            System.out.println(Person.this.name);//访问外部类的
            System.out.println("Person中的Test的say方法");
        }
    }

而解决办法有两种:

七、Enumeration和Iterator接口的区别

这个我在前面的文章中也没有详细去讲它们,只是大概知道的是:Iterator替代了Enumeration,Enumeration是一个旧的迭代器了。

与Enumeration相比,Iterator更加安全,因为当一个集合正在被遍历的时候,它会阻止其它线程去修改集合

  • 我们在做练习的时候,迭代时会不会经常出错,抛出ConcurrentModificationException异常,说我们在遍历的时候还在修改元素。
  • 这其实就是fail-fast机制~具体可参考博文:

区别有三点:

  • Iterator的方法名比Enumeration更科学
  • Iterator有fail-fast机制,比Enumeration更安全
  • Iterator能够删除元素,Enumeration并不能删除元素

2.静态内部类。

 

八、ListIterator有什么特点

  • ListIterator继承了Iterator接口,它用于遍历List集合的元素
  • ListIterator可以实现双向遍历,添加元素,设置元素

看一下源码的方法就知道了:

图片 1

(1)使用static修饰的内部类,叫做静态内部类。

1、你可以使用jq的stop方法:

九、并发集合类是什么?

Java1.5并发包(java.util.concurrent)包含线程安全集合类,允许在迭代时修改集合

  • 迭代器被设计为fail-fast的,会抛出ConcurrentModificationException。
  • 一部分类为:
    • CopyOnWriteArrayList
    • ConcurrentHashMap
    • CopyOnWriteArraySet

(2)实例化:外部类名.内部名 变量=new 外部类名.内部类名();

$(div).stop(false, true).animate({'margin-top':'100px'},1000);

十、Java中HashMap的key值要是为类对象则该类需要满足什么条件?

需要同时重写该类的hashCode()方法和它的equals()方法

  • 从源码可以得知,在插入元素的时候是先算出该对象的hashCode。如果hashcode相等话的。那么表明该对象是存储在同一个位置上的。
  • 如果调用equals()方法,两个key相同,则替换元素
  • 如果调用equals()方法,两个key不相同,则说明该hashCode仅仅是碰巧相同,此时是散列冲突,将新增的元素放在桶子上

一般来说,我们会认为:只要两个对象的成员变量的值是相等的,那么我们就认为这两个对象是相等的!因为,Object底层比较的是两个对象的地址,而对我们开发来说这样的意义并不大~这也就为什么我们要重写equals()方法

重写了equals()方法,就要重写hashCode()的方法。因为equals()认定了这两个对象相同,而同一个对象调用hashCode()方法时,是应该返回相同的值的!

class Person{
     public String name="1";
     public Static int age="12";
         static class Test{
     } 
      }
      Person.Test t=new Person.Test();

如果stop()的第一个参数为true,表示立即清除当前的动画队列,默认为fx;如果第二个参数为true,表示立即将当前正在执行的动画置为它的结束状态。

十一、与Java集合框架相关的有哪些最好的实践

  1. 根据需要确定集合的类型。如果是单列的集合,我们考虑用Collection下的子接口ArrayList和Set。如果是映射,我们就考虑使用Map~
  2. 确定完我们的集合类型,我们接下来确定使用该集合类型下的哪个子类~我认为可以简单分成几个步骤:
    • 是否需要同步
      • 去找线程安全的集合类使用
    • 迭代时是否需要有序(插入顺序有序)
      • 去找Linked双向列表结构的
    • 是否需要排序(自然顺序或者手动排序)
      • 去找Tree红黑树类型的(JDK1.8)
  3. 估算存放集合的数据量有多大,无论是List还是Map,它们实现动态增长,都是有性能消耗的。在初始集合的时候给出一个合理的容量会减少动态增长时的消耗~
  4. 使用泛型,避免在运行时出现ClassCastException
  5. 尽可能使用Collections工具类,或者获取只读、同步或空的集合,而非编写自己的实现。它将会提供代码重用性,它有着更好的稳定性和可维护性

   注意与成员内部类实例化的区别。

延伸:(看来我真的得好好看看jq的api了)

十二、ArrayList集合加入1万条数据,应该怎么提高效率

ArrayList的默认初始容量为10,要插入大量数据的时候需要不断扩容,而扩容是非常影响性能的。因此,现在明确了10万条数据了,我们可以直接在初始化的时候就设置ArrayList的容量

这样就可以提高效率了~

(3)静态内部类中不能访问外部类的非静态属性和方法。

停止元素的动画:stop([cleanQueue,
gotoEnd]):第一个参数代表是否要清空未执行完的动画队列,第二个参数代表是否直接将正在执行的动画跳转到末状态。(可缺省)。

十三、总结

2018年4月15日17:14:03,上面找了一些面试题答了一下,感觉不够过瘾呀。很多我觉得比较重要的知识点我都没有找到对应的面试题(可能我搜索的能力太水了?)。

将这篇文章作为集合的总结篇,但觉得没什么好写就回答一些面试题去了,找了一会面试题又觉得不够系统。而这篇总结我又不想复制前面的章节总结到这里来。于是我决定画一个脑图来结束这篇文章

2018年4月15日19:31:33 画完啦!!!!!

图片 2

需要更多脑图的同学可关注公众号:Java3y,回复【脑图】即可~

如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:Java3y。为了大家方便,刚新建了一下qq群:742919422,大家也可以去交流交流。谢谢支持了!希望能多介绍给其他有需要的朋友

文章的目录导航

目前初步打算写多线程,你们觉得怎么样呢?可以在评论区留言~

   但可以访问外部类中的静态属性和方法,使用外部类名.属性名;
     Person.age;

(1)无参数stop():立即停止当前的动画,如果接下来还有动画则以当前状态开始接下来的动画。

(4)外部类不能访问静态内部类的非静态属性和方法,但可以访问静态内部类的的静态属性和方法,外部类名.属性名。

(2)stop(false, true) : 当前的动画直接达到末状态。

    Test.age;

(3)stop(true, true) :
当前的动画直接达到末状态并清空当前对象的动画队列。

(5)【静态内部类和成员内部类的区别】

注意:jQuery只能设置正在执行的动画的最终状态,不能直接达到未执行动画的最终状态。

  1.声明方式和实例化方式不同。成员内部类不带static
     静态内部类实例化: Person.Test t=new Person.Test();
     成员内部类实例化: Person.Test t=p.new Test();
  2.成员内部类中不能出现静态属性和方法,但静态内部类可以。
  3.外部类和内部类的互相访问权限不同:
    ①静态内部类中不能访问外部类的非静态属性和方法。
    但可以访问外部类中的静态属性和方法,使用外部类名.属性名;
    ②成员内部类中不能出现静态属性,静态方法和静态内部类。
    但是静态常量例外。

2、执行动画前判断一下:

static class Test1{
        public static String test="111";

        public void say(){
            System.out.println("Test1");
        }
    }
if (!$(obj).is(':animated')) {
    // to do something
}

3.【局部内部类】(一般不用)。

(1)定义在某个类中的方法中的内部类,称为局部内部类。
(2)局部内部类是对外部完全隐藏的,只能在其作用域范围内被实例化。
(3)局部内部类可以访问外部类的属性和方法,使用外部类.this.属性名;
  Person.this.name;
(4)局部内部类:不能访问其所在方法中的变量,只能访问常量。
(5)注意:局部内部类不能使用访问修饰符修饰,因为它不属于类的成员,它属于方法中的局部变量。

public void eat(){
//        final String name1="123";
        class Test2{
            public String name="123";
            public void test(){
                System.out.println(Person.this.name);
//                System.out.println(name1);
            }
        }
        Test2 t=new Test2();
        System.out.println(t.name);
    }

}

4.匿名内部类。

new Test1(){

 };
(1)写法
(2)含义:相当于一个匿名类继承了Test1类,使用匿名内部类将直接返回当前子类的对象。
Test1 t=new Test1(){};
(3)相当于:①一个匿名内部类,继承了Test1类。
        ②匿名内部类返回一个子类对象,并付给父类引用。
    因此这一行代码用于向上转型。
(4)匿名内部类通常用于直接实例化抽象类或接口。
  Test1 t=new Test1(){
     //重写抽象类Test1中的所有的抽象方法
    //相当于一个匿名内部类,继承了Test1类,然后返回这个匿名内部类的对象。
};

Test1 t2=new Test1(){
            public int age=12;
            public void eat(){
                System.out.println(age);
                System.out.println("eat");
            }
            public void say(){
                System.out.println("匿名重写Test1类的say方法");
            }
        };
        //向上转型,会丢失掉子类特有的属性和方法,new Test1(){}.eat();
        t2.say();

5.使用内部类模拟多继承 。

class A{
    public void a() {
        System.out.println("a");
    }
}
class B{
    public void b(){
        System.out.println("b");
    }
}
class C{
    class A1 extends A{

    }
    class B1 extends B{

    }
    public  void a(){
        new A1().a();//不管A类是不是抽象类都可以,如果是A则要考虑是不是抽象类
    }
    public void b(){
        new B1().b();
    }
}

C c=new C();  //结果a,b
c.a();
c.b();

二、接口

 1.【接口的定义】

(1)接口名/接口文件与类类似,接口也是使用.java文件编写。

(2)声明接口的关键字:interface,接口名命名规范与类名相同,习惯上,接口可以用I开头表示。

(3)接口的访问修饰符只能使用public和default修饰,不能使用protected和private(与外部类相同)。

(4)接口中的所有的属性,只能是公共的,静态的,常量。

   public static final int NAME=1;

  而且,public/static/final都可以省略,省略后依然是公共的,静态的常量。

  int NAME=1;

(5)接口中所有的方法必须是公共的抽象方法。

  而且,接口中的抽象方法,也可以省略public/abstract关键字。

  (public abstract) void say();

 2.【实现类实现接口】

(1)一个类实现接口使用implements关键字

(2)实现类实现一个接口,必须重写接口中的所有抽象方法,除非这个类是抽象类。

   一个类可以使用多个接口,多个接口之间,使用逗号隔开。

   class Desk implements IUsb2,3()

   一个类如果实现多个接口,必须重写所有接口中的所有方法。

(3)接口的引用可指向其实现类的对象(类似于父类引用指向子类对象)

  IUsb2 usb=new UDesk();

  因此可以使用接口实现多态。

 3.【接口继承接口】

(1)接口可以继承接口,使用extends关键字,接口继承与类的接口一样。

interface IUsb3 extends IUsb2{}

(2)接口可以多继承,多个父接口用逗号分隔,子接口继承多个父接口,将拥有所有父接口的抽象方法。

interface IUsb3 extends IUsb2,IUsb1{}

(接口不能被实例化)

4.【接口和抽象类的区别】 

 它们的本质区别:

  抽象类是一个类,接口是一个接口,子类继承抽象类,要求子类必须和父类是同一类事物,必须符合is-a关系。

  接口只是对功能的扩展。

  多个实现类实现接口时,并不要求所有的实现类是一类事物。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

标签:
网站地图xml地图