在开发过程中如果多处用到同一个List且会修改List元素内容的时候,如果想要不互相干扰就需要进行深拷贝。
// 浅拷贝 List<Msg> source = new ArrayList<>(); List<Msg> target= new ArrayList<>(source); // 浅拷贝 List<Msg> target= new ArrayList<>(); target.addAll(source); 复制代码
@Data @Builder @NoArgsConstructor @AllArgsConstructor public class Msg implements Serializable { private static final long serialVersionUID = -3893311742256460336L; private String detail; private String body; } public static void main( String[] args ) { List<Msg> msgs = new ArrayList<>(); Msg msg1 = new Msg("davids", "body"); msgs.add(msg1); for (Msg msg : msgs) { msg.setBody("one" + msg.getBody()); System.out.println(msg); } for (Msg msg : msgs) { msg.setBody("two" + msg.getBody()); System.out.println(msg); } } // output Msg(detail=davids, body=onebody) Msg(detail=davids, body=twoonebody) // 试试new ArrayList<>(source); public static void main( String[] args ) { List<Msg> msgs = new ArrayList<>(); Msg msg1 = new Msg("davids", "body"); msgs.add(msg1); List<Msg> one = new ArrayList<>(msgs); for (Msg msg : one) { msg.setBody("one" + msg.getBody()); System.out.println(msg); } List<Msg> two = new ArrayList<>(msgs); for (Msg msg : two) { msg.setBody("two" + msg.getBody()); System.out.println(msg); } } // output 跟new之前一样 Msg(detail=davids, body=onebody) Msg(detail=davids, body=twoonebody) // 看看源码 public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { if (elementData.getClass() != Object[].class) // 内部采用的Arrays.copyOf,看看里面是什么 elementData = Arrays.copyOf(elementData, size, Object[].class); } else { this.elementData = EMPTY_ELEMENTDATA; } } // Arrays.copyOf public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) { @SuppressWarnings("unchecked") T[] copy = ((Object)newType == (Object)Object[].class) ? (T[]) new Object[newLength] : (T[]) Array.newInstance(newType.getComponentType(), newLength); // 内部调用System.arraycopy 表面上生成了一个新数组,其实指针指向的是同一块内存 System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); return copy; } // 试试target.addAll(source ); public static void main( String[] args ) { List<Msg> msgs = new ArrayList<>(); Msg msg1 = new Msg("davids", "body"); msgs.add(msg1); List<Msg> one = new ArrayList<>(); one.addAll(msgs); for (Msg msg : one) { msg.setBody("one" + msg.getBody()); System.out.println(msg); } List<Msg> two = new ArrayList<>(); two.addAll(msgs); for (Msg msg : two) { msg.setBody("two" + msg.getBody()); System.out.println(msg); } } // output 还是跟之前一样 Msg(detail=davids, body=onebody) Msg(detail=davids, body=twoonebody) // 看看源码 public boolean addAll(Collection<? extends E> c) { Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); // 同样使用的是System.arraycopy 表面上生成了一个新数组,其实指针指向的是同一块内存 System.arraycopy(a, 0, elementData, size, numNew); size += numNew; return numNew != 0; } 复制代码
// BeanUtils.cloneBean @SneakyThrows public static void main( String[] args ) { List<Msg> msgs = new ArrayList<>(); Msg msg1 = new Msg("davids", "body"); msgs.add(msg1); for (Msg msg : msgs) { Msg msg2 = (Msg) BeanUtils.cloneBean(msg); msg2.setBody("one" + msg2.getBody()); System.out.println(msg2); } for (Msg msg : msgs) { Msg msg2 = (Msg) BeanUtils.cloneBean(msg); msg2.setBody("two" + msg2.getBody()); System.out.println(msg2); } } // output ok Msg(detail=davids, body=onebody) Msg(detail=davids, body=twobody) // IO序列化 public static void main( String[] args ) { List<Msg> msgs = new ArrayList<>(); Msg msg1 = new Msg("davids", "body"); msgs.add(msg1); List<Msg> one = ObjectUtil.cloneByStream(msgs); for (Msg msg : one) { msg.setBody("one" + msg.getBody()); System.out.println(msg); } List<Msg> two = ObjectUtil.cloneByStream(msgs); for (Msg msg : two) { msg.setBody("two" + msg.getBody()); System.out.println(msg); } } // output ok Msg(detail=davids, body=onebody) Msg(detail=davids, body=twobody) 复制代码
反射相对于IO序列化更依赖CPU和内存,IO序列化则相对而言更依赖内存和磁盘,我本机测试10w、100w、500w情况下IO序列化都略优于反射。