CharlesXiao‘s Blog

  • 首页

  • 归档

  • 分类

  • 标签

  • 关于

  • 搜索

Roman与Integer之间的相互转换

发表于 2016-01-13 | 更新于 2016-01-15 | 字数统计604字 | 阅读时长2分钟 | 分类于 Leetcode

罗马数字与阿拉伯数字之间的简单转换方法,首先需要了解罗马数字计数法,在这两道转换题里我们只用到了前三项规则

  1. 罗马数字共有7个,即I(1)、V(5)、X(10)、L(50)、C(100)、D(500)和M(1000)

  2. 右加左减: 较大的罗马数字的右边记上较小的罗马数字,表示大数字加小数字; 较大的罗马数字的左边记上较小的罗马数字,表示大数字减小数字

  3. 左减有一定限制: 只有I, X, C可以放到左边当做减数使用,而且左减不能越位,I的右边只能是VX, X右边只能是LC, C的右边只能是DM; 左减数字必须为一位
  4. 其他规则: 加线乘千、重复数次、右加数字不可连续超过三位、同一数码最多只能连续出现三次

Roman to Integer

解题思路: 对于每一个roman字符,除了I,X,C三种情况会出现减去对应数值,其他情况都是加上该字符对应的数值; 针对I、X、C,我们可以总结发现只需要考察他们的下一位是否满足减的条件,是则减,否则加。

public class Solution {
    public int romanToInt(String s) {
        int res = 0;
        int len = s.length();
        for (int i=0; i<len; i++) {
            char tmp = s.charAt(i);
            switch (tmp) {
                case 'I':
                    if (i+1 <len && (s.charAt(i+1) == 'V' || s.charAt(i+1) == 'X')) {
                        res -= 1;
                        break;
                    }
                    res += 1;
                    break;
                case 'V':
                    res += 5;
                    break;
                case 'X':
                    if (i+1 <len && (s.charAt(i+1) == 'L' || s.charAt(i+1) == 'C')) {
                        res -= 10;
                        break;
                    }
                    res += 10;
                    break;
                case 'L':
                    res += 50;
                    break;
                case 'C':
                    if (i+1 <len && (s.charAt(i+1) == 'D' || s.charAt(i+1) == 'M')) {
                        res -= 100;
                        break;
                    }
                    res += 100;
                    break;
                case 'D':
                    res += 500;
                    break;
                case 'M':
                    res += 1000;
                    break;
                default:
                    break;
            }
        }
        return res;
    }
}

Integer To Roman

解题思路: 对于输入的数值进行取商处理,从大的计数单位到小的计数单位依次取商, 商则是该计数单位对应的roman字符的个数。

public class Solution {
    public String intToRoman(int num) {
        // igr存储的是从大到小每一个基本计数单位,与roman一一对应
        String[] roman = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
        int[] igr = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
        StringBuilder res = new StringBuilder();
        for (int i=0; i<roman.length; i++) {
            int quotient = num / igr[i];
            for (int j=0; j<quotient; j++) {
                res.append(roman[i]);
            }
            num -= quotient * igr[i];
        }
        return res.toString();
    }
}

Google Compute Engine配置LAMP环境以及部署Web项目

发表于 2015-12-24 | 字数统计0字 | 阅读时长1分钟

Java容器类库的学习研究

发表于 2015-10-20 | 更新于 2016-08-17 | 字数统计3.6k字 | 阅读时长13分钟 | 分类于 java学习笔记

