MENU

容器(集合、Map)

2024 年 06 月 13 日 • 访问: 260 次 • Java

Java容器

  • Collection

    • List
    • Set
    • Queue
  • Map

List

Q:arrList = new ArrayList<>(Arrays.asList()) 和 arrList = Arrays.asList()有什么区别

List<Integer> arrList1 = new ArrayList<>(Arrays.asList(1,2,3));
List<Integer> arrList2 = Arrays.asList(1,2,3);

arrList1.add(4);
arrList2.add(4);

A:执行arrList2.add(4)时会报错。
因为 Arrays.asList()只会返回1个固定大小的列表, 其返回的List是AbstractList,无法调用add、remove和clear, 如果调用会直接抛异常。
异常名字为UnsupportedOperationException

Q:那asList()支持通过修改原数组来修改list内容吗?
例如

String[] ss = {"a","b","c"};
List<String> arrList = Arrays.asList(ss);
ss[0] = "d";
System.out.println(arrList);

上面操作后arrList会报错吗?

A:
不会报错。arrList里的内容变成了{“d”,“b”,“c”}

Q: 当输入为哪些字母时,迭代时会报错

public static void main(String[] args) throws Exception {
    List<String> list = Lists.newArrayList("A", "B", "C", "D");

    String s = args[0];
    for (String curStr : list) {
        if (s.equals(curStr)) {
            list.remove(curStr);
        }
    }
}

A:
删除A或者B会报错, 但是删除C不会!
对于foreach遍历容器,并用remove做删除时,当删除倒数第二个元素时,是不会报错的。

这个知识点只做理论理解,实际编程中在循环中去删除元素是错误的,必须使用迭代器中去删除元素

int i = 0;
List<Integer> list = new ArrayList<Integer>(Arrays.asList(10, 20, 30, 40, 50, 60, 70, 80));
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
    Integer num = iterator.next(); //必须在remove()之前,显式调用next()
    iterator.remove();
    System.out.println("当前值:" + num + ",执行次数" + ++i);
}

Q: ArrayList的扩容公式和默认初始大小是多少?
A:
扩容容量= 原容量 + (原容量右移1位,即0.5倍)= 1.5倍
初始容量为10.
注意,初始的数组长度还是0,数组长度和容量不是一个东西。

Q: Vector的扩容公式是多少?

A:
扩容容量= 原容量 *2
初始大小为10

Q:vector和ArrayList的区别
A:
vector是线程安全的, 每个方法都加了syn关键字,频繁的加锁可能导致性能降低

Q:为什么不推荐使用stack
A:
stack 继承自vector , 但是vector里包含了很多不需要的public方法
只是为了实现栈,不用链表来单独实现,而是为了复用简单的方法而迫使它继承 Vector,Stack 和 Vector 本来是毫无关系的。这使得 Stack 在基于数组实现上效率受影响,另外因为继承 Vector 类,Stack 可以复用 Vector 大量方法,这使得 Stack 在设计上不严谨

Map相关问题

Q: 解答下列特性和哪些Map有关
实现基于散列表,迭代时是不确定随机的顺序的Map为(HashMap)
迭代时按照插入顺序进行迭代(LinkedHashMap)
迭代时按照键值的比较顺序进行迭代(TreeMap)
基于红黑树的实现(TreeMap)
线程安全的Map(ConcurrentHashMap)
键值比较时使用==而不是equal的Map(IdentityHashMap)
可以按区间得到1个子map的Map(sortMap)

Set也有HashSet、LinkedSet、TreeSet、SortSet等,作用同理。

Q: hashCode相同, 那么equals肯定true吗?
A: 不一定。equals()方法比较的是对象的内容(可以重写),而hashCode()方法则用于获取对象的哈希码(也可以重写),哈希值相同不代表原始值就一定相同,可能会出现哈希碰撞的情况(虽然概率比较低)。

Q: equals为true, 那么hashCode肯定相同吗?
A: 对。
原因: 参见散列表的原理,明白hashCode的作用后就明白为什么了。

Q: 2个String如果内容相同,那么hashCode相同吗?
A: 对,相同。因为二者equals返回true,所以必定hashCode相同。

Q: 如果在插入后,修改某个key的hashCode,可能造成什么问题?
A:
可能造成内存泄漏。因为map是按计算后的hashCode存放的,而如果在外部修改了某个key的值,可能造成之前塞入的那个哈希所在的地址无法被外部remove(key),却又无法被gc(因为一直持有),造成内存泄漏。

Q: HashTable、HashMap、ConcurrentHashMap比较:

  • key和value不可以为null的是:HashTable
  • 线程安全的是:HashTable和ConcurrentHashMap
    他们2个的区别:

在迭代的过程中,ConcurrentHashMap仅仅锁定map的某个部分,而Hashtable则会锁定整个map。(具体可参考底层实现原理,总之ConcurrentHashMap最重要的是做了分段,所以使用ConcurrentHashMap性能更优)

Q:Collections.synchronizedMap(map)和ConcurrentHashMap,哪个同步效果好?

A:

  • Collections.synchronizedMap()和Hashtable一样,实现上在调用map所有方法时,都对整个map进行同步
  • ConcurrentHashMap的实现却更加精细,分端加锁

其实Colletions.synchronizedMap就是对放进去的map包了一层sync关键字。
详见:https://www.cnblogs.com/a198720/articles/4227500.html

Q:linkedHashMap的accessOrder问题,下面输出什么

public void fun2() throws Exception {
    LinkedHashMap<String, String> accessOrderTrue = new LinkedHashMap<>(16, 0.75f, true);
    accessOrderTrue.put("1","1");
    accessOrderTrue.put("2","2");
    accessOrderTrue.put("3","3");
    accessOrderTrue.put("4","4");
    System.out.println("put后的数据:"+accessOrderTrue);
    accessOrderTrue.get("2");
    accessOrderTrue.get("3");
    System.out.println("get后的数据"+accessOrderTrue);
}

A:
省略key值
put后的数据: {1=1,2=2,3=3,4=4}
get后的数据: {1=1,4=4,2=2,3=3}

accessOrder为true时, 会把最近访问过的数据放到链表末尾。

Q:hashMap为什么多线程使用时可能会造成死循环?
A:
https://www.jianshu.com/p/1e9cf0ac07f4
主要发生在2个线程同时put并进行扩容时, 对同一个对象的链表引用会出现问题。(底层原理细究还是蛮有意思的

Q:java1.7 和1.8 之间, hashMap做了什么改进?
A:
1.7的哈希如果冲突严重,则在一个点上形成的链表会越来越长。
因此1.8做了改进,如果那个点的链表长度超过TREEIFY_THRESHOLD,则会转为红黑树。

Collections

  • Collection是接口, Collections是1个工具类
  • 排序: Collections.sort(collection ,Comparator<>)
  • 打乱顺序: Collections.shuffle(collection ,Random)
  • 填充: Collections.fill(list, 对象) , 注意是浅拷贝填充, 即填充后使用的是同一个引用。
  • 返回不可变容器(即无法对容器做修改): Collections.unmodifiableMap(容器)
  • 除此之外可以返回空的不可变集合和仅有单个元素的不可变集合。

队列

参考文章Java中的五大队列:https://www.cnblogs.com/vipstone/p/13862311.html

  • 普通队列(Queue)是指实现了先进先出的基本队列,例如 ArrayBlockingQueue 和 LinkedBlockingQueue,其中 ArrayBlockingQueue 是用数组实现的普通队列
  • 双端队列(Deque)是指队列的头部和尾部都可以同时入队和出队的数据结构,比如LinkedBlockingDeque
  • 优先队列(PriorityQueue)是一种特殊的队列,它并不是先进先出的,而是优先级高的元素先出队。优先队列是根据二叉堆实现的二叉堆分为两种类型:一种是最大堆一种是最小堆。在最大堆中,任意一个父节点的值都大于等于它左右子节点的值。因为优先队列是基于二叉堆实现的,因此它可以将优先级最好的元素先出队。优先队列的出队是不考虑入队顺序的,它始终遵循的是优先级高的元素先出队
  • 延迟队列(DelayQueue)是基于优先队列 PriorityQueue 实现的,它可以看作是一种以时间为度量单位的优先的队列,当入队的元素到达指定的延迟时间之后方可出队。
  • 其他队列:在 Java 的队列中有一个比较特殊的队列 SynchronousQueue,它的特别之处在于它内部没有容器,每次进行 put() 数据后(添加数据),必须等待另一个线程拿走数据后才可以再次添加数据
最后编辑于: 2024 年 10 月 17 日
返回文章列表 打赏
本页链接的二维码
打赏二维码