如何在Java中join两个列表?
条件:不要修改原来的列表; 只有JDK,没有外部库。 单线或JDK 1.3版本的奖励积分。
有没有比以下更简单的方法:
List<String> newList = new ArrayList<String>(); newList.addAll(listOne); newList.addAll(listTwo);
在我头顶,我可以缩短一行:
List<String> newList = new ArrayList<String>(listOne); newList.addAll(listTwo);
在Java 8中:
List<String> newList = Stream.concat(listOne.stream(), listTwo.stream()) .collect(Collectors.toList());
你可以使用Apache的commons-collections库:
List<String> newList = ListUtils.union(list1,list2);
可能不简单,但有趣和丑陋:
List<String> newList = new ArrayList<String>() { { addAll(listOne); addAll(listTwo); } };
不要在生产代码中使用它;;)
你的要求之一是保留原来的名单。 如果您创build了一个新列表并使用addAll()
,则实际上将对列表中对象的引用数量加倍。 这可能会导致内存问题,如果你的名单是非常大的。
如果不需要修改连接结果,则可以使用自定义列表实现来避免这种情况。 自定义实现类不只一行,显然…但使用它是短而甜。
CompositeUnmodifiableList.java:
public class CompositeUnmodifiableList<E> extends AbstractList<E> { private final List<E> list1; private final List<E> list2; public CompositeUnmodifiableList(List<E> list1, List<E> list2) { this.list1 = list1; this.list2 = list2; } @Override public E get(int index) { if (index < list1.size()) { return list1.get(index); } return list2.get(index-list1.size()); } @Override public int size() { return list1.size() + list2.size(); } }
用法:
List<String> newList = new CompositeUnmodifiableList<String>(listOne,listTwo);
不简单,但不调整开销:
List<String> newList = new ArrayList<>(listOne.size() + listTwo.size()); newList.addAll(listOne); newList.addAll(listTwo);
发现这个问题看来连接任意数量的列表,而不是外部库。 所以,也许它会帮助别人:
com.google.common.collect.Iterables#concat()
如果你想把相同的逻辑应用于()中的许多不同的集合,那么这很有用。
另一个Java 8单行版本:
List<String> newList = Stream.of(listOne, listTwo) .flatMap(x -> x.stream()) .collect(Collectors.toList());
作为奖励,因为Stream.of()
是可变的,所以你可以连接尽可能多的列表。
List<String> newList = Stream.of(listOne, listTwo, listThree) .flatMap(x -> x.stream()) .collect(Collectors.toList());
这很简单,只是一行,但会将listTwo的内容添加到listOne。 你真的需要把内容放在第三个列表中吗?
Collections.addAll(listOne, listTwo.toArray());
稍短一些将是:
List<String> newList = new ArrayList<String>(listOne); newList.addAll(listTwo);
这是一个使用两行的java 8解决scheme:
List<Object> newList = new ArrayList<>(); Stream.of(list1, list2).forEach(newList::addAll);
请注意,如果不使用此方法
-
newList
的来源是未知的,它可能已经与其他线程共享 - 修改
newList
的stream是并行stream,对newList
访问不同步或线程安全
由于副作用的考虑。
上述两个条件都不适用于上述两个列表的情况,所以这是安全的。
基于这个答案的另一个问题。
稍微简单一点:
List<String> newList = new ArrayList<String>(listOne); newList.addAll(listTwo);
如果目标列表是预先声明的,你可以做一个链接。
(newList = new ArrayList<String>(list1)).addAll(list2);
build议的解决scheme是针对三个列表,尽pipe它也可以应用于两个列表。 在Java 8中,我们可以使用Stream.of或Stream.concat作为:
List<String> result1 = Stream.concat(Stream.concat(list1.stream(),list2.stream()),list3.stream()).collect(Collectors.toList()); List<String> result2 = Stream.of(list1,list2,list3).flatMap(Collection::stream).collect(Collectors.toList());
Stream.concat
将两个stream作为input,并创build一个延迟级联stream,其元素是第一个stream的所有元素,后面是第二个stream的所有元素。 由于我们有三个列表,我们已经使用了这个方法( Stream.concat
)两次。
我们也可以写一个实用类(比如说StringUtils),它带有一个方法,它使用任意数量的列表(使用可变参数 ),并返回一个连接列表:
public static <T> List<T> concatenatedList(List<T>... collections) { return Arrays.stream(collections).flatMap(Collection::stream).collect(Collectors.toList()); }
那么我们可以使用这个方法:
List<String> result3 = StringUtils.concatenatedList(list1,list2,list3);
在Java 8中 (另一种方式):
List<?> newList = Stream.of(list1, list2).flatMap(List::stream).collect(Collectors.toList());
另一个使用Java8
stream的Java8
解决scheme,因为flatMap
解决scheme已经发布,这里是一个没有flatMap
的解决scheme
List<E> li = lol.stream().collect(ArrayList::new, List::addAll, List::addAll);
要么
List<E> ints = Stream.of(list1, list2).collect(ArrayList::new, List::addAll, List::addAll);
码
List<List<Integer>> lol = Arrays.asList(Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6)); List<Integer> li = lol.stream().collect(ArrayList::new, List::addAll, List::addAll); System.out.println(lol); System.out.println(li);
产量
[[1, 2, 3], [4, 5, 6]] [1, 2, 3, 4, 5, 6]
我认为最聪明的是:
/** * @param smallLists * @return one big list containing all elements of the small ones, in the same order. */ public static <E> List<E> concatenate (final List<E> ... smallLists) { final ArrayList<E> bigList = new ArrayList<E>(); for (final List<E> list: smallLists) { bigList.addAll(list); } return bigList; }
支持通过对象键join的Java 8版本:
public List<SomeClass> mergeLists(final List<SomeClass> left, final List<SomeClass> right, String primaryKey) { final Map<Object, SomeClass> mergedList = new LinkedHashMap<>(); Stream.concat(left.stream(), right.stream()) .map(someObject -> new Pair<Object, SomeClass>(someObject.getSomeKey(), someObject)) .forEach(pair-> mergedList.put(pair.getKey(), pair.getValue())); return new ArrayList<>(mergedList.values()); }
你可以用静态导入和辅助类来完成
这个类的生殖能力可能可以提高
public class Lists { private Lists() { } // can't be instantiated public static List<T> join(List<T>... lists) { List<T> result = new ArrayList<T>(); for(List<T> list : lists) { result.addAll(list); } return results; } }
那么你可以做像这样的事情
import static Lists.join; List<T> result = join(list1, list2, list3, list4);
public static <T> List<T> merge(List<T>... args) { final List<T> result = new ArrayList<>(); for (List<T> list : args) { result.addAll(list); } return result; }
使用助手类。
我build议:
public static <E> Collection<E> addAll(Collection<E> dest, Collection<? extends E>... src) { for(Collection<? extends E> c : src) { dest.addAll(c); } return dest; } public static void main(String[] args) { System.out.println(addAll(new ArrayList<Object>(), Arrays.asList(1,2,3), Arrays.asList("a", "b", "c"))); // does not compile // System.out.println(addAll(new ArrayList<Integer>(), Arrays.asList(1,2,3), Arrays.asList("a", "b", "c"))); System.out.println(addAll(new ArrayList<Integer>(), Arrays.asList(1,2,3), Arrays.asList(4, 5, 6))); }
我并不是说这很简单,但是你提到了单线的奖金;-)
Collection mergedList = Collections.list(new sun.misc.CompoundEnumeration(new Enumeration[] { new Vector(list1).elements(), new Vector(list2).elements(), ... }))
没有办法靠近单线,但我认为这是最简单的:
List<String> newList = new ArrayList<String>(l1); newList.addAll(l2); for(String w:newList) System.out.printf("%s ", w);
如果您的列表具有不同的types,并且想要将它们组合到另一个types的列表中,则可以使用streams和java 8。
public static void main(String[] args) { List<String> list2 = new ArrayList<>(); List<Pair<Integer, String>> list1 = new ArrayList<>(); list2.add("asd"); list2.add("asdaf"); list1.add(new Pair<>(1, "werwe")); list1.add(new Pair<>(2, "tyutyu")); Stream stream = Stream.concat(list1.stream(), list2.stream()); List<Pair<Integer, String>> res = (List<Pair<Integer, String>>) stream .map(item -> { if (item instanceof String) { return new Pair<>(0, item); } else { return new Pair<>(((Pair<Integer, String>)item).getKey(), ((Pair<Integer, String>)item).getValue()); } }) .collect(Collectors.toList()); }
List<?> newList = ListUtils.combine(list1, list2);
哦,你必须执行combine
方法:-)
public class TestApp { /** * @param args */ public static void main(String[] args) { System.out.println("Hi"); Set<List<String>> bcOwnersList = new HashSet<List<String>>(); List<String> bclist = new ArrayList<String>(); List<String> bclist1 = new ArrayList<String>(); List<String> object = new ArrayList<String>(); object.add("BC11"); object.add("C2"); bclist.add("BC1"); bclist.add("BC2"); bclist.add("BC3"); bclist.add("BC4"); bclist.add("BC5"); bcOwnersList.add(bclist); bcOwnersList.add(object); bclist1.add("BC11"); bclist1.add("BC21"); bclist1.add("BC31"); bclist1.add("BC4"); bclist1.add("BC5"); List<String> listList= new ArrayList<String>(); for(List<String> ll : bcOwnersList){ listList = (List<String>) CollectionUtils.union(listList,CollectionUtils.intersection(ll, bclist1)); } /*for(List<String> lists : listList){ test = (List<String>) CollectionUtils.union(test, listList); }*/ for(Object l : listList){ System.out.println(l.toString()); } System.out.println(bclist.contains("BC")); } }
如果不介绍自己的实用方法,我无法在一般情况下改进双线程,但如果您确实有string列表,并且您愿意假定这些string不包含逗号,则可以拉长这个长整数-衬垫:
List<String> newList = new ArrayList<String>(Arrays.asList((listOne.toString().subString(1, listOne.length() - 1) + ", " + listTwo.toString().subString(1, listTwo.length() - 1)).split(", ")));
如果你放弃generics,这应该符合JDK 1.4(尽pipe我没有testing过)。 也不build议用于生产代码;-)