Comparable和Comparator接口区别

  1. Comparable & Comparator 都是用来实现集合中元素的比较、排序的,只是 Comparable 是在集合内部定义的方法实现的排序,Comparator 是在集合外部实现的排序,所以,如想实现排序,就需要在集合外定义 Comparator 接口的方法或在集合内实现 Comparable 接口的方法
  2. Comparable:一个实现了comparable接口的对象的实例可以被用于和相同类的不同实例做对比。它本身必须实现java.lang.Comparable的接口,这样它就拥有了对比的能力;java.util.Collections.sort(List) 和 java.util.Arrays.sort(Object[]) 方法被用来排列使用内在排序(natural ordering)方式的对象.

  3. Comparator:一个实现了comparator接口的对象能够对比不同的对象。它不能用于同一个类的不同实例的对比,但是可以用于其他的类的实例做对比。它必须实现java.util.Comparator的接口;java.util.Collections.sort(List, Comparator) 和 java.util.Arrays.sort(Object[], Comparator)方法在Comparator如果可供比较的时候会被用到.

  4. 两者的接口实现差异:
    java.lang.Comparable: int compareTo(Object o1)

    当前对象与o1对象做对比,返回int值:

    positive – 当前对象大于o1

    zero – 当前对象等于o1

    negative – 当前对象小于o1

    java.util.Comparator: int compare(Object o1, Objecto2)

    用于o1与o2对象做对比,返回int值:

    positive – o1大于o2

    zero – o1等于o2

    negative – o1小于o2

  5. 实例:
    内在ID顺序排序

    public class Employee implements Comparable<Employee> {
        private int empId;
        private String name;
        private int age;
    
        /**
        * Compare a given Employee with this object.
        * If employee id of this object is 
        * greater than the received object,
        * then this object is greater than the other.
        */
        public int compareTo(Employee o) {
            return this.empId - o.empId ;
        }
    ….
    }
    
按照name排序

    public class EmpSortByName implements Comparator<Employee>{

        public int compare(Employee o1, Employee o2) {
            return o1.getName().compareTo(o2.getName());
        }
    }     

Collections.sort()与Arrays.sort()

  1. Arrays.sort()在其内部实现上针对数组元素的不同类型采用不同的排序方式: ①基本类型数据(int, short, long, float, double等)采用QuickSort, 而对对象类型采取MergeSort. Why? 我们知道QuickSort在统计意义上效率比MergeSort高,为何不都采用 QuickSort ?
    Answer: 概括的说,一个是稳定性,一个是移动次数。使用不同类型的排序算法主要是由于 quick sort 是不稳定的,而 merge sort 是 stable 的。这里的 stable 是指比较相等的数据在排序之后仍然按照排序之前的前后顺序排列(保序性)。对于基本数据类型,稳定性没有意义。而对于对象类型,稳定性是比较重要的,因为对象相等的判断可能只是判断关键属性,最好保持相等对象的非关键属性的顺序与排序前一直;另外一个原因是由于合并排序相对而言比较次数比快速排序少,移动(对象引用的移动)次数比快速排序多,而对于对象来说,比较一般比移动耗时。

    MergeSort:()1)最坏时间复杂度是 O(nlgn);(2)平均时间复杂度是 O(nlgn);(3)空间复杂度是 O(n)。

    QuickSort: (1)最坏时间复杂度是 O(n^2);(2)平均时间复杂度是 O(nlgn);(3)空间复杂度最坏是 O(n),均匀就是O(logn)。

  1. Collections.sort()主要是针对集合的排序,比如List, 其内部实现如下:

    public static <T extends Comparable<? super T>> void sort(List<T> list) {
        Object[] a = list.toArray();
        Arrays.sort(a);
        ListIterator<T> i = list.listIterator();
        for (int j=0; j<a.length; j++) {
            i.next();
            i.set((T)a[j]);
        }
    }
    

    可以看出其内部实际采用的就是Arrays.sort()

