可以从它们的底层数据结构、效率、开销进行阐述
Collection.sort是对list进行排序,Arrays.sort是对数组进行排序。
Collections.sort底层实现
Collections.sort方法调用了list.sort方法
list.sort方法调用了Arrays.sort的方法
因此,Collections.sort方法底层就是调用的Array.sort方法
Arrays.sort底层实现
Arrays的sort方法,如下:
如果比较器为null,进入sort(a)方法。如下:
因此,Arrays的sort方法底层就是:
Timesort排序
Timsort排序是结合了合并排序(merge.sort)和插入排序(insertion sort)而得出的排序方法;
1.当数组长度小于某个值,采用的是二分插入排序算法,如下:
2.找到各个run,并入栈。
3.按规则合并run。
Queue队列中,poll() 和 remove() 都是从队列中取出一个元素,在队列元素为空的情况下,remove() 方法会抛出异常,poll() 方法只会返回 null 。
/** * Retrieves and removes the head of this queue. This method differs * from {@link #poll poll} only in that it throws an exception if this * queue is empty. * * @return the head of this queue * @throws NoSuchElementException if this queue is empty */ E remove(); /** * Retrieves and removes the head of this queue, * or returns {@code null} if this queue is empty. * * @return the head of this queue, or {@code null} if this queue is empty */ E poll();
HashMap
HashTable
ConcurrentHashMap
安全删除元素:
不安全删除元素:
数组是不能直接打印的,如下:
public class Test { public static void main(String[] args) { String[] jayArray = {"jay", "boy"}; System.out.println(jayArray); } } //output [Ljava.lang.String;@1540e19d
打印数组可以用流的方式Strem.of().foreach(),如下:
public class Test { public static void main(String[] args) { String[] jayArray = {"jay", "boy"}; Stream.of(jayArray).forEach(System.out::println); } } //output jay boy
打印数组,最优雅的方式可以用这个APi,Arrays.toString()
public class Test { public static void main(String[] args) { String[] jayArray = {"jay", "boy"}; System.out.println(Arrays.toString(jayArray)); } } //output [jay, boy]
Hashmap的扩容:
可以看一下HashSet的add方法,元素E作为HashMap的key,我们都知道HashMap是不允许重复的
public boolean add(E e) { return map.put(e, PRESENT)==null; }
不是线性安全的。
并发的情况下,扩容可能导致死循环问题。
线性安全的
线性不安全的
Collection是Java集合框架中的基本接口,如List接口也是继承于它
public interface List<E> extends Collection<E> {
Collections是Java集合框架提供的一个工具类,其中包含了大量用于操作或返回集合的静态方法。如下
public static <T extends Comparable<? super T>> void sort(List<T> list) { list.sort(null); }
这个点,主要考察HashMap和TreeMap的区别。
TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按key的升序排序,也可以指定排序的比较器。当用Iterator遍历TreeMap时,得到的记录是排过序的。
List 转 Array
List 转Array,必须使用集合的 toArray(T[] array),如下:
List<String> list = new ArrayList<String>(); list.add("jay"); list.add("tianluo"); // 使用泛型,无需显式类型转换 String[] array = list.toArray(new String[list.size()]); System.out.println(array[0]);
如果直接使用 toArray 无参方法,返回值只能是 Object[] 类,强转其他类型可能有问题,demo如下:
List<String> list = new ArrayList<String>(); list.add("jay"); list.add("tianluo"); String[] array = (String[]) list.toArray(); System.out.println(array[0]);
运行结果:
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String; at Test.main(Test.java:14)
Array 转List
使用Arrays.asList() 把数组转换成集合时,不能使用修改集合相关的方法,如下:
String[] str = new String[] { "jay", "tianluo" }; List list = Arrays.asList(str); list.add("boy");
Exception in thread "main" java.lang.UnsupportedOperationException at java.util.AbstractList.add(AbstractList.java:148) at java.util.AbstractList.add(AbstractList.java:108) at Test.main(Test.java:13)
因为 Arrays.asList不是返回java.util.ArrayList,而是一个内部类ArrayList。
可以这样使用弥补这个缺点:
//方式一: ArrayList< String> arrayList = new ArrayList<String>(strArray.length); Collections.addAll(arrayList, strArray); //方式二: ArrayList<String> list = new ArrayList<String>(Arrays.asList(strArray)) ;
public interface Collection<E> extends Iterable<E> { Iterator<E> iterator();
方法如下:
next() 方法获得集合中的下一个元素 hasNext() 检查集合中是否还有元素 remove() 方法将迭代器新返回的元素删除 forEachRemaining(Consumer<? super E> action) 方法,遍历所有元素
Iterator 主要是用来遍历集合用的,它的特点是更加安全,因为它可以确保,在当前遍历的集合元素被更改的时候,就会抛出 ConcurrentModificationException 异常。
使用demo如下:
List<String> list = new ArrayList<>(); Iterator<String> it = list. iterator(); while(it. hasNext()){ String obj = it. next(); System. out. println(obj); }
再看一下demo吧
public class Test { private static Map<Integer, String> map = new HashMap<Integer, String>(); { map.put(1, "jay"); map.put(2, "tianluo"); } public static void main(String[] args) { map = Collections.unmodifiableMap(map); map.put(1, "boy"); System.out.println(map.get(1)); } }
运行结果:
// 可以发现,unmodifiableMap确保集合不能修改啦,抛异常了 Exception in thread "main" java.lang.UnsupportedOperationException at java.util.Collections$UnmodifiableMap.put(Collections.java:1457) at Test.main(Test.java:14)
快速失败
在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出Concurrent Modification Exception。
public class Test { public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(1); list.add(2); Iterator iterator = list.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); list.add(3); System.out.println(list.size()); } } }
运行结果:
1 Exception in thread "main" java.util.ConcurrentModificationException 3 at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909) at java.util.ArrayList$Itr.next(ArrayList.java:859) at Test.main(Test.java:12)
安全失败
采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。
public class Test { public static void main(String[] args) { List<Integer> list = new CopyOnWriteArrayList<>(); list.add(1); list.add(2); Iterator iterator = list.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); list.add(3); System.out.println("list size:"+list.size()); } } }
运行结果:
1 list size:3 2 list size:4
其实,在java.util.concurrent 并发包的集合,如 ConcurrentHashMap, CopyOnWriteArrayList等,默认为都是安全失败的。
优先队列PriorityQueue是Queue接口的实现,可以对其中元素进行排序
public class PriorityQueue<E> extends AbstractQueue<E> implements java.io.Serializable { ... private final Comparator<? super E> comparator;
方法:
peek()//返回队首元素 poll()//返回队首元素,队首元素出队列 add()//添加元素 size()//返回队列元素个数 isEmpty()//判断队列是否为空,为空返回true,不空返回false
特点:
jdk8 放弃了分段锁而是用了Node锁,减低锁的粒度,提高性能,并使用CAS操作来确保Node的一些操作的原子性,取代了锁。
ArrayBlockingQueue是数组实现的线程安全的有界的阻塞队列,继承自AbstractBlockingQueue,间接的实现了Queue接口和Collection接口。底层以数组的形式保存数据(实际上可看作一个循环数组)。常用的操作包括 add ,offer,put,remove,poll,take,peek。
是双向链表,看源码
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; } }
ArrayList扩容的本质就是计算出新的扩容数组的size后实例化,并将原有数组内容复制到新数组中去。
public boolean add(E e) { //扩容 ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private static int calculateCapacity(Object[] elementData, int minCapacity) { //如果传入的是个空数组则最小容量取默认容量与minCapacity之间的最大值 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } private void ensureExplicitCapacity(int minCapacity) { modCount++; // 如果最小需要空间比elementData的内存空间要大,则需要扩容 // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); } private void grow(int minCapacity) { // 获取elementData数组的内存空间长度 int oldCapacity = elementData.length; // 扩容至原来的1.5倍 int newCapacity = oldCapacity + (oldCapacity >> 1); //校验容量是否够 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //若预设值大于默认的最大值,检查是否溢出 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // 调用Arrays.copyOf方法将elementData数组指向新的内存空间 //并将elementData的数据复制到新的内存空间 elementData = Arrays.copyOf(elementData, newCapacity); }
为了能让HashMap存取高效,数据分配均匀。
以下等式相等,但是位移运算比取余效率高很多
hash%length=hash&(length-1)
java8不是用红黑树来管理hashmap,而是在hash值相同的情况下(且重复数量大于8),用红黑树来管理数据。
红黑树相当于排序数据。可以自动的使用二分法进行定位。性能较高。
ArrayList 的默认大小是 10 个元素
/** * Default initial capacity. */ private static final int DEFAULT_CAPACITY = 10;
public interface Enumeration<E> { boolean hasMoreElements(); E nextElement(); } public interface Iterator<E> { boolean hasNext(); E next(); void remove(); }
可以用 Collections.sort()+ Comparator.comparing(),因为对对象排序,实际上是对对象的属性排序
public class Student { private String name; private int score; public Student(String name, int score){ this.name = name; this.score = score; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getScore() { return score; } public void setScore(int score) { this.score = score; } @Override public String toString() { return "Student: " + this.name + " 分数:" + Integer.toString( this.score ); } } public class Test { public static void main(String[] args) { List<Student> studentList = new ArrayList<>(); studentList.add(new Student("D", 90)); studentList.add(new Student("C", 100)); studentList.add(new Student("B", 95)); studentList.add(new Student("A", 95)); Collections.sort(studentList, Comparator.comparing(Student::getScore).reversed().thenComparing(Student::getName)); studentList.stream().forEach(p -> System.out.println(p.toString())); } }
在作为参数传递之前,使用Collections.unmodifiableCollection(Collection c)方法创建一个只读集合,这将确保改变集合的任何操作都会抛出UnsupportedOperationException。
看看它的add方法,元素E作为HashMap的key
public boolean add(E e) { return map.put(e, PRESENT)==null; }
String、Integer等包装类的特性能够保证Hash值的不可更改性和计算准确性,能够有效的减少Hash碰撞的几率~
因为
ArrayBlockingQueue: (有界队列)是一个用数组实现的有界阻塞队列,按FIFO排序量。
LinkedBlockingQueue: (可设置容量队列)基于链表结构的阻塞队列,按FIFO排序任务,容量可以选择进行设置,不设置的话,将是一个无边界的阻塞队列,最大长度为Integer.MAX_VALUE,吞吐量通常要高于ArrayBlockingQuene;newFixedThreadPool线程池使用了这个队列
DelayQueue:(延迟队列)是一个任务定时周期的延迟执行的队列。根据指定的执行时间从小到大排序,否则根据插入到队列的先后排序。newScheduledThreadPool线程池使用了这个队列。
PriorityBlockingQueue:(优先级队列)是具有优先级的无界阻塞队列;
SynchronousQueue:(同步队列)一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene,newCachedThreadPool线程池使用了这个队列。针对面试题:线程池都有哪几种工作队列?
元素重复与否是使用equals()方法进行判断的,这个可以跟面试官说说==和equals()的区别,hashcode()和equals
因为ArrayList的底层是数组实现,并且数组的默认值是10,如果插入10000条要不断的扩容,耗费时间,所以我们调用ArrayList的指定容量的构造器方法ArrayList(int size) 就可以实现不扩容,就提高了性能。
public class Person { private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Person(String name, Integer age) { this.name = name; this.age = age; } } public class Test { public static void main(String[] args) { List<Person> list = new ArrayList<>(); list.add(new Person("jay", 18)); list.add(new Person("tianLuo", 10)); list.stream().forEach(p -> System.out.println(p.getName()+" "+p.getAge())); // 用comparing比较对象属性 list.sort(Comparator.comparing(Person::getAge)); System.out.println("排序后"); list.stream().forEach(p -> System.out.print(p.getName()+" "+p.getAge()+" ")); } }
在 Java 7 中,ArrayList 的默认大小是 10 个元素,HashMap 的默认大小是16个元素(必须是2的幂)。
Hashmap解决hash冲突,使用的是链地址法,即数组+链表的形式来解决。put执行首先判断table[i]位置,如果为空就直接插入,不为空判断和当前值是否相等,相等就覆盖,如果不相等的话,判断是否是红黑树节点,如果不是,就从table[i]位置开始遍历链表,相等覆盖,不相等插入。
原文链接:
https://mp.weixin.qq.com/s/si_V6J_6ZZn4Akc12mMk2g
以上内容仅供参考学习,如有侵权请联系我删除!
如果这篇文章对您有帮助,左下角的大拇指就是对博主最大的鼓励。
您的鼓励就是博主最大的动力!