JAVA集合Collection与Map
定西集合框架概述
数组的特点与弊端
特点
- 数组一旦初始化,长度就已经确定了
- 数组中的多个元素是依次紧密排列的,有序的,可重复
- 数组一旦初始化完成,其元素的类型就是确定的,不是此类型的元素,就不能添加到数组中
- 元素的类型既可以是基本数据类型,也可以是引用数据类型
弊端
- 数组一旦初始化,长度就已经不可变了
- 数组中存储数据特点的单一性,对于无序的,不可重复的场景的多个数据就不行了
- 数组中可用的方法,属性都极少
- 针对于数组中元素的删除,插入操作,性能较差(尾部添加性能并不差)
Java集合框架体系
java.util.Collection:存储一个一个数据
- List
- 存储有序的,可重复的数据
- ArrayList
- LinkedList
- Vector
- Set(底层其实就是Map,只使用了Map的键,来保证唯一,不可重复)
- 存储无序的,不可重复的数据
- HashSet
- LinkedHashSet
- TreeSet
java.util.Map:存储一对一对数据
- HashMap
- LinkedHashMap
- TreeMap
- Hashtable
- Properties
Collection接口
add(Object obj)/addAll(Collection coll)1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
@Test public void test1() { Collection collection = new ArrayList(); collection.add("aa"); collection.add("bb"); collection.add(123); collection.add(new Object()); collection.add(new Person("tom", 12)); System.out.println(collection);
Collection collection1 = new ArrayList(); collection1.add("cc"); System.out.println(collection1.size()); collection1.addAll(collection); System.out.println(collection1.size()); System.out.println(collection1); }
|
size(),isEmpty(),contains(Object obj),containsAll(Collection coll),equals()1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
@Test public void test2() { Collection collection = new ArrayList(); collection.add("aa"); collection.add("bb"); collection.add(128); collection.add(new Person("111", 123)); System.out.println(collection.isEmpty()); System.out.println(collection.contains(128)); System.out.println(collection.contains(new Person("111", 123)));
Collection coll1 = new ArrayList(); coll1.add("aa"); coll1.add("cc"); System.out.println(collection.containsAll(coll1)); }
|
clear(),remove(),removeAll(),retainAll()1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| @Test public void test3() { Collection coll1 = new ArrayList(); coll1.add("AA"); System.out.println(coll1); coll1.clear(); System.out.println(coll1);
coll1.add(new Person("tom", 11)); coll1.add(new Person("tom", 11)); coll1.remove(new Person("tom", 11)); System.out.println(coll1);
coll1.add("AA"); coll1.add(111);
Collection coll2 = new ArrayList(); coll2.add("AA"); coll2.add("BB"); coll2.add("CC");
coll1.retainAll(coll2); System.out.println(coll1);
}
|
Object[] toArray():返回包含当前集合中所有元素的数组
hashCode():获取集合对象的哈希值
iterator():返回迭代器对象,用于集合遍历(后面讲)1 2 3 4 5 6 7 8 9 10 11
| @Test public void test4() { Collection collection = new ArrayList(); collection.add("aa"); collection.add("bb"); collection.add(128); Object[] array = collection.toArray();
int i = collection.hashCode(); System.out.println(i); }
|
ArrayList集合和数组之间的转换
集合转换为数组,那么这个新的数组跟原集合就没有关系了,改变集合的值,不会影响到数组的值
但是如果是对数组使用Arrays.asList(Object[] obj)方法,来让一个数组转化为一个集合,此时的集合是只读(可以使用set,但是不能使用add和remove)的,且数组改变,会影响到集合的值
一个注意点
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Test public void test6() { Integer[] arr = new Integer[]{1, 2, 3}; List<Integer> list = Arrays.asList(arr); System.out.println(list); System.out.println(list.size());
int[] arr1 = new int[]{1, 2, 3}; List list1 = Arrays.asList(arr1); System.out.println(list1); System.out.println(list1.size()); }
|
小总结:向Collection中添加的元素,一般要求元素所属的类一定要重写equals,因为Collection中的相关方法(contains,remove)要调用equals()
iterator迭代器
用来遍历集合元素的
两个重要的方法
hashNext()
next():指针下移,将下移以后集合位置上的元素返回
搭配使用,可以用来遍历集合
1 2 3 4 5 6 7 8 9 10 11
| @Test public void test1() { Collection coll1 = new ArrayList(); coll1.add("AAA"); coll1.add("BBB"); coll1.add("CCC"); Iterator iterator = coll1.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } }
|
增强for循环的原理就是使用的迭代器,实际上是语法糖,来简化迭代器的使用
List
List,用于存储有序的,可以重复的数据
List中的常用方法除了Collection中的方法外,还有一些其他的
因为List是有序的,进而就有索引,就会有一些针对索引的操作方法
remove(int index)删除指定索引的元素
set(int index,Object obj)给指定索引设置值
get(int index)获取指定索引的元素
add(int index,Object obj)指定位置插入
addAll(int index,Collection eles)插入多个
indexOf(Object obj)返回obj在集合中首次出现的位置
lastIndexOf(Object obj)返回obj在当前集合中末次出现的位置
subList(int fromIndex,int toIndex)返回从fromIndex到toIndex-1位置的子集合
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Test public void test1() { List list = new ArrayList(); list.add("111"); list.add("222"); list.add(new Person("BB", 1)); list.forEach(System.out::println);
System.out.println("===");
list.add(1, "testInsert"); list.forEach(System.out::println);
}
|
三个List实现类
- ArrayList:最常用的List实现类
- LinkedList:基于双向链表的结构
- Vector:List的一个古老的实现类
Set
Set:存储无序的,不可重复的数据
开发中,相较于List和Map,Set使用比较少
Set中的实现类
- HashSet:底层使用的是HashMap
- LinkedHashSet(继承的HashSet,在现有的结构上,又添加了一组双向链表,用于记录添加元素的先后顺序,即:我们可以按照添加元素的顺序实现遍历,便于频繁的查询操作)
- TreeSet:底层使用红黑树存储,可以按照添加的元素的指定的属性进行遍历
HashSet
- 无序性 != 随机性
这里的无序性,是指添加元素的位置是无序的,不像ArrayList一样是依次排列紧密的
- 不可重复性
添加到Set中的元素是不能相同的,比较的标准:需要判断hashCode得到哈希值与equals得到的结果,哈希值相同且equals返回true才是相同
如果Person没有重写hashCode方法,那么最下面的语句就会输出false,如果没有重写,调用的就是Object中的hashCode,Object 类的默认 hashCode 实现是为每个不同的对象实例返回不同的哈希码,即使这些对象在逻辑上是“相等的”(即它们的 equals 方法可能返回 true)。,这也就解释了为什么不重写hashCode,下面语句就返回false了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Test public void test1() { Set set = new HashSet();
set.add("AA"); set.add(123); set.add("BB"); set.add("CC"); set.add("DD"); set.add(new Person("tom", 11)); for (Object o : set) { System.out.println(o); }
System.out.println(set.contains(new Person("tom", 11))); }
|
小总结:添加到HashSet/LinkedHashSet中的元素的要求:要求元素所在的类要重写equals和hashCode方法
TreeSet
TreeSet的底层是红黑树,添加数据后,可以按照添加的元素的指定的大小顺序进行遍历
要求添加到TreeSet中的元素必须是同一个类型的对象,否则会报类型转换异常,因为TreeSet中会将其元素相互比较
1 2 3 4 5 6 7 8 9 10 11 12
| @Test public void test1() { TreeSet set = new TreeSet(); set.add("AA"); set.add("BB"); set.add("CC"); set.add("DD"); set.add(123); for (Object o : set) { System.out.println(o); } }
|
TreeSet添加自定义的类,那自定义类要实现Comparable接口,或者TreeSet中传入一个Comparator对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Override public int compareTo(Object o) { if (o == this) { return 0; } if (o instanceof User) { User u = (User) o; int result1 = Integer.compare(this.age, u.age); if (result1 != 0) { return result1; } return this.name.compareTo(u.name); } throw new RuntimeException("类型错误"); }
|
如果这个compareTo方法中只比较了一个属性,那么在插入对象时,这个属性相同,就会判断为相同的属性,根本不会调用equals和hashCode方法,也就是说,在TreeSet中,判断相同的标准就是使用排序的方法了
下面是使用定制排序的方式,也就是不需要自定义类实现Comparable接口,而是给TreeMap传入一个Comparator接口的实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @Test public void test3() { Comparator comparator = (o1, o2) -> { if (o1 instanceof User && o2 instanceof User) { User u1 = (User) o1; User u2 = (User) o2; int result = u1.getName().compareTo(u2.getName()); if (result != 0) { return -result; } return Integer.compare(u1.getAge(), u2.getAge()); } throw new RuntimeException("类型错误"); }; TreeSet set = new TreeSet(comparator); set.add(new User("tom", 11)); set.add(new User("aom", 13)); set.add(new User("bom", 13)); set.add(new User("tom", 14));
set.forEach(System.out::println); }
|
Set案例
定义方法如下:public static List duplicateList(List list)
- 参数List只存在Integer的对象
- 在List内去除重复数字值,尽量简单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public class TestDup { public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(1); list.add(2); list.add(2); list.add(3); List result = duplicateList(list); list.forEach(System.out::println); System.out.println("==="); result.forEach(System.out::println); }
public static List duplicateList(List<Integer> list) { HashSet<Integer> hashSet = new HashSet<>(list); List resultList = new ArrayList(hashSet); return resultList; }
|
Set案例二
编写一个程序,获取10个1-20的随机数,要求随机数不能重复,并把最终的随机数输出到控制台
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class TestRandom { public static void main(String[] args) { HashSet<Integer> set = new HashSet(); List<Integer> list = new ArrayList(); while (set.size() < 20) { int a = (int) (Math.random() * 20) + 1; list.add(a); set.add(a); } set.forEach(System.out::println); System.out.println("+++"); System.out.println(list.size()); } }
|
还有一个案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class HashSetDemo { public static void main(String[] args) { Set<Person> set = new HashSet(); Person p1 = new Person(1001, "AA"); Person p2 = new Person(1002, "BB"); set.add(p1); set.add(p2); System.out.println(set);
p1.setName("CC"); set.remove(p1); System.out.println(set);
set.add(new Person(1001, "CC")); System.out.println(set);
set.add(new Person(1001, "AA")); System.out.println(set); } }
|

Map接口
java.util.Map:存储一对一对数据
- HashMap,Map主要实现类,线程不安全的,可以添加null的key和value值
- LinkedHashMap,是HashMap的子类,在HashMap使用的数据结构的基础上,增加了双向链表,用于记录添加元素的先后顺序,可以按照添加的元素的指定的大小顺序进行遍历,对于频繁遍历的数据,可以优先考虑此结构
- TreeMap,底层使用红黑树存储,可以按照添加的key-value中的key元素的指定属性的大小顺序进行遍历
- Hashtable,Map的古老实现类,线程安全的,不可以添加null的key和value值
- Properties,其key和value都是String类型,常用来处理属性文件
HashMap
HashMap中的key用Set来存放,不允许重复,即同一个HashMap对象所对应的类,须重写hashCode()和equals方法(不重写,也能存,不过可能会有问题,参考Set)
HashMap中的一个key-value就构成了一个Entry,所以的entry彼此之间是不可重复的,无序的,所有的entry就构成了一个Set集合
常用方法
- 添加/修改
- Object put(key,value)将指定的key-value添加到(或修改)当前map对象中
- void putAll(Map m)将m中所有的key-value对存放到当前map中
- 删除操作
- Object remove(key):移除指定key的key-value对,并返回value
- void clear(),清空当面map中的所有数据
- 元素查询操作
- Object get(Object key),获取指定key对应的value
- containsKey(key)是否包含指定的key
- containsValue(Object value),是否包含指定的value
- int size()返回map中key-value对的个数
- isEmpty(),判断当前map是否为空
equals(Object obj):判断当前map和参数对象obj是否相等
- 元视图操作的方法
- Set keySet():返回所有key构成的set集合
- Collection values()返回所有value构成的Collection集合
- Set entrySet(),返回所有key-value对构成的Set集合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| @Test public void test3() { Map<String, String> map = new LinkedHashMap<>(); map.put("11", "22"); map.put("110", "220"); map.put("12", "00"); map.put("00", "11"); Set<String> strings = map.keySet(); System.out.println(strings); Collection<String> values = map.values(); System.out.println(values); Set<Map.Entry<String, String>> entries = map.entrySet(); System.out.println(entries); System.out.println("--"); for (Map.Entry<String, String> entry : entries) { String key = entry.getKey(); String value = entry.getValue(); System.out.println("key=" + key + " value=" + value); } }
@Test public void test4() { Map<String, String> map = new LinkedHashMap<>(); map.put("11", "22"); map.put("110", "220"); map.put("12", "00"); map.put("00", "11");
for (String s : map.keySet()) { String values = map.get(s); System.out.println(values); }
System.out.println("===");
String value = map.remove("00"); System.out.println(value);
System.out.println("===");
for (String s : map.keySet()) { String values = map.get(s); System.out.println(values); }
System.out.println("---"); map.put("11", "testUpdate"); System.out.println(map); }
|
TreeMap
可以按照添加的key-value中的key元素的指定属性的大小顺序进行遍历
需要考虑使用自然排序或定制排序,且TreeMap中添加的key必须是同一个类型的对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| @Test public void test2() { Map<User, String> map = new TreeMap<>(); map.put(new User("tom", 11), "11"); map.put(new User("aom", 13), "22"); map.put(new User("tom", 13), "33"); map.put(new User("tom", 14), "44"); Set<Map.Entry<User, String>> entries = map.entrySet(); for (Map.Entry<User, String> entry : entries) { System.out.println(entry); } }
@Test public void test3() { Comparator comparator = (o1, o2) -> { if (o1 instanceof User && o2 instanceof User) { User u1 = (User) o1; User u2 = (User) o2; int result1 = u1.getName().compareTo(u2.getName()); if (result1 != 0) { return result1; } return u1.getAge() - u2.getAge(); } throw new RuntimeException("类型错误"); }; Map<User, String> map = new TreeMap<>(comparator); map.put(new User("tom", 11), "11"); map.put(new User("aom", 13), "22"); map.put(new User("tom", 13), "33"); map.put(new User("tom", 14), "44"); Set<Map.Entry<User, String>> entries = map.entrySet(); for (Map.Entry<User, String> entry : entries) { System.out.println(entry); } }
|
还有关于Properties的使用
1 2 3 4 5 6 7 8 9 10
| @Test public void test1() throws IOException { File file = new File("test.properties"); System.out.println(file.getAbsolutePath()); FileInputStream fis = new FileInputStream(file); Properties properties = new Properties(); properties.load(fis); System.out.println(properties.getProperty("name")); System.out.println(properties.getProperty("password")); }
|
Collections工具类
Collections是一个操作Set,List和Map等集合的工具类
Collection和Collections的区别
Collection是集合框架中的用于存储一个一个元素的接口,又分为List和Set等子接口,而Collections是用于操作集合框架的工具类
常用api
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| @Test public void test1() { List<Integer> list = new ArrayList<>(); list.add(1); list.add(2); list.add(3); list.add(4); list.add(5);
Collections.shuffle(list); System.out.println(list);
Collections.sort(list); System.out.println(list);
Collections.sort(list, (o1, o2) -> -o1 - o2); System.out.println(list);
Collections.swap(list, 0, 4); System.out.println(list); }
@Test public void test2() { List<String> list = Arrays.asList("AA", "BB", "CC", "DD"); String max = Collections.max(list); System.out.println(max);
String max1 = Collections.max(list, (o1, o2) -> -o1.compareTo(o2)); System.out.println(max1);
String min = Collections.min(list); System.out.println(min);
String min1 = Collections.min(list, (o1, o2) -> -o1.compareTo(o2)); System.out.println(min1);
int i = Collections.binarySearch(list, "AA"); System.out.println(i);
int time = Collections.frequency(list, "AA"); System.out.println(time); }
@Test public void test3() { List<String> src = Arrays.asList("AA", "BB", "CC", "DD"); List<Object> dest = Arrays.asList(new Object[src.size()]); Collections.copy(dest, src); System.out.println(dest); }
@Test public void test4() { List<Integer> list = new ArrayList<>(); list.add(1); list.add(2);
List<Integer> unmodifiableList = Collections.unmodifiableList(list); unmodifiableList.set(0, 1); System.out.println(unmodifiableList); }
@Test public void test5() { List<Integer> list = new ArrayList<>(); list.add(1); list.add(2);
List<Integer> list1 = Collections.synchronizedList(list); System.out.println(list1);
Map<String, String> map = new HashMap<>(); map.put("11", "22"); Map<String, String> map1 = Collections.synchronizedMap(map); System.out.println(map1); }
|
一个发牌案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| @Test public void test6() { String[] num = {"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"}; String[] color = {"方片", "梅花", "红桃", "黑桃"};
ArrayList<String> poker = new ArrayList<>(); for (int i = 0; i < num.length; i++) { for (int i1 = 0; i1 < color.length; i1++) { poker.add(num[i] + color[i1]); } }
poker.add("大王"); poker.add("小王");
Collections.shuffle(poker);
System.out.println(poker.size());
List<String> one = new ArrayList<>(); List<String> two = new ArrayList<>(); List<String> three = new ArrayList<>(); List<String> last = new ArrayList<>();
for (int i = 0; i < poker.size(); i++) { if (i >= poker.size() - 3) { last.add(poker.get(i)); } if (i % 3 == 0) { one.add(poker.get(i)); } else if (i % 3 == 1) { two.add(poker.get(i)); } else if (i % 3 == 2) { three.add(poker.get(i)); } }
System.out.println(one); System.out.println(two); System.out.println(three); System.out.println(last);
}
|
Map练习题
其实是两道面试题,都是使用到了HashMap
查找排名最接近荣誉的排名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public class ClosestRankFinder { public static int findClosestRank(Map<Integer, Integer> user) { int closestRank = -1; int minDifference = Integer.MAX_VALUE;
for (Map.Entry<Integer, Integer> entry : user.entrySet()) { int currentRank = entry.getKey(); int currentHonor = entry.getValue(); int difference = Math.abs(currentRank - currentHonor);
if (difference <= minDifference) { minDifference = difference; closestRank = currentRank; } }
return closestRank; }
public static void main(String[] args) { Map<Integer, Integer> user = new HashMap<>(); user.put(1, 93); user.put(10, 55); user.put(15, 30); user.put(20, 19); user.put(23, 11); user.put(30, 2); int closestRank = findClosestRank(user); System.out.println("排名最接近荣誉值的是:" + closestRank); } }
|
判断str1字符串重排后是否能组成str2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @Test public void test3() { String str1 = "kstedak"; String str2 = "steakk";
Map<Character, Integer> map = new HashMap<>(); char[] charArray = str1.toCharArray(); for (char c : charArray) { map.put(c, map.getOrDefault(c, 0) + 1); }
for (char c : str2.toCharArray()) { if (!map.containsKey(c) || map.get(c) == 0) { System.out.println(false); return; } map.put(c, map.get(c) - 1); } System.out.println(true);
}
|