HashMap与LinkedHashMap,TreeMap

  1. HashMap实现原理内部对键值对的存储结构使用的是数组+链表+红黑树的形式(JDK1.8增加了红黑树部分,当一个链表长度>8就转换为红黑树,利用红黑树快速增删改查的特点提高HashMap的性能),每一个数组元素都是一个键值对,分别都对应着一个链表结构

    • put存储元素HashMap在存储Entry对象的时候,首先根据Key的hashCode()方法得到其hashCode值,然后根据高位运算和取模运算来判定将存储到Entry[]数组的哪一个位置对应的链表上O(1),然后调用key的Equals方法去遍历链表比较key值,如果找到则更新value并返回旧址,否则将新的Entry加入到该链表头部
    • 内部结构图:
    • 遍历顺序也就是说Entry对象随机地分配到某个Entry[] table数组的元素表示的链表上, HashMap内部Entry的遍历顺序是对Entry[] table 数组,从index=0开始,依次遍历table[i]上的链表上的Entry对象;所以对HashMap遍历Entry对象的顺序和Entry对象的存储顺序之间没有任何关系.
    • 重新hash调节大小HashMap允许null键插入,其内部会将null存到table[0]中进行处理. 内部有负载因子和数组长度、size等参数,当map中元素达到16*0.75=12时,因为链表长度可能很长导致查找效率降低,hashmap会对原来的所有元素重新进行hash,数组容量变为原来的2倍,当然Java里的数组是无法自动扩容的,方法是使用一个新的数组代替已有的容量小的数组;重新hash时链表顺序会被反转,可以避免尾部遍历(1.8中不会倒置,也不会重新计算哈希).HashMap; 在HashMap调整大小时会出现条件竞争,也就是多个线程同时来调整大小,出现死循环.如果内存空间很多而又对时间效率要求很高,可以降低负载因子Load factor的值;相反,如果内存空间紧张而对时间效率要求不高,可以增加负载因子loadFactor的值,这个值可以大于1。
    • key不可变Map的key值应该使用不可变对象,以防put和get的时候对象发生变化从而导致hashcode变化,取不到正确的对象;HashMap也可以声明为同步的,使用Map m = Collections.synchronizeMap(hashMap);
    • Fail-Fast机制:在使用迭代器的过程中有其他线程修改了map结构,那么将抛出ConcurrentModificationException,这就是所谓fail-fast机制。这一机制在源码中的实现是通过modCount域,modCount顾名思义就是修改次数,对HashMap元素个数的修改都将增加这个值;在迭代器初始化过程中会将这个值赋给迭代器的expectedModCount。在迭代过程中,判断modCount跟expectedModCount是否相等,如果不相等就表示已经有其他线程修改了Map中元素个数.例如:HashMap在foreach迭代器循环遍历时如果其他线程对该map进行结构上的更改(比如进行add和map.remove元素操作,而不是简单的修改集合元素的内容)会导致ConcurrrentModificationEaception
    • 遍历区别①for循环遍历时删除或者增加某个元素只会导致数组越界(rangeCheck函数),或者一直add生成无穷序列以及遍历不完整 ②使用Iterator遍历或者foreach遍历时如果采用iterator.remove()不会报错,因为他会重新设置expectedModCount=modCount,如果采用list.remove()则会出现ConcurrrentModificationException
  2. LinkedHashMap是HashMap的子类,它可以实现对容器内Entry的存储顺序和对Entry的遍历顺序保持一致; LinkedHashMap内部使用了一个Entry类型的双向链表,用这个双向链表记录Entry的存储顺序。当需要对该Map进行遍历的时候,实际上是遍历的是这个双向链表。

    内部结构图:

    遍历方式:

    //构造函数的默认值为false,按照插入顺序排序,先进先出;
    //true代表按照访问顺序排序,那么调用get方法后,内部会调用recordAccess方法记录访问顺序,将最新访问的元素添加到双向链表的表头,并从原来的位置删除,不断访问可以形成按访问顺序排序的链表;可以重写removeEldestEntry方法返回true值指定插入元素时移除最老的元素。 
    Map<String, String> linkedHashMap = new LinkedHashMap<String, String>(16,0.75f,true);
    
    Iterator<Map.Entry> iterator= linkedHashMap.entrySet().iterator();  
    
    while(iterator.hasNext())  
    {  
        Map.Entry entry = iterator.next();  
        System.out.println(entry.getKey()+":"+entry.getValue());  
    } 
    
    for(Entry<String, Integer> entry : linkedHashMap.entrySet()) {
        System.out.println(entry.getKey() + ": " + entry.getValue());
    }
    
  3. TreeMap实现SortedMap接口,可以保证元素的排列以key值有序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。内部实现采用红黑树(一种平衡排序二叉树),循环遍历时直接中序遍历树输出。在使用TreeMap时,key必须实现Comparable接口或者在构造TreeMap传入自定义的Comparator,否则会在运行时抛出java.lang.ClassCastException类型的异常。

HashTable,Hashset和ConcurrentHashMap

  1. HashTable实现Map接口(和HashMap一样)不允许null键的存在,他是一个synchronized的map,因为内部的put,get方法进行了同步所以导致效率降低,线程安全性强于ConcurrentHashMap
  2. HashSet实现set接口,不允许重复元素的存在,但是允许null存在;每次add之前会先调用hashcode和equals方法去判断该对象是否已经存在,已存在则返回false,不会更改set中任何内容;存取速度没有HashMap快;但是其内部实现就是一个HashMap,只是每一个中的value都是PRESET
  3. HashMap和 HashTable 的区别:
    • 两者都实现Map接口,但是HashTable继承Dictionary类,HashMap则是继承AbstractMap接口;
    • HashTable是线程安全的(通过直接对get,put等方法加Synchronized锁来实现),HashMap则是线程不安全的;
    • HashMap可以让你将空值作为一个表的条目的key或value,但是HashTable不可以;
    • 哈希值的使用不同,HashTable直接使用对象的hashCode。而HashMap重新计算hash值;
    • 两个遍历方式的内部实现上不同,Hashtable、HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式
  4. ConcurrentHashMap是一个Java5以后才提供的同步容器,其内部实现采用二次hash,分区同步的方式,内部默认维护一个包含16个segment对象的数组,每个数组对象又对应一个桶数组(相当于一个HashMap),每个桶数组对象又维护着一个HashEntry链表;对于put的hashcode相同的key会出现同步阻塞,因为它会将当前hashcode对应的segment锁住,但是hashcode不相同的key插入时是不会出现同步阻塞的,这样相对于HashTable(同一时间点,只能有一个线程持有锁,也就是说在同一时间点,只能有一个线程能访问容器)就可以大大提高效率,提高了并发性,相当于16个线程同时操作。
    • ConcurrentHashMap 针对读操作做了大量的优化。通过 HashEntry 对象的不变性和用 volatile 型变量协调线程间的内存可见性,使得大多数时候,读操作不需要加锁就可以正确获得值
    • 其内部的结构可以让其在进行写操作的时候能够将锁的粒度保持地尽量地小,不用对整个ConcurrentHashMap加锁,而是分段加锁
    • 源码中containsValue和size操作实现原理: ConcurrentHashMap的size操作是在多个Segment中进行,但是也采用了一种比较巧的方式,来尽量避免对所有的Segment都加锁。具体怎么做的呢???Segment中的有一个modCount变量,代表的是对Segment中元素的数量造成影响的操作的次数,这个值只增不减,size操作就是遍历了两次Segment,每次记录Segment的modCount值,然后将两次的modCount进行比较,如果相同,则表示期间没有发生过写入操作,就将原先遍历的结果返回,如果不相同,则把这个过程再重复做一次,如果再不相同,则就需要将所有的Segment都锁住,然后一个一个遍历了
    • hash函数: 用到了Wang/Jenkins hash算法的变种,主要的目的是为了减少哈希冲突,使元素能够均匀的分布在不同的Segment上,从而提高容器的存取效率。假如哈希的质量差到极点,那么所有的元素都在一个Segment中,不仅存取元素缓慢,分段锁也会失去意义

