Collections工具类、StringBuilder与String对比

一、Collections工具类所含通用算法

  1. 排序、二分查找、洗牌、旋转等

  2. Sort排序

    1. 旧版的有归并排序和新版的TimSort

    2. 基本都是执行了TimSort

    3. 排序过程中还会因为元素的个数是否大于32而选择分段排序和二分插入排序。

  3. BinarySearch 二分查找

    1. 需要是有序集合,对链表结构和数组结构分两个函数执行,数组的耗时基本是O(1),但链表的耗时基本都是O(n)级别。

    2. 对数组结构的操作

    int indexedBinarySearch(List<? extends Comparable<? super T>> list, T key) {
            int low = 0;
            int high = list.size()-1;
    ​
            while (low <= high) {
                int mid = (low + high) >>> 1;
                Comparable<? super T> midVal = list.get(mid);
                int cmp = midVal.compareTo(key);
    ​
                if (cmp < 0)
                    low = mid + 1;
                else if (cmp > 0)
                    high = mid - 1;
                else
                    return mid; // key found
            }
            return -(low + 1);  // key not found
        }
    1. 对链表结构的查询

      int iteratorBinarySearch(List<? extends Comparable<? super T>> list, T key)
          {
              int low = 0;
              int high = list.size()-1;
              ListIterator<? extends Comparable<? super T>> i = list.listIterator();
      ​
              while (low <= high) {
                  int mid = (low + high) >>> 1;
                  Comparable<? super T> midVal = get(i, mid);
                  int cmp = midVal.compareTo(key);
      ​
                  if (cmp < 0)
                      low = mid + 1;
                  else if (cmp > 0)
                      high = mid - 1;
                  else
                      return mid; // key found
              }
              return -(low + 1);  // key not found
          }
      // 链表迭代查询
      private static <T> T get(ListIterator<? extends T> i, int index) {
              T obj = null;
              int pos = i.nextIndex();
              if (pos <= index) {
                  do {
                      obj = i.next();
                  } while (pos++ < index);
              } else {
                  do {
                      obj = i.previous();
                  } while (--pos > index);
              }
              return obj;
          }
  4. shuffle 洗牌算法

    1. 主要作用就是对集合中的元素进行打乱,一般可以用在摇奖、摇号、洗牌等各个场景中。

    2. 主要逻辑就是随机位置后进行位置置换,具体逻辑就是顺延处理,因为要保持rnd的最大值是已被置换过的。

      public static void shuffle(List<?> list, Random rnd) {
              int size = list.size();
              // 当size小于5 且集合类属于 RandomAccess
              if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) {
                  for (int i=size; i>1; i--)
                      swap(list, i-1, rnd.nextInt(i));
              } else {
                  Object[] arr = list.toArray();
      ​
                  // Shuffle array
                  for (int i=size; i>1; i--)
                      swap(arr, i-1, rnd.nextInt(i));
      ​
                  // Dump array back into list
                  // instead of using a raw type here, it's possible to capture
                  // the wildcard but it will require a call to a supplementary
                  // private method
                  // 链表结构置换数据
                  ListIterator it = list.listIterator();
                  for (Object e : arr) {
                      it.next();
                      it.set(e);
                  }
              }
          }
      // 置换位置
      public static void swap(List<?> list, int i, int j) {
              // instead of using a raw type here, it's possible to capture
              // the wildcard but it will require a call to a supplementary
              // private method
              final List l = list;
              l.set(i, l.set(j, l.get(i)));
          }
  5. rotate 旋转算法

    1. 可以把ArrayList和LinkedList从指定的某个位置开始,进行正旋或逆旋的操作,把需要的数据前移或后移,类似把数组比作绳子,然后打结成圆。

    2. 逻辑

      1. distance = distance % size;,获取旋转的位置。

      2. for 循环和 dowhile,配合每次的旋转操作,比如这里第一次会把 0 位置元素替换到2 位置,接着在 dowhile 中会判断 i != cycleStart,满足条件继续把替换了 2位置的元素继续往下替换。直到一轮循环把所有元素都放置到正确位置。

      3. 最终 list 元素被循环替换完成,也就相当从某个位置开始,正序旋转 2 个位置的操作。

二、StringBuilder与String的对比

  1. StringBuilder为什么比String快,为什么

    1. 虽然String的加号拼接会被jvm优化成StringBuilder但它是每次都会new一个新的StringBuilder来使用,所以在for循环里还是把new StringBuilder的操作提取到外面性能会更好。

  2. String的内部value和String类都是final,所以每次+号拼接时都会new 一个新的类来进行拼接并且toString();

  3. String.intern() 时直接把值推进了常量池,所以两个对象都做了intern()操作后,比对的是常量池里的值。

    1. String str_1 = new String("ab");
      String str_2 = new String("ab");
      String str_3 = "ab"; // 不会重新创建对象,jvm优化成先去常量池查找
      System.out.println(str_1 == str_2);
      System.out.println(str_1 == str_2.intern());
      System.out.println(str_1.intern() == str_2.intern());
      System.out.println(str_1 == str_3);
      System.out.println(str_1.intern() == str_3);
    2. intern是一个本地方法,底层给通过JNI调用C++语言编写的功能。

  4. StringBuilder源码分析

    1. 底层是char[] 结构,数组结构,操作基本和ArrayList一致,也是需要扩容,copy数组,只不过再toString的时候会将char[] 通过new String的方式创建出来。

  5. StringBuffer源码分析

    1. 底层操作基本和StringBuilder一致,只不过是在append时增加了synchronized锁,所以它是线程安全的。

    2. synchronized 不是重量级锁吗,JVM 对它有什么优化呢?

      1. 其实为了减少获得锁与释放锁带来的性能损耗,从而引入了偏向锁、轻量级锁、重量级锁来进行优化。图是引用

      2. 从无锁状态开始,当线程进入 synchronized 同步代码块,会检查对象头和栈帧内是否有当前线下 ID 编号,无则使用 CAS 替换。

      3. 解锁时,会使用 CAS 将 Displaced Mark Word 替换回到对象头,如果成功,则表示竞争没有发生,反之则表示当前锁存在竞争锁就会升级成重量级锁。

      4. 另外,大多数情况下锁是不发生竞争的,基本由一个线程持有。所以,为了避免获得锁与释放锁带来的性能损耗,所以引入锁升级,升级后不能降级

三、如果你来提问,你会怎么提问

  1. StringBuffer和StringBuilder的区别在哪里?

    1. StringBuffer是线程安全的,StringBuilder是非线程安全的,主要区别在append方法中,StringBuffer是增加了synchronized锁。

  2. String.intern()的作用

    1. 它会将char数组推入常量池,这个函数的本质是一个本地方法,会通过JNI调用C++编写的功能。

  3. 使用StringBuilder然后toString后会创建几个对象

    1. 两个,在toString时会创建一个String对象。

  4. 使用StringBuilder 进行for循环拼接为什么比String + 号拼接快

    1. 在JVM层面,String的+号会被优化成StrinBuilder,但它是每拼接一次+号就会新new出来一个StringBuilder,创建对象的时间耗时,所以将StringBuilder提取到外层来会比直接加号拼接快很多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值