集合
● 当我们需要保存一组一样(类型相同)的元素的时候,
我们应该使用一个容器 来存储,数组就是这样一个容器。
● 数组有什么缺点?
● 数组一旦定义,长度将不能再变化。
● 然而在我们的开发实践中,经常需要保存一些变长的数据集合,
于是,我们需要一些能够动态增长长度的容器来保存我们的数据。
● 而我们需要对数据的保存的逻辑可能各种各样,于是就有了各种各样的数据结构。
Java中对于各种数据结构的实现,就是我们用到的集合。
集合 API
Java集合API是Java中用于存储和操作数据集合的框架,主要包括以下常用的集合接口和类:
集合容器类默认可以添加Object类型,
但是还是习惯一个集对象只保存同一种类型;
因为保存多个类型,当后期处理时,设计类型转换问题;
-
Collections类
● Collections是集合类的工具类,与数组的工具类Arrays类似
Collections类是Java中提供的一个工具类,用于操作集合类的工具方法。我们可以通过Collections类来对集合进行排序、检索、替换、调整大小等操作。
使用Collections类的方法很简单,首先需要导入java.util.Collections包,在需要使用的地方创建Collections对象,然后调用其中的方法即可。
例如,对一个List集合进行排序:
List<Integer> list = new ArrayList<>(); list.add(3); list.add(1); list.add(2); Collections.sort(list); System.out.println(list); // 输出 [1, 2, 3]
需要注意的地方包括:
-
Collections类中的方法都是静态方法,直接通过类名调用即可。
-
Collections类提供了丰富的静态方法,查阅官方文档或者IDE的代码提示可以获取更多的使用方法。
-
在使用Collections类的方法时,需要注意集合的泛型类型,确保操作的对象和方法参数类型一致。
T...element//可变长度参数
如:int...a;
test(int...a);
test(1,2,3,4,5,6,7,8);
其本质是一个数组,一个参数列表只能有一个可变长度参数,且必须放在参数列表最后;
如:public static void test(int a,int...b){
}
Collections.sort(list);//默认升序 //想要降序,就要重写方法 //内部类,直接在内部定义,不用定义一个新的类 Collections.sort(list,new Comparator<Integer>(){ @Override public int compare(Integer o1,Integer o2){ return o2.intValue()-o1.intValue(); } })
-
-
List接口:
List是有序集合,允许重复元素。常用的实现类有:
ArrayList(数组列表,数据采用数组方式存储):
底层有一个数组,可以动态扩展数组长度,并提供一个一系列方法操作. 查询快 中间增加,删除慢
//**泛型:可以在声明类型时,自定义参数类型;** ArrayList<String> arraylist = new ArrayList(); arraylist.add();//增加元素 例:arraylist.add("a");//往后增添; arraylist.add(0,"e");//指定位置添加元素,不会替换原位置的元素,而是其后全部自动往后移动一位; arraylist.remove("a");//按照元素删除,其余元素自动往前补一位 arraylist.remove(1);//按照位置删除 //其有返回值boolean),根据内容删除匹配第一个元素,删除成功返回true,否则返回false; arraylist.get(1);//获取指定位置上的元素; arraylist.indexOf("a");//返回这个元素首次出现的位置; arraylist.lastindexOf("a");//返回这个元素从后往前首次出现的位置; arraylist.set(1,"a");//将该元素赋给定位置,并返回原元素; arraylist.clear();//清空集合中的元素; arraylist.isEmpty();//判断集合中的元素是否为空,为空返回true,否则返回false; arraylist.contains("a");//判断是否包含某一个元素,若有返回true,否则返回false; arraylist.size();//返回集合中元素的个数;
ArrayList.addAll()
方法用于将另一个集合中的所有元素添加到 ArrayList 中。在使用该方法时,需要注意以下几点限制:-
类型匹配:被添加的集合和目标 ArrayList 的元素类型必须匹配。如果类型不匹配,会导致编译错误。
-
容量限制:在将一个集合中的元素添加到 ArrayList 时,需要考虑目标 ArrayList 的容量是否足够。如果目标 ArrayList 的容量不足以容纳要添加的所有元素,会触发扩容操作,导致性能下降。
-
并发问题:
ArrayList.addAll()
方法不是线程安全的,如果在多个线程同时操作这个方法,可能会导致并发问题。可以通过在操作时进行适当的同步措施或使用线程安全的集合类来避免这种问题。 -
返回值:
ArrayList.addAll()
方法返回一个布尔值,表示是否添加成功。成功添加会返回 true,否则返回 false。
总的来说,
ArrayList.addAll()
方法是一个方便快捷的方式将多个元素一次性添加到 ArrayList 中,但在使用时需要注意上述限制,以确保正常运行和性能。Collections.addAll()
方法是一个静态方法,用于将多个元素添加到一个 Collection 集合中。它的参数包括一个目标集合和一个可变参数列表,用于指定待添加的元素。使用
Collections.addAll()
方法有以下限制:-
类型匹配:被添加的元素的类型必须与目标集合的类型相匹配,否则会导致编译错误。
-
空值处理:要添加的元素不能为 null,否则会抛出 NullPointerException 异常。
-
不支持基本数据类型:
Collections.addAll()
方法不支持直接添加基本数据类型,需要先将基本数据类型转换成对应的包装类再进行添加。 -
容量问题:如果目标集合的容量不足以容纳待添加的元素,会触发扩容操作,导致性能下降。
-
返回值:
Collections.addAll()
方法没有返回值,它直接修改了目标集合,将元素添加到了目标集合中。 -
线程安全:
Collections.addAll()
方法不是线程安全的,如果在多线程环境下使用,需要进行适当的同步操作。
总的来说,
Collections.addAll()
方法是一个方便用于向集合中批量添加元素的工具方法,但在使用过程中需要留意上述限制条件,以确保正确性和性能。LinkedList(链表)
底层是一个链表结构 查询慢 增加,删除快
//**泛型:可以在声明类型时,自定义参数类型;** LinkedList<String> linkedlist = new LinkedList(); linkedlist.add();//增加元素 linkedlist.add(1,"a");//指定位置增加元素 linkedlist.addFirst();//首位添加 linkedlist.get(1);//获取指定位置上的游戏; linkedlist.remove("a");//按照元素删除 linkedlist.remove(1);//按照位置删除 linkedlist.remove();//删除第一个元素 linkedlist.First();//删除第一个元素 linkedlist.removeFirst();//删除第一个元素
Vector(数组列表,添加同步锁,线程安全的)
Vector<String> vector = new Vector(); //方法同上 //区别:线程安全的
List接口集合迭代
//● for循环遍历 for(int i=0;i <= arraylist.size();i++){ if("a".equals(arraylist.get(i)) ) { arraylist.remove("a"); i--; } System.out.println(arraylist.get(i);); } //● 增强for循环的 for(String s: arraylist){ /*if(s.equals("a")){ arraylist.remove(s); //增强遍历中,不允许修改集合,否则会报错,只允许遍历 }*/ System.out.println(s); } //● 迭代器遍历(Iterator) //获取集合对象的迭代器对象 Iterator<String> it = arraylist.iterator(); while(it.hasNext()){//判断还有没有元素 String s = it.next();//获取到元素; if(s.equals("a")){ it.remove();//使用迭代器对象删除元素; } }
-
-
Set接口:
Set是无序集合,不允许重复元素。常用的实现类有HashSet和TreeSet。
● HashSet
HashSet类中的元素不能重复
1. add(E e): //向HashSet中添加元素。 2. remove(Object o): //从HashSet中移除指定元素。 3. contains(Object o): //判断HashSet是否包含指定元素。 4. size(): //返回HashSet中元素的数量。 5. isEmpty(): //判断HashSet是否为空。 6. clear()://清空HashSet中的所有元素。 7. iterator(): //返回一个迭代器,可以用来遍历HashSet中的元素。 8. containsAll(Collection<?> c): 判断HashSet//是否包含指定集合中的所有元素。 9. addAll(Collection<? extends E> c): //将指定集合中的所有元素添加到HashSet中。 10. removeAll(Collection<?> c): //移除HashSet中包含在指定集合中的全部元素。 11. retainAll(Collection<?> c): //保留HashSet中与指定集合中相同的元素,移除其他元素。 12. toArray(): //将HashSet中的元素转换为数组。 13. equals(Object o): //判断HashSet是否与指定对象相等。 14. hashCode(): //返回HashSet的哈希码值。
● TreeSet
可以给Set集合中的元素进行指定方式的排序。
存储的对象必须实现Comparable接口
必须重写compareTo(),每次添加元素时,调用compareTo()进行元素大小判断(小于0放左子结点,等于0表示重复,覆盖元素,大于0放在右子节点)
//不能存储重复元素
Map接口
将键映射到值的对象
一个映射不能包含重复的键
每个键最多只能映射到一个值
Map接口共性:
数据存储是建:值的形式存储
键不可以重复,只可以重复
通过键可以找到值
一个键只能映射一个值
在Java中,Map接口被实现的对象有HashMap、TreeMap、LinkedHashMap、ConcurrentHashMap等。Map接口定义了一种键值对的数据结构,其中键是唯一的,每个键对应一个值。
常用的Map接口方法包括:
1. put(K key, V value)://将指定的值与指定的键关联 2. get(Object key)://返回指定键所映射的值,如果键不存在则返回null 3. containsKey(Object key)://判断是否包含指定的键 4. containsValue(Object value)://判断是否包含指定的值 5. keySet()://返回所有键的集合 6. values()://返回所有值的集合 7. entrySet()://返回所有键值对的集合 8. remove(Object key)://删除指定键的映射关系 9.getOrDefault(Object key, V defaultValue)//方法用于获取指定 key 对应的 value,如果 key 存在则返回相应的 value,如果 key 不存在则返回指定的 defaultValue。
在使用Map接口的实现类时,需要注意的事项包括:
-
HashMap是无序的,即键值对的顺序不确定。如果需要有序的键值对集合,可以使用TreeMap或LinkedHashMap。
-
HashMap允许null值和null键,但是不保证多个线程访问时的同步。
-
-
ConcurrentHashMap是线程安全的Map实现类,多个线程可以同时对其进行读操作,而不会出现线程安全问题。
-
在遍历Map时,可以使用keySet()、values()或entrySet()方法,根据具体需求选择最合适的方式。
遍历Map可以使用三种方式:keySet()、values()和entrySet()。
-
keySet()方法:通过keySet()方法可以获取Map中所有键的集合,然后可以通过遍历键集合来获取对应的值。示例代码如下:
Map<String, Integer> map = new HashMap<>(); map.put("A", 1); map.put("B", 2); for (String key : map.keySet()) { Integer value = map.get(key); System.out.println(key + " = " + value); }
-
values()方法:通过values()方法可以直接获取Map中所有值的集合,然后遍历值集合。示例代码如下:
Map<String, Integer> map = new HashMap<>(); map.put("A", 1); map.put("B", 2); for (Integer value : map.values()) { System.out.println(value); }
-
entrySet()方法:通过entrySet()方法可以获取Map中所有键值对的集合,然后遍历键值对。示例代码如下:
Map<String, Integer> map = new HashMap<>(); map.put("A", 1); map.put("B", 2); for (Map.Entry<String, Integer> entry : map.entrySet()) { String key = entry.getKey(); Integer value = entry.getValue(); System.out.println(key + " = " + value); }
区别:
-
keySet()方法返回的是Map中所有键的集合,通过遍历获取对应的值,适用于需要遍历键并通过键获取值的情况。
-
values()方法返回的是Map中所有值的集合,适用于只需要遍历值的情况。
-
entrySet()方法返回的是Map中所有键值对的集合,通过迭代获取键和值,适用于需要同时访问键和值的情况。
根据具体需求选择最合适的遍历方式,可以提高代码的效率和可读性。
在使用keySet()、values()和entrySet()方法遍历Map时,需要注意以下几点:
-
遍历过程中是否需要修改Map:如果在遍历过程中需要对Map进行修改操作(如添加、删除元素),应当使用迭代器来遍历,并通过迭代器的remove()方法进行元素的安全删除。直接在foreach循环中修改Map可能会引发ConcurrentModificationException异常。
-
entrySet()方法的效率:在需要同时访问键和值的情况下,使用entrySet()方法遍历是最高效的方式,因为每次从entrySet()返回的Entry对象中同时包含了键值对,只需一次访问即可获取键和值,避免了重复的查找操作。
-
values()方法的性能:values()方法返回的是值的集合,如果需要频繁地根据键查找值,使用values()方法可能会导致性能下降,因为在获取值时需要再次遍历整个值的集合。
-
keySet()方法的用途:keySet()方法主要用于遍历键,并通过键获取对应的值,适用于只需要访问Map中的键和值的情况。
-
注意空指针异常:在遍历过程中,需要注意判断键或值是否为null,以避免空指针异常。
总的来说,根据具体需求选择最合适的遍历方式,并注意遍历过程中的一些细节问题,可以确保代码的安全性和性能。
-
-
● HashMap
HashMap中元素的key值不能重复,
排列顺序是不固定的,可以存储一个 为null的键
1.底层使用一个长度默认为16的数组,用来确定元素的位置,每次用key计算出哈希值,用哈希值%数组长度确定元素位置,将元素放在哈希表中指定位置;
2.后来继续添加元素,如果出现位置相同且不重复的元素,那么将后来的元素添加到之前元素的next节点;
3.当链表长度等于8时,且哈希数组长度大于64链表自动转为红黑树;
小于64仅扩容
4.哈希表负载因子为0.75
当哈希表使用数组的0.75时,会自动扩容为原来数组的2倍
map遍历:
先拿到所有的链,遍历链,根据链找值