Android面试题总结

1.String转化为Integer的Integer.valueOf方法

public static Integer valueOf(String s) throws NumberFormatException {
        return Integer.valueOf(parseInt(s, 10));
    }

先通过parseInt方法把String转化为int类型,有个格式检查

public static int parseInt(String s, int radix)
                throws NumberFormatException
    {

如果格式正确,调用下面的方法

public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

如果i的值在[-128,127之间],会从缓存池里取,否则创建一个新的Integer对象。

2.Volatile关键字

Volatile关键字

3.HashMap相关

数据结构的分类:集合,线性结构,树形结构,图形结构

集合结构:除了同属于一种类型外,别无其它关系

线性结构:元素之间存在一对一关系常见类型有: 数组,链表,队列,栈,它们之间在操作上有所区别.例如:链表可在任意位置插入或删除元素,而队列在队尾插入元素,队头删除元素,栈只能在栈顶进行插
入,删除操作.

树形结构:元素之间存在一对多关系,常见类型有:树(有许多特例:二叉树、平衡二叉树、查找树等)

图形结构:元素之间存在多对多关系,图形结构中每个结点的前驱结点数和后续结点多个数可以任意
数组和链表的特点:

数组:查询快,增删效率低,增删会涉及到后面元素的移动

链表:查询效率低,因为要做全链表扫描,删除效率高,直接把前面的指向后面的

HashMap如何计算元素存放位置:(1.8)

 public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

我们先看hash方法

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);//尽可能的分散,减少碰撞概率
    }
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;//对数组进行初始化
        if ((p = tab[i = (n - 1) & hash]) == null)//判断数组当前下标位置是否有元素
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

获取当前位置的时候采用了

(n - 1) & hash

这种计算方式,为什么要采用这种方式呢?

首先可以保证计算出的位置不大于数组长度,其次可以减少碰撞概率,n代表数组的长度,源码中要求n必须是2的次幂,因为2的次幂减去1以后,得到的是类似0111这样的数,如果不是2的次幂,那么就会得到00001110这样的数,0与1和0都为0,计算出的位置可能是同一个点,这样就增加了碰撞的概率,

 final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        if (oldCap > 0) {
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        else {               // zero initial threshold signifies using defaults,第一次走这里
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;

resize方法用于对数组进行初始化或者扩容


HashMap的非线程安全解决:

1.HashTable:锁住整张hash表,让线程独占,hashtabl允许为空,synchronized是针对整张hash表的

2.ConcurentHashMap:一个更快的hashmap,提供了更好的并发性,多个读操作可以并发的执行,采用锁分段,默认把hash表分为16个段,在get,put,remove操作中只锁定当前需要的段,只有在求size时才锁定整个表。


2.HashSet总结

不保证元素的顺序,元素不可重复,HashSet底层是以HashMap来存储的,值是一个Object对象

private static final Object PRESENT = new Object();

我们重点看add方法

 public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

add方法其实是调用了HashMap的put方法,返回true,代表存入成功,如果元素已经存在(存在的判定是元素的hashCode和equals方法相同),则set集合不变,返回false(因为map集合存放元素时,如果键相同,值会覆盖,因为对HashSet来说,值是Object对象,所以即使覆盖了,HashSet中的元素还是没有变)

泛型

类型擦除与多态的冲突和解决方法

public class Pair<T> {//父类
    private T t;

    public void setValue(T t) {
        this.t = t;
    }

    public T getValue() {
        return t;
    }
}
public class Info extends Pair<Date> {//子类

    @Override
    public void setValue(Date date) {
        super.setValue(date);
    }

    @Override
    public Date getValue() {
        return super.getValue();
    }
}
public class GenericDemo {
    public static void main(String[] args) {
        Pair<Date> pair = new Info();
        pair.setValue(new Date());
    }
}
/*
* 1.类型擦除和多态的冲突
* 类型擦除以后,我们如果调用pair.setValue这句,应该调用Info的setValue方法,可是Info此时
* 有两个setValue方法,
* setValue(Date date)//自身的
* setValue(Object obj)//继承自Pair<Date>
* 那么应该调用哪个方法?
*
* 此时就要引出桥方法。
* JVM工作原理如下:
* 1.变量pair声明为Pair<Date>类型,该类型只有一个setValue(Object obj)方法,所以用pair指向的实际对象
* 去调用setValue(Object obj)这个方法
* 2.pair引用的对象是Info,所以会调用Info的setValue(Object obj)方法,这个方法是桥方法
*3.这个桥方法会调用Info的setValue(Date date)方法
* */

异常中不能使用泛型,因为类型擦除后会出现两个同样的异常

ArrayList总结
  //将数组转化为集合,该集合是只读的
        List<String> list = Arrays.asList("2", "3");
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("A");
        arrayList.add("B");
        arrayList.add("C");
        //将ArrayList转化为数组,new String[0]代表默认0个元素
        String[] strings = arrayList.toArray(new String[0]);
        for (String string : strings) {
            System.out.println(string);
        }

在初始化时,数组长度是空的,当通过add添加元素时,才会对数组长度进行初始化为默认长度10,具体代码如下

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;//元素添加到集合中,索引增加
        return true;
    }
 private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//默认true
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);//minCapacity此处为10
        }
        ensureExplicitCapacity(minCapacity);
    }
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code,当需要的容量大于数组长度的时候,就扩容,第一次扩容为默认的长度10
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);//增长容量
    }
