美文网首页
Java基础

Java基础

作者: syxyabc | 来源:发表于2018-09-26 21:53 被阅读0次

1、List 和 Set 的区别

1.1 异同点

首先List最重要的特点是能保证数据存入的顺序性,而set由于是根据hash映射的地址,因此是无序的。
其次,list可以存储相同的内容,允许多个null值,set不允许数据重复,只允许一个null值。

List和set都是集成Collection接口的。
List包括,ArrayList、LinkedList、Vector.其中ArrayList类似于数组,可以随意访问,适合查找频繁的场景,而LinkedList则适合插入删除频繁的场景。
Set包括,HashSet,TreeSet,LinkedSet;HashSet是基于HashMap实现的,TreeSet可以对保存的数据自动排序。

1.2、List、Set的使用场景

如果知道数据的索引位置,那可以用List中的ArrayList,可以很快的查找到数据。如果经常的增删数据则用LinkedList。
如果希望容器中的数据按照插入顺序进行存储,比如最近访问列表等,需要按照时间的先后顺序存储的,就可以用List。
如果是希望容器中的数据是不重复的,只保留一份,那就使用set,其中TreeSet还支持对存入数据进行排序。

2、HashSet 是如何保证不重复的

HashSet是由HasnMap实现的,可以直接看源码中HashSet的构造函数,直接new了一个hashMap

  public HashSet() {
        map = new HashMap<>();
    }

所以,HashSet和HashMap的去重方式是一样的,可以看一下HashSet的add方法,直接调用了hasMap.put
方法,如下:

 public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {//主要判重逻辑
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

可以看到上面的主要判重逻辑代码,首先,判断了原有元素e.hash与新传入key的hash,e是已存在的键值对中的元素,e.hash实际就是已经存在的key值的hash值。判断完后,判断key是否相同。如果重复,则用新的值替换旧的值。

3、HashMap 是线程安全的吗,为什么不是线程安全的(最好画图说明多线程环境下不安全)?

 HashMap不是线程安全的。
详细说明为什么会出现线程不安全->传送门
 线程不安全主要是由于HashMap有扩容机制,HashMap的初始大小是16,如果超过了16,就会进行扩容,在扩容的过程中,如果两个线程同时达到了需要进行rehash的时候,会对链表进行重排,此时,在取链表数据时,可能会发生链表循环。以下是源码:

 void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }

        Entry[] newTable = new Entry[newCapacity];
        boolean oldAltHashing = useAltHashing;
        useAltHashing |= sun.misc.VM.isBooted() &&
                (newCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
        boolean rehash = oldAltHashing ^ useAltHashing;
        transfer(newTable, rehash);//此处会进行数据重新存放到一个新的数组里面去。
        table = newTable;
        threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
    }

    /**
     * 如果两个线程同时到达该方法,此时对于两个线程来说newTable是固定,且都保存一份数组
     * 如果其中一个线程正确执行完,另外一个线程就有可能造成链表环。
     */
    void transfer(Entry[] newTable, boolean rehash) {
        int newCapacity = newTable.length;
        for (Entry<K,V> e : table) {
            while(null != e) {
                Entry<K,V> next = e.next;
                if (rehash) {
                    e.hash = null == e.key ? 0 : hash(e.key);
                }
                int i = indexFor(e.hash, newCapacity);
                e.next = newTable[i];
                newTable[i] = e;
                e = next;
            }
        }
    }

4、HashMap 的扩容过程

hashmap有threshold,如果当前数组容量大于threshold,就会进行数组扩容,扩容大小是当前数组长度的2倍。
threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
阈值等于 容量乘以负载因子与最大容量中较小的值。
数组扩大后,会对原有hashmap重新hash并定位。

5、HashMap 1.7 与 1.8 的 区别,说明 1.8 做了哪些优化,如何优化的?

hashMap1.8在hashMap的链表中增加了红黑树,如果链表长度超过了8,就会调用方法把 链表转换为红黑树,如果链表长度小于

6、final finally finalize

final是修饰符,可以修饰在类上,表示该类不能被继承;修饰在方法上,表示该方法不能被重写,但是可以重载;修饰在变量上面,如果是基本类型,值不能变,是对象的话,表示该变量引用不能被修改。
finally 是异常捕获时候使用的,一般在finally中执行关闭资源操作,即使在try中return了,也会执行finally中的代码。实际finally是在return前执行的。
finalize是垃圾回收时显示声明用的,finallize方法调用了,不一定会执行gc过程。

7、强引用 、软引用、 弱引用、虚引用

强引用:
Object o=new Object();
这是最常见的实例化对象的方法,这种方法就是强引用,除非设置 o = null;否则在垃圾回收时不会被清理。
软引用:
SoftReference:
会在内存溢出之前进行一次检查,将所有软引用内存释放。一般软引用可以用来做临时缓存;应用场景比如,需要读取经常使用的图片,图片可以缓存在内存中,加快读取速度,也可以从远程下载,放在内存中的话,因为图片size比较大,容易引起OOM,可以使用softReference,在发生内存溢出时才清理。此时如果还要请求图片,才重新从远程去下载图片。
用法:

Object o=new Object();  
SoftReference<Object> softRef = new SoftReference<Object>(o);

弱引用:
在gc之前,清除弱引用内存。主要用来检测对象是不是被标记为可回收对象。

import java.lang.ref.WeakReference;

public class Main {
    public static void main(String[] args) {

        WeakReference<String> sr = new WeakReference<String>(new String("hello"));

        System.out.println(sr.get());
        System.gc();                //通知JVM的gc进行垃圾回收
        System.out.println(sr.get());
    }
}
  输出结果为:
hello
null

虚引用就像名字一样是一个虚的,只要执行垃圾回收都会把他回收掉。虚引用的get方法永远获取的都是null值,主要用来判断这个对象是否已经从内存里面删除了。

相关文章

  • Java 基础

    Java 基础01Java开发入门 Java 基础02Java编程基础 Java 基础03面向对象 Java 基础...

  • 技术体系

    一,java核心 java基础,jvm,算法,多线程,设计模式 Java基础:java基础相关,全栈java基础 ...

  • 面试题汇总

    1.Java基础面试问题 Java基础之基础问题 Java基础之面向对象 Java基础之数据结构 Java基础之I...

  • 【Android】知识点汇总,坚持原创ing

    Android基础 Java基础 Java基础——Java内存模型和垃圾回收机制 语法基础 语法基础——C语法基础...

  • Java基础:反射

    反射注解动态代理相关阅读 Java基础:类加载器 Java基础:反射 Java基础:注解 Java基础:动态代理 ...

  • Java基础:注解

    系列阅读 Java基础:类加载器 Java基础:反射 Java基础:注解 Java基础:动态代理 1. 概述 注解...

  • Java基础:动态代理

    系列阅读 Java基础:类加载器 Java基础:反射 Java基础:注解 Java基础:动态代理 概述 在运行时,...

  • Java 集合类原理

    Java基础——HashMap源码分析 Java基础——HashSet源码分析 Java基础——HashTable...

  • Java基础:类加载器

    系列阅读 Java基础:类加载器 Java基础:反射 Java基础:注解 Java基础:动态代理 1. 什么是类加...

  • java基础(一)-String、StringBuffer、St

    java基础-String、StringBuffer、StringBuilder java基础小白,初学java,...

网友评论

      本文标题:Java基础

      本文链接:https://www.haomeiwen.com/subject/cmuvgftx.html