ArrayList,CopyOnWriteArrayList和LinkedList

  1. ArrayList、LinkedList、Vector的区别:ArrayList 和Vector底层是采用数组方式存储数据,ArrayList可以通过List list = Collections.synchronizeList(arrayList);实现同步;Vector由于使用了synchronized方法(线程安全)所以性能上比ArrayList要差,LinkedList使用双向链表实现存储,随机存取比较慢,但是增删非常快,LinkedList中包含了 first 和 last 两个指针(Node)。Node 中包含了上一个节点和下一个节点的引用,这样就构成了双向的链表
  2. CopyOnWriteArrayList: CopyOnWriteArrayList的核心思想是利用高并发往往是读多写少的特性,对读操作不加锁,对写操作加锁,进行写操作时先复制一份新的集合,在新的集合上面修改,然后将新集合赋值给旧的引用,并通过volatile 保证其可见性。这种“写入时复制”策略,对容器的写操作将导致的容器中基本数组的复制,性能开销较大。所以在有写操作的情况下CopyOnWriteArrayList性能不佳,而且如果容器容量较大的话容易造成溢出
  3. LinkedBlockingQueue: 它是线程安全的阻塞队列,是作为生产者消费者的首选,LinkedBlockingQueue 可以指定容量,也可以不指定,不指定的话,默认最大是Integer.MAX_VALUE,其中主要用到put和take方法,put方法在队列满的时候会阻塞直到有队列成员被消费,take方法在队列空的时候会阻塞,直到有队列成员被放进来
  4. ConcurrentLinkedQueue: ConcurrentLinkedQueue是一个无锁的并发线程安全的非阻塞队列;Queue中元素按FIFO原则进行排序.采用CAS操作,来保证元素的一致性

参考链接

  1. Java8系列之重新认识HashMap

OpenWrt

发表于 2015-09-04 | 字数统计37字 | 阅读时长1分钟

Power consumption will be higher if a USB device is attached to its USB port!

scp command:

scp libmysqlclient_5.1.73-1_ar71xx.ipk root@192.168.1.1:

scp error: Host key verification failed

cd ~/.ssh
rm known_hosts

Android应用性能优化

发表于 2015-08-30 | 更新于 2016-06-07 | 字数统计2.6k字 | 阅读时长9分钟 | 分类于 Android

Android手机由于其本身的后台机制和硬件特点,性能上一直被诟病,所以软件开发者对软件本身的性能优化就显得尤为重要;本文将对Android开发过程中性能优化的各个方面做一个回顾与总结。

Cache优化

  1. ListView缓存:

    • ListView中有一个回收器,Item滑出界面的时候View会回收到这里,需要显示新的Item的时候,就尽量重用回收器里面的View;每次在getView函数中inflate新的item之前会先判断回收器中是否有缓存的view,即判断convertView是否为null,是则inflate一个新的item View,否则重用回收器中的item。
    • 此外,ListView还使用静态的ViewHolder减少findViewById的次数
    • ListView中有getViewTypeCount()函数用于获取列表有几种布局类型,getItemViewType(int position)用于获取在position位置上的布局类型; 我们可以利用ViewType来给不同类型的item创建不同的View,这样可以利于ListView的回收
    • 对Item中图片进行适当压缩, 并进行异步加载;如果快速滑动,不加载图片;实现数据的分页加载
  2. IO缓存:在文件和网络IO中使用具有缓存策略的输入流,BufferedInputStream替代InputStream,BufferedReader替代Reader,BufferedReader替代BufferedInputStream

  3. data缓存(空间换时间):①缓存数据库的查询结果,比如缓存数据库表中的数据条数,这样就可以避免多次的select count查询 ②缓存磁盘文件中需要频繁访问的数据到内存中 ③缓存耗时计算的结果

