두 목록을 하나의 맵 (Java)으로 결합하는 가장 좋은 방법은 무엇입니까?
을 사용하는 것이 좋지만 for (String item: list)
하나의 목록 만 반복하고 다른 목록에있는 것들이 반복적으로 필요합니다. 또는 두 가지 모두에 대해 명시 적 반복기를 사용할 수 있습니다.
다음은 문제의 예와 for
대신 인덱싱 된 루프를 사용하는 솔루션입니다 .
import java.util.*;
public class ListsToMap {
static public void main(String[] args) {
List<String> names = Arrays.asList("apple,orange,pear".split(","));
List<String> things = Arrays.asList("123,456,789".split(","));
Map<String,String> map = new LinkedHashMap<String,String>(); // ordered
for (int i=0; i<names.size(); i++) {
map.put(names.get(i), things.get(i)); // is there a clearer way?
}
System.out.println(map);
}
}
다수 :
{apple=123, orange=456, pear=789}
더 명확한 방법이 있습니까? 어딘가에 컬렉션 API에서?
루프 솔루션은 매우 명확하고 짧다고 생각합니다.
이 질문을 한 지 오래 계속해서 나는 다음과 같은 부분적입니다.
public static <K, V> Map<K, V> zipToMap(List<K> keys, List<V> values) {
return IntStream.range(0, keys.size()).boxed()
.collect(Collectors.toMap(keys::get, values::get));
}
스트림에 익숙하지 않은 사람들을 위해 가장 많이 사용하는 일은 IntStream
0에서 길이까지의 값을 변환하는 다음 상자에 Stream<Integer>
배열 변환 할 수 있도록 다음 Collectors.toMap
두 공급 업체를 사용하여 수집하고 그중 하나는 키로 생성합니다. , 다른 값.
약간의 유효성 이것은 검사를 견딜 수 있지만 ( keys.size()
보다 작게 요구하는 것과 같은 values.size()
) 간단한 솔루션으로 훌륭하게 작동합니다.
편집 : 위의 내용은 동일한 시간 조회를 사용하는 것과 같이 작업에 적합하지만 순서로 작동하고 여전히 동일한 유형의 패턴을 사용합니다.
public static <K, V> Map<K, V> zipToMap(List<K> keys, List<V> values) {
Iterator<K> keyIter = keys.iterator();
Iterator<V> valIter = values.iterator();
return IntStream.range(0, keys.size()).boxed()
.collect(Collectors.toMap(_i -> keyIter.next(), _i -> valIter.next()));
}
출력은 동일하지만 (다시 말하지만, 길이 검사 누락 등) 복잡성 time- get
은 사용되는 목록 에, 대한 메서드 구현에 의존하지 않습니다 .
나는 종종 다음 관용구를 사용합니다. 나는 그것이 더 확실한 지 주장의 여지가 있음을 인정합니다.
Iterator<String> i1 = names.iterator();
Iterator<String> i2 = things.iterator();
while (i1.hasNext() && i2.hasNext()) {
map.put(i1.next(), i2.next());
}
if (i1.hasNext() || i2.hasNext()) complainAboutSizes();
LinkedList, TreeSets 또는 SQL ResultSets와 같이 임의의 액세스없이 또는 쉬운 액세스없이 Collections 및 이와 일치하는 것들에 작동한다는 점이 있습니다. 예를 들어 LinkedLists에서 원래 알고리즘을 사용하는 경우 길이 n의 목록에 실제로 n * n 작업이 필요한 느린 Shlemiel the painter 알고리즘 이 있습니다.
으로 13ren 은 지적, 당신은 또한 당신이 길이가 일치하지 않는 경우 하나의 목록이 끝난 후 계속하려고하면 Iterator.next이 예외 : NoSuchElementException을 던진다는 사실을 사용할 수 있습니다. 따라서 간결하지만 약간의 혼란스러운 변형을 얻을 수 있습니다.
Iterator<String> i1 = names.iterator();
Iterator<String> i2 = things.iterator();
while (i1.hasNext() || i2.hasNext()) map.put(i1.next(), i2.next());
귀하의 질문은 명확성에 관한 것이 었습니다.
두 목록을 결합 하는 가장 명확한 방법은 그 조합을 깔끔한 이름의 방법에 넣는 것입니다. 방금 귀하의 솔루션을 가져 오기 방법으로 추출했습니다.
Map <String, String> combineListsIntoOrderedMap (List <String> 키, List <String> 값) { if (keys.size ()! = values.size ()) throw new IllegalArgumentException ( "다른 크기의 목록을 결합 할 수 없음"); 지도 <문자열, 문자열> 맵 = 새 LinkedHashMap <문자열, 문자열> (); for (int i = 0; i <keys.size (); i ++) { map.put (keys.get (i), values.get (i)); } 반환지도; }
물론 리팩토링 된 메인은 다음과 다양합니다.
static public void main (String [] args) { List <String> 이름 = Arrays.asList ( "apple, orange, pear".split ( ",")); List <String> things = Arrays.asList ( "123,456,789".split ( ",")); Map <String, String> map = combineListsIntoOrderedMap (이름, 사물); System.out.println (지도); }
나는 길이 체크에 저항 할 수 없었다.
개인적으로 수준을 반복하는 간단한 루프가 가장 명확한 솔루션이라고 생각하지만 여기에 할 다른 두 가지 가능성이 있습니다.
전화 피한다 대안 자바 (8) 솔루션을 boxed()
온은 IntStream
이다
List<String> keys = Arrays.asList("A", "B", "C");
List<String> values = Arrays.asList("1", "2", "3");
Map<String, String> map = IntStream.range(0, keys.size())
.collect(
HashMap::new,
(m, i) -> m.put(keys.get(i), values.get(i)),
Map::putAll
);
);
명확성 포함 할 다른 사항이 생각합니다.
- 서로 다른 크기 목록 및 불법 인수의 올바른 거부
null
의 (경우 발생하는 모습things
이다null
질문 코드). - 빠른 임의 액세스가없는 목록을 처리하는 기능.
- 동시 및 동기화 된 수집을 처리하는 기능.
따라서 라이브러리 코드의 경우 아마도 다음과 가변적입니다.
@SuppressWarnings("unchecked")
public static <K,V> Map<K,V> linkedZip(List<? extends K> keys, List<? extends V> values) {
Object[] keyArray = keys.toArray();
Object[] valueArray = values.toArray();
int len = keyArray.length;
if (len != valueArray.length) {
throwLengthMismatch(keyArray, valueArray);
}
Map<K,V> map = new java.util.LinkedHashMap<K,V>((int)(len/0.75f)+1);
for (int i=0; i<len; ++i) {
map.put((K)keyArray[i], (V)valueArray[i]);
}
return map;
}
(더 많은 등호 키를 넣지 않는지 확인하고 싶을 수 있습니다.)
ArrayUtils # toMap () 은 두 개의 목록을지도로 결합하지 않지만 2 개의 배열을 그렇게합니다 (모든 차원에서 어떤 내용은 나중에 참조 할 수 있습니다 ...)
명확한 방법이 없습니다. Apache Commons 또는 Guava에있는 것이 여전히 궁금합니다. 어쨌든 나만의 정적 유틸리티가 있었다. 그러나 이것은 키 충돌을 알고 있습니다!
public static <K, V> Map<K, V> map(Collection<K> keys, Collection<V> values) {
Map<K, V> map = new HashMap<K, V>();
Iterator<K> keyIt = keys.iterator();
Iterator<V> valueIt = values.iterator();
while (keyIt.hasNext() && valueIt.hasNext()) {
K k = keyIt.next();
if (null != map.put(k, valueIt.next())){
throw new IllegalArgumentException("Keys are not unique! Key " + k + " found more then once.");
}
}
if (keyIt.hasNext() || valueIt.hasNext()) {
throw new IllegalArgumentException("Keys and values collections have not the same size");
};
return map;
}
Strings로 제한 할 필요도 없습니다. CPerkins에서 약간의 코드 수정 :
Map<K, V> <K, V> combineListsIntoOrderedMap (List<K> keys, List<V> values) {
if (keys.size() != values.size())
throw new IllegalArgumentException ("Cannot combine lists with dissimilar sizes");
Map<K, V> map = new LinkedHashMap<K, V>();
for (int i=0; i<keys.size(); i++) {
map.put(keys.get(i), values.get(i));
}
return map;
}
와 vavr
라이브러리 :
List.ofAll(names).zip(things).toJavaMap(Function.identity());
Clojure를 사용하십시오. 한 줄이면 충분합니다.)
(zipmap list1 list2)
이 Eclipse 컬렉션을 사용하여 작동합니다 .
Map<String, String> map =
Maps.adapt(new LinkedHashMap<String, String>())
.withAllKeyValues(
Lists.mutable.of("apple,orange,pear".split(","))
.zip(Lists.mutable.of("123,456,789".split(","))));
System.out.println(map);
참고 : 저는 Eclipse Collections의 커미터입니다.
또 다른 Java 8 솔루션 :
Guava 라이브러리에 액세스 할 수있는 경우 (버전 21 [1]의 스트림에 대한 초기 지원 ) 다음을 수행 할 수 있습니다.
Streams.zip(keyList.stream(), valueList.stream(), Maps::immutableEntry)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
저 에게이 방법의 장점은 하나의 라이너라는 사실에 사용 사례에 도움이되는 것을 알았습니다.
이에 대한 또 다른 관점은 구현을 숨기는 것입니다. 이 기능의 호출자가 자바의 사용 for-loop 의 모양과 느낌을 즐기기 를 원 하십니까?
public static void main(String[] args) {
List<String> names = Arrays.asList("apple,orange,pear".split(","));
List<String> things = Arrays.asList("123,456,789".split(","));
Map<String, String> map = new HashMap<>(4);
for (Map.Entry<String, String> e : new DualIterator<>(names, things)) {
map.put(e.getKey(), e.getValue());
}
System.out.println(map);
}
예 ( Map.Entry
편의상 선택됨)이면 완전한 예가 다음과 같습니다 (참고 : 스레드가 안전하지 않음).
import java.util.*;
/** <p>
A thread unsafe iterator over two lists to convert them
into a map such that keys in first list at a certain
index map onto values in the second list <b> at the same index</b>.
</p>
Created by kmhaswade on 5/10/16.
*/
public class DualIterator<K, V> implements Iterable<Map.Entry<K, V>> {
private final List<K> keys;
private final List<V> values;
private int anchor = 0;
public DualIterator(List<K> keys, List<V> values) {
// do all the validations here
this.keys = keys;
this.values = values;
}
@Override
public Iterator<Map.Entry<K, V>> iterator() {
return new Iterator<Map.Entry<K, V>>() {
@Override
public boolean hasNext() {
return keys.size() > anchor;
}
@Override
public Map.Entry<K, V> next() {
Map.Entry<K, V> e = new AbstractMap.SimpleEntry<>(keys.get(anchor), values.get(anchor));
anchor += 1;
return e;
}
};
}
public static void main(String[] args) {
List<String> names = Arrays.asList("apple,orange,pear".split(","));
List<String> things = Arrays.asList("123,456,789".split(","));
Map<String, String> map = new LinkedHashMap<>(4);
for (Map.Entry<String, String> e : new DualIterator<>(names, things)) {
map.put(e.getKey(), e.getValue());
}
System.out.println(map);
}
}
다음과 같이 인쇄됩니다 (요구 사항 당).
{apple=123, orange=456, pear=789}
Java 8에서는 두 가지를 하나씩 반복하고 맵을 채 있습니다.
public <K, V> Map<K, V> combineListsIntoOrderedMap (Iterable<K> keys, Iterable<V> values) {
Map<K, V> map = new LinkedHashMap<>();
Iterator<V> vit = values.iterator();
for (K k: keys) {
if (!vit.hasNext())
throw new IllegalArgumentException ("Less values than keys.");
map.put(k, vit.next());
}
return map;
}
또는 기능적 스타일로 한 단계 더 나아가 다음을 수행 할 수 있습니다.
/**
* Usage:
*
* Map<K, V> map2 = new LinkedHashMap<>();
* combineListsIntoOrderedMap(keys, values, map2::put);
*/
public <K, V> void combineListsIntoOrderedMap (Iterable<K> keys, Iterable<V> values, BiConsumer<K, V> onItem) {
Iterator<V> vit = values.iterator();
for (K k: keys) {
if (!vit.hasNext())
throw new IllegalArgumentException ("Less values than keys.");
onItem.accept(k, vit.next());
}
}
활용 AbstractMap
및 AbstractSet
:
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
public class ZippedMap<A, B> extends AbstractMap<A, B> {
private final List<A> first;
private final List<B> second;
public ZippedMap(List<A> first, List<B> second) {
if (first.size() != second.size()) {
throw new IllegalArgumentException("Expected lists of equal size");
}
this.first = first;
this.second = second;
}
@Override
public Set<Entry<A, B>> entrySet() {
return new AbstractSet<>() {
@Override
public Iterator<Entry<A, B>> iterator() {
Iterator<A> i = first.iterator();
Iterator<B> i2 = second.iterator();
return new Iterator<>() {
@Override
public boolean hasNext() {
return i.hasNext();
}
@Override
public Entry<A, B> next() {
return new SimpleImmutableEntry<>(i.next(), i2.next());
}
};
}
@Override
public int size() {
return first.size();
}
};
}
}
용법 :
public static void main(String... args) {
Map<Integer, Integer> zippedMap = new ZippedMap<>(List.of(1, 2, 3), List.of(1, 2, 3));
zippedMap.forEach((k, v) -> System.out.println("key = " + k + " value = " + v));
}
다수 :
key = 1 value = 1
key = 2 value = 2
key = 3 value = 3
나는 java.util.stream.Collectors.Partition
기본적으로 같은 일 을하는 수업 에서이 아이디어를 얻었다 .
캡슐화 및 명확한 의도 (두 목록 압축)는 물론 재사용 가능성과 성능을 제공합니다.
이것은 다른 답변 보다 낫습니다. 항목을 만든 다음 즉시 맵에 배치하기 위해 래핑을 해제 .
'ProgramingTip' 카테고리의 다른 글
Objective-C의 NSString indexOf (0) | 2021.01.05 |
---|---|
부모 양식의 중앙 MessageBox (0) | 2021.01.05 |
키로 배열 값 가져 오기 (0) | 2021.01.05 |
gvim : 키보드로 탭 전환 (0) | 2021.01.05 |
Jquery $ ( '# div'). 보여 주다 (). 지연 (5000) .hide (); (0) | 2021.01.05 |