private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;//oldCapacity为0
        int newCapacity = oldCapacity + (oldCapacity >> 1);//newCapacity为0
        if (newCapacity - minCapacity < 0)//默认为true
            newCapacity = minCapacity;//newCapacity设置为10
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);//返回指定长度的复制后的数组
    }

ArrayList不是同步的,同步要使用Vector或者CopyOnWriteArrayList,Collections.synchronizedList
public static <T> List<T> synchronizedList(List<T> list) {
        return (list instanceof RandomAccess ?
                new SynchronizedRandomAccessList<>(list) :
                new SynchronizedList<>(list));
    }
 static class SynchronizedList<E>
        extends SynchronizedCollection<E>
        implements List<E> {
        private static final long serialVersionUID = -7754090372962971524L;

        final List<E> list;

        SynchronizedList(List<E> list) {
            super(list);
            this.list = list;
        }
        SynchronizedList(List<E> list, Object mutex) {
            super(list, mutex);
            this.list = list;
        }

        public boolean equals(Object o) {
            if (this == o)
                return true;
            synchronized (mutex) {return list.equals(o);}
        }
        public int hashCode() {
            synchronized (mutex) {return list.hashCode();}
        }

        public E get(int index) {//对list的每个方法进行了同步代码块的包装
            synchronized (mutex) {return list.get(index);}
        }

还有一点必须注意,当使用iterator迭代Collections.synchronizedList返回的List时,需要手动的进行一下同步

 List list = Collections.synchronizedList(new ArrayList());
      synchronized (list) {
           Iterator i = list.iterator(); // Must be in synchronized block
           while (i.hasNext())
               foo(i.next());
       }

CopyOnWriteArrayList原理

线程安全的CopyOnWriteArrayList介绍

 Collections.synchronizedMap()

同样,在迭代的时候需要手动保持同步,如下

Map<Object, Object> map = Collections.synchronizedMap(new HashMap<>());
        Set<Object> keySet = map.keySet();
        synchronized (map) {//同步map,而不是keySet
            for (Object next : keySet) {
                System.out.println(next);
            }
        }
ConcurrentHashMap

 ConcurrentHashMap<String,String> concurrentHashMap = new ConcurrentHashMap<>();
        concurrentHashMap.put("A","北京");
        concurrentHashMap.put("B","西安");
        Set<Map.Entry<String, String>> entrySet = concurrentHashMap.entrySet();
        for(Map.Entry<String,String> entry : entrySet) {
            System.out.println(entry.getKey());
            System.out.println(entry.getValue());
        }

ConcurrentHashMap是线程安全的,

ConcurrentHashMap总结

信号量Semaphore(控制资源被并发访问的次数,同时可以实现监控多个线程是否全部执行完毕)

public class ThreadDemo {
    static Semaphore semaphore = new Semaphore(3);
    public static void main(String[] args) {
        Thread1 t1 = new Thread1();
        Thread2 t2 = new Thread2();
        Thread3 t3 = new Thread3();
        t1.start();
        t2.start();
        t3.start();
    }

    static class Thread1 extends Thread{
        @Override
        public void run() {
            try {
                semaphore.acquire();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程一执行完毕");
            semaphore.release();
            checkFinish(semaphore.availablePermits());
        }
    }
    static class Thread2 extends Thread{
        @Override
        public void run() {
            try {
                semaphore.acquire();
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程二执行完毕");
            semaphore.release();
            checkFinish(semaphore.availablePermits());
        }
    }
    static class Thread3 extends Thread{
        @Override
        public void run() {
            try {
                semaphore.acquire();
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程3执行完毕");
            semaphore.release();
            checkFinish(semaphore.availablePermits());
        }
    }

    static void checkFinish(int count){
        if (count == 3) {
            System.out.println("所有线程执行完毕");
        }
    }
LinkedList

保证了元素的插入顺序,底层是双向链表,每个节点保存了自己上一个和下一个节点

private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }
void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);//创建一个新节点,last是上一个节点
        last = newNode;
        if (l == null)//如果上一个节点没有,说明是头结点
            first = newNode;
        else
            l.next = newNode;//上一个有节点,新节点做为下一个节点
        size++;
        modCount++;
    }
TreeSet
保证了元素的顺序,如果元素没有实现Comparable接口,则会抛出类型转化异常
 public static void main(String[] args) {
        //第一种,元素必须实现Comparable接口,并且实现compareTo方法
      /*  TreeSet<Person> treeSet = new TreeSet<>();
        treeSet.add(new Person("a",2));
        treeSet.add(new Person("b",1));
        treeSet.add(new Person("c",3));
        treeSet.add(new Person("d",4));
        for(Person i : treeSet) {
            System.out.println(i.getAge());
        }*/
      //第二种,TreeSet的构造方法中传入Comparator实现类,实现compare方法排序
        TreeSet<Person> treeSet2 = new TreeSet<>(new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getAge() - o2.getAge();
            }
        });
    }



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值