JAVA的集合类
一、HashMap排序,上机题
已知一个HashMap<Integer,User>集合,User有name(String)和age(int)属性。请写一个方法实现对HashMap的排序功能,该方法接收HashMap<Integer,User>为形参,返回类型为HashMap<Integer,User>,要求对HashMap中User的age进行倒序进行排序。排序时key=value键值对不得拆散。
注意:要做出这道题必须对集合的体系结构非常的熟悉。HashMap本身就是不可排序的,但是该道题偏偏让给HashMap排序,那我们就得想在API中有没有这样的Map结构是有序的,LinkedHashMap,对的,就是他,它是Map结构,也是链表结构,有序的,更可喜的是它是HashMap的子类,我们返回LinkedHashMap即可,还符合面向接口。
单发是对集合的操作,我们应该保持一个原则就是能用JDK中的API就用,比如排序算法我们不应该用冒泡或者选择,而是首先想到Collections集合工具类。
public static void main(String[] args) {
HashMap<Integer, User> users = new HashMap<>();
users.put(1, new User("张三", 25));
users.put(3, new User("李四", 22));
users.put(2, new User("王五", 28));
System.out.println(users);
HashMap<Integer, User> sortHashMap = sortHashMap(users);
System.out.println(sortHashMap);
}
public static HashMap<Integer, User> sortHashMap(HashMap<Integer, User> map) {
// 首先拿到map键值对集合
Set<Map.Entry<Integer, User>> entrySet = map.entrySet();
// 将set集合转为List集合,为了使用工具类的排序方法
List<Map.Entry<Integer, User>> list = new ArrayList<>(entrySet);
// 使用Collections集合工具类对list进行排序,排序规则使用匿名内部类来实现
Collections.sort(list, new Comparator<Map.Entry<Integer, User>>() {
@Override
public int compare(Map.Entry<Integer, User> o1, Map.Entry<Integer, User> o2) {
// 按照规则根据User的age的倒叙进行排序
return o2.getValue().getAge() - o1.getValue().getAge();
}
});
// 创建一个新的有序的HashMap子类的集合
LinkedHashMap<Integer, User> linkedHashMap = new LinkedHashMap<>();
// 将List中数据存储在LinkedHashMap中
for (Map.Entry<Integer, User> entry : list) {
linkedHashMap.put(entry.getKey(), entry.getValue());
}
// 返回结果
return linkedHashMap;
}
二、集合的安全性问题
集合的安全性指的就是多线程模式中,集合是否还安全,众所周知,Vector是线程安全的,ArrayList线程不安全,HashTable线程安全,HashMap线程不安全。打开源码就会发现,大部分线程安全的操作,就是在核心方法中加上synchronized关键字。
Collections工具类中提供了相关的API,可以让部分集合变为线程安全
三、ArrayList内部是怎么实现的?
ArrayList使用Object[]数组实现的。
1、构造函数
a.空参构造函数
private static final Object[] EMPTY_ELEMENTDATA = {};
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
当空参的时候,直接创建了一个空的不可修改的数组
b.构造函数1
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
当构造函数传入参数是整型时,代表创建了一个自定义长度的数组
c.构造函数2
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
当构造函数为集合时,第二行会将集合转为数组的形式,至于为什么下面要对elementData.getClass() != Object[].class进行判断,我也说不好,因为在第二行转换过后,一般该条件都不成立。
2、add方法
a.构造函数1
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
当构造函数的参数是单独一个泛型时,构造函数中ensureCapacityInternal方法要计算add之后需要的空间
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
这个方法里嵌套了两个函数,外层的函数 ensureExplicitCapacity要给ArrayList进行扩容操作,里层函数calculateCapacity是为了计算当前ArrayList需要的空间大小是多少
private static final int DEFAULT_CAPACITY = 10;
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
这里可以看出,add方法执行的是时候,默认最小的ArrayList空间大小为DEFAULT_CAPACITY,也就是10。
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
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);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
在这里可以看出,当add方法执行时,如果数组空间大小不够,会根据grow方法中的条件,进行扩容
b.构造方法2
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
另一个add方法是要在指定的位置插入指定的元素,实现方法哈上一个构造方法大体相同。
3、remove方法
remove方法有两种方式,第一种是传入int,第二种是传入Object,这里我们只讲第一种,感兴趣的小伙伴可以自行查阅源代码了解另一种实现方式。
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
这里我们看到, E oldValue = elementData(index)这句会通过传入的int参数获取到对应的数组元素,然后通过 System.arraycopy方法对数组进行复制,同时对原数组进行remove操作
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
操作之后改变了原数组的内容,进而实现了对ArrayList集合的remove操作。
4、clear方法
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
这个方法的目的很清晰,就是要将ArrayList集合置空,具体的2实现就是先把原数组中所有的元素都修改成null,然后将集合的的size修改成0,达到集合置空的效果。
四、List三个子类的特点
ArrayList:底层实现是数组,查询快,增删慢;
LinkedList:底层实现是链表,查询慢,增删快;
vector:底层是现实数组,线程安全,查询慢,增删慢;
五、List、Set、Map的特点、实现类和区别
特点:
- List和Set是单列存储数据的集合,Map是双列(键值对)存储数据的集合;
- List有序且允许重复;
- Map无序,key不允许重复,value可以重复;
- Set是无序的,且不允许重复;
实现类:
List:LinkedList、ArrayList、vector
Map:HashMap、HashTable、LinkedHashMap、TreeMap
Set:HashSet、LinkedHashSet
区别:
同特点;
六、HashTable和HashMap有什么区别?
- HashMap线程不安全,不允许key重复,允许key为空值;
- HashTable线程安全,不允许key或者value为空值;
- 由于非线程安全,HashMap比HashTable访问效率要高一些;
七、List a=new ArrayList()和ArrayList a = new ArrayList()有什么区别?
前者创建了一个ArrayList对象之后把它上溯到List,因此他是一个List对象,有些ArrayList中特有的方法便不能再用了。
八、Collection和Map的继承体系
Collection:
Map: