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);
}
|