Battery优化

  • cpu的使用率和使用频率将直接或间接的影响电量的分配和使用,cpu降频可以节约电量
  • service优化

    • service作为一个运行在主线程中的后台服务,应该尽量避免耗时动作,而应该尽量新开线程去处理耗时动作
    • 监听系统广播看service是否存活,否则kill掉;降低service优先级使得系统内存吃紧时会被自动kill掉

    • 使用Android提供的IntentService代替service,因为IntentService会在运行完成之后自动停止,而service需要手动调用stopService()才能停止运行

    • 定时执行任务的Alarm机制:Android的定时任务有两种实现方式,Timer类和Alarm机制;Timer不适合长期后台运行的定时任务。因为每种手机都会有自己的休眠策略,Android手机就会在长时间不操作的情况下自动让CPU进入到睡眠状态,这就有可能导致Timer中的定时任务无法正常运行。而Alarm机制则不存在这种情况,它具有唤醒CPU的功能,即可以保证每次需要执行定时任务的时候CPU能正常工作。然而从Android4.4之后,Alarm任务的触发时间将会变得不准确,有可能会延迟一段时间后任务才能得到执行。这不是bug,而是系统在耗电性方面进行的优化。系统会自动检测目前有多少Alarm任务存在,然后将触发时间将近的几个任务放在一起执行,这就可以大幅度的减少CPU被唤醒的次数,从而有效延长电池的使用时间

渲染层优化

  1. Android 界面卡顿的原因?

    • UI线程中做耗时操作,比如进行网络请求,磁盘读取,位图修改,更新UI等耗时操作,从而导致UI线程卡顿
    • 布局Layout过于复杂,无法在16ms内完成渲染,或者嵌套层次过深
    • View过度绘制或者频繁的触发measure、layout,同一时间动画执行的次数过多,导致CPU或GPU负载过重
    • 冗余资源及逻辑等导致加载和执行缓慢
  2. Android 界面卡顿怎么处理?

    • xml布局优化:尽量使用include、merge、ViewStub标签,尽量不存在冗余嵌套及过于复杂布局(譬如10层就会直接异常),例如使用RelativeLayout代替LinearLayout可以减少布局层次和复杂性,View的嵌套层次不能过深,尽量使用GONE替换INVISIBLE,使用weight后尽量将width和heigh设置为0dp,减少运算,Item存在非常复杂的嵌套时考虑使用自定义Item View来取代,减少measure与layout次数等。
    • ListView及Adapter优化;尽量复用getView方法中的相关View,不重复获取实例导致卡顿,列表尽量在滑动过程中不进行UI元素刷新等。
    • 背景和图片等内存分配优化;尽量减少不必要的背景设置,图片尽量压缩处理显示,尽量避免频繁内存抖动等问题出现;尽可能为不同分辨率创建资源,以减少不必要的硬件缩放
    • 自定义View等绘图与布局优化;尽量避免在draw、measure、layout中做过于耗时及耗内存操作,尤其是draw方法中,尽量减少draw、measure、layout等执行次数,避免过度渲染和绘制;减少不必要的inflate,尽量使用全局变量缓存View
    • 避免ANR,不要在UI线程中做耗时操作,譬如多次数据库操作等
  3. Layout常用的标签

    • include标签:该标签可以用于将布局文件中的公共部分提取出来给其它布局文件复用,从而使得布局模块化,代码轻量化; 注意点: ①如果标签已经定义了id,而嵌入布局文件的root布局文件也定义了id,标签的id会覆盖掉嵌入布局文件root的id,如果include标签没有定义id则会使用嵌入文件root的id ②如果想使用标签覆盖嵌入布局root布局属性,必须同时覆盖layout_height和layout_width属性,否则会直接报编译时语法错误

    • viewstub标签:该标签与include一样用于引入布局模块,只是引入的布局默认不会扩张,既不会占用显示也不会占用位置,从而在解析layout时节省cpu和内存,只有通过调用setVisibility函数或者Inflate函数才会将其要装载的目标布局给加载出来,从而达到延迟加载的效果;例如条目详情、进度条标识或者未读消息等,这些情况如果在一开始初始化,虽然设置可见性View.GONE,但是在Inflate的时候View仍然会被Inflate,仍然会创建对象。

    • merge标签:该标签在layout中会被自动忽略,从而减少一层布局嵌套,其主要用处是当一个布局作为子布局被其他布局include时,使用merge当作该布局的顶节点来代替layout顶节点就可以减少一层嵌套

    • hierarchy viewer:该工具可以方便的查看Activity的布局,各个View的属性、measure、layout、draw的时间,如果耗时较多会用红色标记,否则显示绿色

网络优化

  • 异步请求网络数据,避免频繁请求数据(例如如果某个页面内请求过多,可以考虑做一定的请求合并),尽可能的减少网络请求次数和减小网络请求时间间隔
  • 网络应用传输中使用高效率的数据格式,譬如使用JSON代替XML,使用WebP代替其他图片格式,并对数据进行Gzip压缩数据,比如post请求的body和header内容
  • 及时缓存数据到内存/文件/数据库
  • 执行某些操作前尽量先进行网络状态判断,比如wifi传输数据比蜂窝数据更省电,所以尽量在wifi下进行数据的预加载

  • httpClient和httpUrlConnection对比:

    1. httpClient是apache的开源实现,API数量多,非常稳定
    2. httpUrlConnection是java自带的模块: ①可以直接支持GZIP压缩,而HttpClient虽然也支持GZIP,但要自己写代码处理 ②httpUrlConnection直接在系统层面做了缓存策略处理,加快重复请求的速度 ③API简单,体积较小,而且直接支持系统级连接池,即打开的连接不会直接关闭,在一段时间内所有程序可共用
    3. HttpURLConnection在Android2.2之前有个重大Bug,调用close()函数会影响连接池,导致连接复用失效,需要关闭keepAlive;因此在2.2之前http请求都是用httpClient,2.2之后则是使用HttpURLConnection
    4. 但是!!!现在!!!Android不再推荐这两种方式!二是直接使用OKHttp这种成熟方案!支持Android 2.3及其以上版本

数据结构优化

  • ArrayList和LinkedList的选择:ArrayList根据index取值更快,LinkedList更占内存、随机插入删除更快速、扩容效率更高

  • ArrayList、HashMap、LinkedHashMap、HashSet的选择:hash系列数据结构查询速度更优,ArrayList存储有序元素,HashMap为键值对数据结构,LinkedHashMap可以记住加入次序的hashMap,HashSet不允许重复元素

  • HashMap、WeakHashMap选择:WeakHashMap中元素可在适当时候被系统垃圾回收器自动回收,所以适合在内存紧张时使用

  • Collections.synchronizedMap和ConcurrentHashMap的选择:ConcurrentHashMap为细分锁,锁粒度更小,并发性能更优;Collections.synchronizedMap为对象锁,自己添加函数进行锁控制更方便

  • Android中性能更优的数据类型:如SparseArray、SparseBooleanArray、SparseIntArray、Pair;Sparse系列的数据结构是为key为int情况的特殊处理,采用二分查找及简单的数组存储,加上不需要泛型转换的开销,相对Map来说性能更优

内存优化

  1. Android应用内存溢出OOM
    • 内存溢出的主要导致原因有如下几类:①应用代码存在内存泄露,长时间积累无法释放导致OOM;②应用的某些逻辑操作疯狂的消耗掉大量内存(譬如加载一张不经过处理的超大超高清图片等)导致超过阈值OOM
    • 解决思路①在内存引用上做些处理,常用的有软引用、弱引用 ②在内存中加载图片时直接在内存中做处理,如:边界压缩 ③动态回收内存,手动recyle bitmap,回收对象 ④优化Dalvik虚拟机的堆内存分配 ⑤自定义堆内存大小

参考链接1

参考链接2

1…121314…18
CharlesXiao

CharlesXiao

在码农炼成之路不断挣扎……stay hungry……keep learning……

87 日志
18 分类
78 标签
github weibo Daijiale的个人站点
推荐阅读
  • RocksDB
  • Google FE
© 2015.05.16 – 2019 CharlesXiao
本站总访问量:
|
总访客数:
|
博客全站共169.6k字